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