001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.system.configuration;
018
019 import java.beans.PropertyEditor;
020 import java.io.IOException;
021 import java.io.PrintWriter;
022 import java.io.StringWriter;
023 import java.io.Serializable;
024 import java.io.StringReader;
025 import java.net.URI;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.HashMap;
029 import java.util.Iterator;
030 import java.util.LinkedHashMap;
031 import java.util.LinkedHashSet;
032 import java.util.Map;
033 import java.util.Set;
034
035 import javax.xml.parsers.DocumentBuilderFactory;
036 import javax.xml.parsers.DocumentBuilder;
037
038 import org.apache.geronimo.common.propertyeditor.PropertyEditors;
039 import org.apache.geronimo.gbean.AbstractName;
040 import org.apache.geronimo.gbean.AbstractNameQuery;
041 import org.apache.geronimo.gbean.GAttributeInfo;
042 import org.apache.geronimo.gbean.GBeanData;
043 import org.apache.geronimo.gbean.GBeanInfo;
044 import org.apache.geronimo.gbean.ReferencePatterns;
045 import org.apache.geronimo.kernel.InvalidGBeanException;
046 import org.apache.geronimo.kernel.util.XmlUtil;
047 import org.apache.geronimo.kernel.repository.Artifact;
048 import org.apache.geronimo.util.EncryptionManager;
049 import org.w3c.dom.Element;
050 import org.w3c.dom.Node;
051 import org.w3c.dom.NodeList;
052 import org.w3c.dom.Document;
053 import org.xml.sax.InputSource;
054
055 /**
056 * @version $Rev: 487175 $ $Date: 2006-12-14 03:10:31 -0800 (Thu, 14 Dec 2006) $
057 */
058 public class GBeanOverride implements Serializable {
059 private final Object name;
060 private boolean load;
061 private final Map attributes = new LinkedHashMap();
062 private final Map references = new LinkedHashMap();
063 private final ArrayList clearAttributes = new ArrayList();
064 private final ArrayList nullAttributes = new ArrayList();
065 private final ArrayList clearReferences = new ArrayList();
066 private final String gbeanInfo;
067
068 public GBeanOverride(String name, boolean load) {
069 this.name = name;
070 this.load = load;
071 gbeanInfo = null;
072 }
073
074 public GBeanOverride(AbstractName name, boolean load) {
075 this.name = name;
076 this.load = load;
077 gbeanInfo = null;
078 }
079
080 public GBeanOverride(GBeanData gbeanData) throws InvalidAttributeException {
081 GBeanInfo gbeanInfo = gbeanData.getGBeanInfo();
082 this.gbeanInfo = gbeanInfo.getSourceClass();
083 if (this.gbeanInfo == null) {
084 throw new IllegalArgumentException("GBeanInfo must have a source class set");
085 }
086 name = gbeanData.getAbstractName();
087 load = true;
088
089 // set attributes
090 for (Iterator iterator = gbeanData.getAttributes().entrySet().iterator(); iterator.hasNext();) {
091 Map.Entry entry = (Map.Entry) iterator.next();
092 String attributeName = (String) entry.getKey();
093 GAttributeInfo attributeInfo = gbeanInfo.getAttribute(attributeName);
094 if (attributeInfo == null) {
095 throw new InvalidAttributeException("No attribute: " + attributeName + " for gbean: " + gbeanData.getAbstractName());
096 }
097 Object attributeValue = entry.getValue();
098 setAttribute(attributeName, attributeValue, attributeInfo.getType());
099 }
100
101 // references can be coppied in blind
102 references.putAll(gbeanData.getReferences());
103 }
104
105 public GBeanOverride(Element gbean) throws InvalidGBeanException {
106 String nameString = gbean.getAttribute("name");
107 if (nameString.indexOf('?') > -1) {
108 name = new AbstractName(URI.create(nameString));
109 } else {
110 name = nameString;
111 }
112
113 String gbeanInfoString = gbean.getAttribute("gbeanInfo");
114 if (gbeanInfoString.length() > 0) {
115 gbeanInfo = gbeanInfoString;
116 } else {
117 gbeanInfo = null;
118 }
119 if (gbeanInfo != null && !(name instanceof AbstractName)) {
120 throw new InvalidGBeanException("A gbean element using the gbeanInfo attribute must be specified using a full AbstractName: name=" + nameString);
121 }
122
123 String loadString = gbean.getAttribute("load");
124 load = !"false".equals(loadString);
125
126 // attributes
127 NodeList attributes = gbean.getElementsByTagName("attribute");
128 for (int a = 0; a < attributes.getLength(); a++) {
129 Element attribute = (Element) attributes.item(a);
130
131 String attributeName = attribute.getAttribute("name");
132
133 // Check to see if there is a value attribute
134 if (attribute.hasAttribute("value")) {
135 setAttribute(attributeName, (String) EncryptionManager.decrypt(attribute.getAttribute("value")));
136 continue;
137 }
138
139 // Check to see if there is a null attribute
140 if (attribute.hasAttribute("null")) {
141 String nullString = attribute.getAttribute("null");
142 if (nullString.equals("true")) {
143 setNullAttribute(attributeName);
144 continue;
145 }
146 }
147
148 String rawAttribute = getContentsAsText(attribute);
149 // If there are no contents, then it's to be cleared
150 if (rawAttribute.length() == 0) {
151 setClearAttribute(attributeName);
152 continue;
153 }
154 String attributeValue = (String) EncryptionManager.decrypt(rawAttribute);
155
156 setAttribute(attributeName, attributeValue);
157 }
158
159 // references
160 NodeList references = gbean.getElementsByTagName("reference");
161 for (int r = 0; r < references.getLength(); r++) {
162 Element reference = (Element) references.item(r);
163
164 String referenceName = reference.getAttribute("name");
165
166 Set objectNamePatterns = new LinkedHashSet();
167 NodeList patterns = reference.getElementsByTagName("pattern");
168
169 // If there is no pattern, then its an empty set, so its a
170 // cleared value
171 if (patterns.getLength() == 0) {
172 setClearReference(referenceName);
173 continue;
174 }
175
176 for (int p = 0; p < patterns.getLength(); p++) {
177 Element pattern = (Element) patterns.item(p);
178 if (pattern == null)
179 continue;
180
181 String groupId = getChildAsText(pattern, "groupId");
182 String artifactId = getChildAsText(pattern, "artifactId");
183 String version = getChildAsText(pattern, "version");
184 String type = getChildAsText(pattern, "type");
185 String module = getChildAsText(pattern, "module");
186 String name = getChildAsText(pattern, "name");
187
188 Artifact referenceArtifact = null;
189 if (artifactId != null) {
190 referenceArtifact = new Artifact(groupId, artifactId, version, type);
191 }
192 Map nameMap = new HashMap();
193 if (module != null) {
194 nameMap.put("module", module);
195 }
196 if (name != null) {
197 nameMap.put("name", name);
198 }
199 AbstractNameQuery abstractNameQuery = new AbstractNameQuery(referenceArtifact, nameMap, Collections.EMPTY_SET);
200 objectNamePatterns.add(abstractNameQuery);
201 }
202
203 setReferencePatterns(referenceName, new ReferencePatterns(objectNamePatterns));
204 }
205 }
206
207 private static String getChildAsText(Element element, String name) throws InvalidGBeanException {
208 NodeList children = element.getElementsByTagName(name);
209 if (children == null || children.getLength() == 0) {
210 return null;
211 }
212 if (children.getLength() > 1) {
213 throw new InvalidGBeanException("invalid name, too many parts named: " + name);
214 }
215 return getContentsAsText((Element) children.item(0));
216 }
217
218 private static String getContentsAsText(Element element) throws InvalidGBeanException {
219 String value = "";
220 NodeList text = element.getChildNodes();
221 for (int t = 0; t < text.getLength(); t++) {
222 Node n = text.item(t);
223 if (n.getNodeType() == Node.TEXT_NODE) {
224 value += n.getNodeValue();
225 } else {
226 StringWriter sw = new StringWriter();
227 PrintWriter pw = new PrintWriter(sw);
228 OutputFormat of = new OutputFormat(Method.XML, null, false);
229 of.setOmitXMLDeclaration(true);
230 XMLSerializer serializer = new XMLSerializer(pw, of);
231 try {
232 serializer.prepare();
233 serializer.serializeNode(n);
234 value += sw.toString();
235 } catch (IOException ioe) {
236 throw new InvalidGBeanException("Error serializing GBean element", ioe);
237 }
238 }
239 }
240 return value.trim();
241 }
242
243 public Object getName() {
244 return name;
245 }
246
247 public String getGBeanInfo() {
248 return gbeanInfo;
249 }
250
251 public boolean isLoad() {
252 return load;
253 }
254
255 public void setLoad(boolean load) {
256 this.load = load;
257 }
258
259 public Map getAttributes() {
260 return attributes;
261 }
262
263 public String getAttribute(String attributeName) {
264 return (String) attributes.get(attributeName);
265 }
266
267 public ArrayList getClearAttributes() {
268 return clearAttributes;
269 }
270
271 public ArrayList getNullAttributes() {
272 return nullAttributes;
273 }
274
275 public boolean getNullAttribute(String attributeName) {
276 return nullAttributes.contains(attributeName);
277 }
278
279 public boolean getClearAttribute(String attributeName) {
280 return clearAttributes.contains(attributeName);
281 }
282
283 public ArrayList getClearReferences() {
284 return clearReferences;
285 }
286
287 public boolean getClearReference(String referenceName) {
288 return clearReferences.contains(referenceName);
289 }
290
291 public void setClearAttribute(String attributeName) {
292 if (!clearAttributes.contains(attributeName))
293 clearAttributes.add(attributeName);
294 }
295
296 public void setNullAttribute(String attributeName) {
297 if (!nullAttributes.contains(attributeName))
298 nullAttributes.add(attributeName);
299 }
300
301 public void setClearReference(String referenceName) {
302 if (!clearReferences.contains(referenceName))
303 clearReferences.add(referenceName);
304 }
305
306 public void setAttribute(String attributeName, Object attributeValue, String attributeType) throws InvalidAttributeException {
307 String stringValue = getAsText(attributeValue, attributeType);
308 attributes.put(attributeName, stringValue);
309 }
310
311 public void setAttribute(String attributeName, String attributeValue) {
312 attributes.put(attributeName, attributeValue);
313 }
314
315 public Map getReferences() {
316 return references;
317 }
318
319 public ReferencePatterns getReferencePatterns(String name) {
320 return (ReferencePatterns) references.get(name);
321 }
322
323 public void setReferencePatterns(String name, ReferencePatterns patterns) {
324 references.put(name, patterns);
325 }
326
327 /**
328 * Creates a new child of the supplied parent with the data for this
329 * GBeanOverride, adds it to the parent, and then returns the new
330 * child element.
331 */
332 public Element writeXml(Document doc, Element parent) {
333 String gbeanName;
334 if (name instanceof String) {
335 gbeanName = (String) name;
336 } else {
337 gbeanName = name.toString();
338 }
339
340 Element gbean = doc.createElement("gbean");
341 parent.appendChild(gbean);
342 gbean.setAttribute("name", gbeanName);
343 if (gbeanInfo != null) {
344 gbean.setAttribute("gbeanInfo", gbeanInfo);
345 }
346 if (!load) {
347 gbean.setAttribute("load", "false");
348 }
349
350 // attributes
351 for (Iterator iterator = attributes.entrySet().iterator(); iterator.hasNext();) {
352 Map.Entry entry = (Map.Entry) iterator.next();
353 String name = (String) entry.getKey();
354 String value = (String) entry.getValue();
355 if (value == null) {
356 setNullAttribute(name);
357 }
358 else {
359 if (getNullAttribute(name)) {
360 nullAttributes.remove(name);
361 }
362 if (name.toLowerCase().indexOf("password") > -1) {
363 value = EncryptionManager.encrypt(value);
364 }
365 Element attribute = doc.createElement("attribute");
366 attribute.setAttribute("name", name);
367 gbean.appendChild(attribute);
368 if (value.length() == 0) {
369 attribute.setAttribute("value", "");
370 }
371 else {
372 try {
373 //
374 // NOTE: Construct a new document to handle mixed content attribute values
375 // then add nodes which are children of the first node. This allows
376 // value to be XML or text.
377 //
378
379 DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
380 DocumentBuilder builder = factory.newDocumentBuilder();
381
382 // Wrap value in an element to be sure we can handle xml or text values
383 String xml = "<fragment>" + value + "</fragment>";
384 InputSource input = new InputSource(new StringReader(xml));
385 Document fragment = builder.parse(input);
386
387 Node root = fragment.getFirstChild();
388 NodeList children = root.getChildNodes();
389 for (int i=0; i<children.getLength(); i++) {
390 Node child = children.item(i);
391
392 // Import the child (and its children) into the new document
393 child = doc.importNode(child, true);
394 attribute.appendChild(child);
395 }
396 }
397 catch (Exception e) {
398 throw new RuntimeException("Failed to write attribute value fragment: " + e.getMessage(), e);
399 }
400 }
401 }
402 }
403
404 // cleared attributes
405 for (Iterator iterator = clearAttributes.iterator(); iterator.hasNext();) {
406 String name = (String) iterator.next();
407 Element attribute = doc.createElement("attribute");
408 gbean.appendChild(attribute);
409 attribute.setAttribute("name", name);
410 }
411
412 // Null attributes
413 for (Iterator iterator = nullAttributes.iterator(); iterator.hasNext();) {
414 String name = (String) iterator.next();
415 Element attribute = doc.createElement("attribute");
416 gbean.appendChild(attribute);
417 attribute.setAttribute("name", name);
418 attribute.setAttribute("null", "true");
419 }
420
421 // references
422 for (Iterator iterator = references.entrySet().iterator(); iterator.hasNext();) {
423 Map.Entry entry = (Map.Entry) iterator.next();
424 String name = (String) entry.getKey();
425 ReferencePatterns patterns = (ReferencePatterns) entry.getValue();
426
427 Element reference = doc.createElement("reference");
428 reference.setAttribute("name", name);
429 gbean.appendChild(reference);
430
431 Set patternSet;
432 if (patterns.isResolved()) {
433 patternSet = Collections.singleton(new AbstractNameQuery(patterns.getAbstractName()));
434 } else {
435 patternSet = patterns.getPatterns();
436 }
437
438 for (Iterator patternIterator = patternSet.iterator(); patternIterator.hasNext();) {
439 AbstractNameQuery pattern = (AbstractNameQuery) patternIterator.next();
440 Element pat = doc.createElement("pattern");
441 reference.appendChild(pat);
442 Artifact artifact = pattern.getArtifact();
443
444 if (artifact != null) {
445 if (artifact.getGroupId() != null) {
446 Element group = doc.createElement("groupId");
447 group.appendChild(doc.createTextNode(artifact.getGroupId()));
448 pat.appendChild(group);
449 }
450 if (artifact.getArtifactId() != null) {
451 Element art = doc.createElement("artifactId");
452 art.appendChild(doc.createTextNode(artifact.getArtifactId()));
453 pat.appendChild(art);
454 }
455 if (artifact.getVersion() != null) {
456 Element version = doc.createElement("version");
457 version.appendChild(doc.createTextNode(artifact.getVersion().toString()));
458 pat.appendChild(version);
459 }
460 if (artifact.getType() != null) {
461 Element type = doc.createElement("type");
462 type.appendChild(doc.createTextNode(artifact.getType()));
463 pat.appendChild(type);
464 }
465 }
466
467 Map nameMap = pattern.getName();
468 if (nameMap.get("module") != null) {
469 Element module = doc.createElement("module");
470 module.appendChild(doc.createTextNode(nameMap.get("module").toString()));
471 pat.appendChild(module);
472 }
473
474 if (nameMap.get("name") != null) {
475 Element patName = doc.createElement("name");
476 patName.appendChild(doc.createTextNode(nameMap.get("name").toString()));
477 pat.appendChild(patName);
478 }
479 }
480 }
481
482 // cleared references
483 for (Iterator iterator = clearReferences.iterator(); iterator.hasNext();) {
484 String name = (String) iterator.next();
485 Element reference = doc.createElement("reference");
486 reference.setAttribute("name", name);
487 gbean.appendChild(reference);
488 }
489
490 return gbean;
491 }
492
493 public static String getAsText(Object value, String type) throws InvalidAttributeException {
494 try {
495 String attributeStringValue = null;
496 if (value != null) {
497 PropertyEditor editor = PropertyEditors.findEditor(type, GBeanOverride.class.getClassLoader());
498 if (editor == null) {
499 throw new InvalidAttributeException("Unable to format attribute of type " + type + "; no editor found");
500 }
501 editor.setValue(value);
502 attributeStringValue = editor.getAsText();
503 }
504 return attributeStringValue;
505 } catch (ClassNotFoundException e) {
506 //todo: use the Configuration's ClassLoader to load the attribute, if this ever becomes an issue
507 throw new InvalidAttributeException("Unable to store attribute type " + type);
508 }
509 }
510 }