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: */package org.apache.geronimo.system.configuration;
017:
018: import java.beans.PropertyEditor;
019: import java.io.Reader;
020: import java.io.Serializable;
021: import java.io.StringReader;
022: import java.net.URI;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.LinkedHashMap;
028: import java.util.LinkedHashSet;
029: import java.util.Map;
030: import java.util.Set;
031:
032: import javax.xml.bind.JAXBException;
033: import javax.xml.stream.XMLStreamException;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037: import org.apache.geronimo.common.propertyeditor.PropertyEditors;
038: import org.apache.geronimo.gbean.AbstractName;
039: import org.apache.geronimo.gbean.AbstractNameQuery;
040: import org.apache.geronimo.gbean.GAttributeInfo;
041: import org.apache.geronimo.gbean.GBeanData;
042: import org.apache.geronimo.gbean.GBeanInfo;
043: import org.apache.geronimo.gbean.GReferenceInfo;
044: import org.apache.geronimo.gbean.ReferencePatterns;
045: import org.apache.geronimo.kernel.ClassLoading;
046: import org.apache.geronimo.kernel.InvalidGBeanException;
047: import org.apache.geronimo.kernel.config.InvalidConfigException;
048: import org.apache.geronimo.kernel.repository.Artifact;
049: import org.apache.geronimo.system.configuration.condition.JexlExpressionParser;
050: import org.apache.geronimo.system.plugin.model.AttributeType;
051: import org.apache.geronimo.system.plugin.model.GbeanType;
052: import org.apache.geronimo.system.plugin.model.ReferenceType;
053: import org.apache.geronimo.crypto.EncryptionManager;
054:
055: /**
056: * @version $Rev: 617588 $ $Date: 2008-02-01 10:20:07 -0800 (Fri, 01 Feb 2008) $
057: */
058: public class GBeanOverride implements Serializable {
059:
060: private static final Log log = LogFactory
061: .getLog(GBeanOverride.class);
062:
063: public static final String ATTRIBUTE_NAMESPACE = "http://geronimo.apache.org/xml/ns/attributes-1.2";
064: private final Object name;
065: private String comment;
066: private boolean load;
067: private final Map<String, String> attributes = new LinkedHashMap<String, String>();
068: private final Map<String, String> propertyEditors = new HashMap<String, String>();
069: private final Map<String, ReferencePatterns> references = new LinkedHashMap<String, ReferencePatterns>();
070: private final Set<String> clearAttributes = new LinkedHashSet<String>();
071: private final Set<String> nullAttributes = new LinkedHashSet<String>();
072: private final Set<String> clearReferences = new LinkedHashSet<String>();
073: private final String gbeanInfo;
074: private final JexlExpressionParser expressionParser;
075:
076: public GBeanOverride(String name, boolean load,
077: JexlExpressionParser expressionParser) {
078: this .name = name;
079: this .load = load;
080: gbeanInfo = null;
081: this .expressionParser = expressionParser;
082: }
083:
084: public GBeanOverride(AbstractName name, boolean load,
085: JexlExpressionParser expressionParser) {
086: this .name = name;
087: this .load = load;
088: gbeanInfo = null;
089: this .expressionParser = expressionParser;
090: }
091:
092: public GBeanOverride(GBeanOverride original, String oldArtifact,
093: String newArtifact) {
094: Object name = original.name;
095: if (name instanceof String) {
096: name = replace((String) name, oldArtifact, newArtifact);
097: } else if (name instanceof AbstractName) {
098: String value = name.toString();
099: value = replace(value, oldArtifact, newArtifact);
100: name = new AbstractName(URI.create(value));
101: }
102: this .name = name;
103: this .load = original.load;
104: this .comment = original.getComment();
105: this .attributes.putAll(original.attributes);
106: this .propertyEditors.putAll(original.propertyEditors);
107: this .references.putAll(original.references);
108: this .clearAttributes.addAll(original.clearAttributes);
109: this .nullAttributes.addAll(original.nullAttributes);
110: this .clearReferences.addAll(original.clearReferences);
111: this .gbeanInfo = original.gbeanInfo;
112: this .expressionParser = original.expressionParser;
113: }
114:
115: private static String replace(String original, String oldArtifact,
116: String newArtifact) {
117: int pos = original.indexOf(oldArtifact);
118: if (pos == -1) {
119: return original;
120: }
121: int last = -1;
122: StringBuffer buf = new StringBuffer();
123: while (pos > -1) {
124: buf.append(original.substring(last + 1, pos));
125: buf.append(newArtifact);
126: last = pos + oldArtifact.length() - 1;
127: pos = original.indexOf(oldArtifact, last);
128: }
129: buf.append(original.substring(last + 1));
130: return buf.toString();
131: }
132:
133: public GBeanOverride(GBeanData gbeanData,
134: JexlExpressionParser expressionParser,
135: ClassLoader classLoader) throws InvalidAttributeException {
136: GBeanInfo gbeanInfo = gbeanData.getGBeanInfo();
137: this .gbeanInfo = gbeanInfo.getSourceClass();
138: if (this .gbeanInfo == null) {
139: throw new IllegalArgumentException(
140: "GBeanInfo must have a source class set");
141: }
142: name = gbeanData.getAbstractName();
143: load = true;
144:
145: // set attributes
146: for (Object o : gbeanData.getAttributes().entrySet()) {
147: Map.Entry entry = (Map.Entry) o;
148: String attributeName = (String) entry.getKey();
149: GAttributeInfo attributeInfo = gbeanInfo
150: .getAttribute(attributeName);
151: if (attributeInfo == null) {
152: throw new InvalidAttributeException("No attribute: "
153: + attributeName + " for gbean: "
154: + gbeanData.getAbstractName());
155: }
156: Object attributeValue = entry.getValue();
157: setAttribute(attributeName, attributeValue, attributeInfo
158: .getType(), classLoader);
159: }
160:
161: // references can be coppied in blind
162: references.putAll(gbeanData.getReferences());
163: this .expressionParser = expressionParser;
164: }
165:
166: public GBeanOverride(GbeanType gbean,
167: JexlExpressionParser expressionParser)
168: throws InvalidGBeanException {
169: String nameString = gbean.getName();
170: if (nameString.indexOf('?') > -1) {
171: name = new AbstractName(URI.create(nameString));
172: } else {
173: name = nameString;
174: }
175:
176: String gbeanInfoString = gbean.getGbeanInfo();
177: if (gbeanInfoString != null && gbeanInfoString.length() > 0) {
178: gbeanInfo = gbeanInfoString;
179: } else {
180: gbeanInfo = null;
181: }
182: if (gbeanInfo != null && !(name instanceof AbstractName)) {
183: throw new InvalidGBeanException(
184: "A gbean element using the gbeanInfo attribute must be specified using a full AbstractName: name="
185: + nameString);
186: }
187:
188: load = gbean.isLoad();
189: comment = gbean.getComment();
190:
191: // attributes
192: for (Object o : gbean.getAttributeOrReference()) {
193: if (o instanceof AttributeType) {
194: AttributeType attr = (AttributeType) o;
195:
196: String propertyEditor = attr.getPropertyEditor();
197: if (null != propertyEditor) {
198: propertyEditors.put(attr.getName(), propertyEditor);
199: }
200:
201: if (attr.isNull()) {
202: getNullAttributes().add(attr.getName());
203: } else {
204: String value;
205: try {
206: value = AttributesXmlUtil
207: .extractAttributeValue(attr);
208: } catch (JAXBException e) {
209: throw new InvalidGBeanException(
210: "Could not extract attribute value from gbean override",
211: e);
212: } catch (XMLStreamException e) {
213: throw new InvalidGBeanException(
214: "Could not extract attribute value from gbean override",
215: e);
216: }
217: if (value == null || value.length() == 0) {
218: setClearAttribute(attr.getName());
219: } else {
220: String truevalue = (String) EncryptionManager
221: .decrypt(value);
222: getAttributes().put(attr.getName(), truevalue);
223: }
224: }
225: } else if (o instanceof ReferenceType) {
226: ReferenceType ref = (ReferenceType) o;
227: if (ref.getPattern().isEmpty()) {
228: setClearReference(ref.getName());
229: } else {
230: Set<AbstractNameQuery> patternSet = new HashSet<AbstractNameQuery>();
231: for (ReferenceType.Pattern pattern : ref
232: .getPattern()) {
233: String groupId = pattern.getGroupId();
234: String artifactId = pattern.getArtifactId();
235: String version = pattern.getVersion();
236: String type = pattern.getType();
237: String module = pattern.getModule();
238: String name = pattern.getName();
239:
240: Artifact referenceArtifact = null;
241: if (artifactId != null) {
242: referenceArtifact = new Artifact(groupId,
243: artifactId, version, type);
244: }
245: Map<String, String> nameMap = new HashMap<String, String>();
246: if (module != null) {
247: nameMap.put("module", module);
248: }
249: if (name != null) {
250: nameMap.put("name", name);
251: }
252: AbstractNameQuery abstractNameQuery = new AbstractNameQuery(
253: referenceArtifact, nameMap,
254: Collections.EMPTY_SET);
255: patternSet.add(abstractNameQuery);
256: }
257: ReferencePatterns patterns = new ReferencePatterns(
258: patternSet);
259: setReferencePatterns(ref.getName(), patterns);
260: }
261: }
262: }
263: this .expressionParser = expressionParser;
264: }
265:
266: public Object getName() {
267: return name;
268: }
269:
270: public String getGBeanInfo() {
271: return gbeanInfo;
272: }
273:
274: public String getComment() {
275: return comment;
276: }
277:
278: public void setComment(String comment) {
279: this .comment = comment;
280: }
281:
282: public boolean isLoad() {
283: return load;
284: }
285:
286: public void setLoad(boolean load) {
287: this .load = load;
288: }
289:
290: public Map<String, String> getAttributes() {
291: return attributes;
292: }
293:
294: public String getAttribute(String attributeName) {
295: return attributes.get(attributeName);
296: }
297:
298: public Set<String> getClearAttributes() {
299: return clearAttributes;
300: }
301:
302: public Set<String> getNullAttributes() {
303: return nullAttributes;
304: }
305:
306: public boolean isNullAttribute(String attributeName) {
307: return nullAttributes.contains(attributeName);
308: }
309:
310: public boolean isClearAttribute(String attributeName) {
311: return clearAttributes.contains(attributeName);
312: }
313:
314: public Set<String> getClearReferences() {
315: return clearReferences;
316: }
317:
318: public boolean isClearReference(String referenceName) {
319: return clearReferences.contains(referenceName);
320: }
321:
322: public void setClearAttribute(String attributeName) {
323: clearAttributes.add(attributeName);
324: }
325:
326: public void setNullAttribute(String attributeName) {
327: nullAttributes.add(attributeName);
328: }
329:
330: public void setClearReference(String referenceName) {
331: clearReferences.add(referenceName);
332: }
333:
334: public void setAttribute(String attributeName,
335: Object attributeValue, String attributeType,
336: ClassLoader classLoader) throws InvalidAttributeException {
337: String stringValue = getAsText(attributeName, attributeValue,
338: attributeType, classLoader);
339: setAttribute(attributeName, stringValue);
340: }
341:
342: public void setAttribute(String attributeName, String attributeValue) {
343: if (attributeValue == null || attributeValue.length() == 0) {
344: clearAttributes.add(attributeName);
345: } else {
346: attributes.put(attributeName, attributeValue);
347: }
348: }
349:
350: public Map<String, ReferencePatterns> getReferences() {
351: return references;
352: }
353:
354: public ReferencePatterns getReferencePatterns(String name) {
355: return references.get(name);
356: }
357:
358: public void setReferencePatterns(String name,
359: ReferencePatterns patterns) {
360: references.put(name, patterns);
361: }
362:
363: public boolean applyOverrides(GBeanData data, Artifact configName,
364: AbstractName gbeanName, ClassLoader classLoader)
365: throws InvalidConfigException {
366: if (!isLoad()) {
367: return false;
368: }
369:
370: GBeanInfo gbeanInfo = data.getGBeanInfo();
371:
372: // set attributes
373: for (Map.Entry<String, String> entry : getAttributes()
374: .entrySet()) {
375: String attributeName = entry.getKey();
376: GAttributeInfo attributeInfo = gbeanInfo
377: .getAttribute(attributeName);
378: if (attributeInfo == null) {
379: throw new InvalidConfigException("No attribute: "
380: + attributeName + " for gbean: "
381: + data.getAbstractName());
382: }
383: String valueString = entry.getValue();
384: Object value = getValue(attributeInfo, valueString,
385: configName, gbeanName, classLoader);
386: data.setAttribute(attributeName, value);
387: }
388:
389: //Clear attributes
390: for (String attribute : getClearAttributes()) {
391: data.clearAttribute(attribute);
392: }
393:
394: //Null attributes
395: for (String attribute : getNullAttributes()) {
396: data.setAttribute(attribute, null);
397: }
398:
399: // set references
400: for (Map.Entry<String, ReferencePatterns> entry : getReferences()
401: .entrySet()) {
402: String referenceName = entry.getKey();
403: GReferenceInfo referenceInfo = gbeanInfo
404: .getReference(referenceName);
405: if (referenceInfo == null) {
406: throw new InvalidConfigException("No reference: "
407: + referenceName + " for gbean: "
408: + data.getAbstractName());
409: }
410:
411: ReferencePatterns referencePatterns = entry.getValue();
412:
413: data.setReferencePatterns(referenceName, referencePatterns);
414: }
415:
416: //Clear references
417: for (String reference : getClearReferences()) {
418: data.clearReference(reference);
419: }
420:
421: return true;
422: }
423:
424: private synchronized Object getValue(GAttributeInfo attribute,
425: String value, Artifact configurationName,
426: AbstractName gbeanName, ClassLoader classLoader) {
427: if (value == null) {
428: return null;
429: }
430: value = substituteVariables(attribute.getName(), value);
431: PropertyEditor editor = loadPropertyEditor(attribute,
432: classLoader);
433: editor.setAsText(value);
434: log.debug("Setting value for " + configurationName + "/"
435: + gbeanName + "/" + attribute.getName() + " to value "
436: + value);
437: return editor.getValue();
438: }
439:
440: protected PropertyEditor loadPropertyEditor(
441: GAttributeInfo attribute, ClassLoader classLoader) {
442: String propertyEditor = propertyEditors
443: .get(attribute.getName());
444: if (null == propertyEditor) {
445: PropertyEditor editor;
446: try {
447: editor = PropertyEditors.findEditor(
448: attribute.getType(), classLoader);
449: } catch (ClassNotFoundException e) {
450: log.error("Unable to load attribute type "
451: + attribute.getType());
452: return null;
453: }
454: if (editor == null) {
455: log.debug("Unable to parse attribute of type "
456: + attribute.getType() + "; no editor found");
457: }
458: return editor;
459: } else {
460: try {
461: Class propertyEditorClass = classLoader
462: .loadClass(propertyEditor);
463: return (PropertyEditor) propertyEditorClass
464: .newInstance();
465: } catch (Exception ex) {
466: throw new IllegalStateException(
467: "Cannot load property editor ["
468: + propertyEditor + "]", ex);
469: }
470: }
471: }
472:
473: public String substituteVariables(String attributeName, String input) {
474: if (expressionParser != null) {
475: return expressionParser.parse(input);
476: }
477: return input;
478: }
479:
480: /**
481: * Creates a new child of the supplied parent with the data for this
482: * GBeanOverride, adds it to the parent, and then returns the new
483: * child element.
484: *
485: * @return newly created element for this override
486: */
487: public GbeanType writeXml() {
488: GbeanType gbean = new GbeanType();
489: String gbeanName;
490: if (name instanceof String) {
491: gbeanName = (String) name;
492: } else {
493: gbeanName = name.toString();
494: }
495: gbean.setName(gbeanName);
496: if (gbeanInfo != null) {
497: gbean.setGbeanInfo(gbeanInfo);
498: }
499: if (!load) {
500: gbean.setLoad(false);
501: }
502: if (comment != null) {
503: gbean.setComment(comment);
504: }
505:
506: // attributes
507: for (Map.Entry<String, String> entry : attributes.entrySet()) {
508: String name = entry.getKey();
509: String value = entry.getValue();
510: if (value == null) {
511: setNullAttribute(name);
512: } else {
513: if (isNullAttribute(name)) {
514: nullAttributes.remove(name);
515: }
516: if (name.toLowerCase().indexOf("password") > -1) {
517: value = EncryptionManager.encrypt(value);
518: }
519: /**
520: * if there was a value such as jdbc url with & then when that value was oulled
521: * from the config.xml the & would have been replaced/converted to '&', we need to check
522: * and change it back because an & would create a parse exception.
523: */
524: value = "<attribute xmlns='" + ATTRIBUTE_NAMESPACE
525: + "'>" + value.replaceAll("&(?!amp;)", "&")
526: + "</attribute>";
527: Reader reader = new StringReader(value);
528: try {
529: AttributeType attribute = AttributesXmlUtil
530: .loadAttribute(reader);
531: attribute.setName(name);
532: String editorClass = propertyEditors.get(name);
533: if (null != editorClass) {
534: attribute.setPropertyEditor(editorClass);
535: }
536: gbean.getAttributeOrReference().add(attribute);
537: } catch (Exception e) {
538: log.error("Could not serialize attribute " + name
539: + " in gbean " + gbeanName + ", value: "
540: + value, e);
541: }
542: }
543: }
544:
545: // cleared attributes
546: for (String name : clearAttributes) {
547: AttributeType attribute = new AttributeType();
548: attribute.setName(name);
549: gbean.getAttributeOrReference().add(attribute);
550: }
551:
552: // Null attributes
553: for (String name : nullAttributes) {
554: AttributeType attribute = new AttributeType();
555: attribute.setName(name);
556: attribute.setNull(true);
557: gbean.getAttributeOrReference().add(attribute);
558: }
559:
560: // references
561: for (Map.Entry<String, ReferencePatterns> entry : references
562: .entrySet()) {
563: String name = entry.getKey();
564: ReferencePatterns patterns = entry.getValue();
565: ReferenceType reference = new ReferenceType();
566: reference.setName(name);
567:
568: Set<AbstractNameQuery> patternSet;
569: if (patterns.isResolved()) {
570: patternSet = Collections
571: .singleton(new AbstractNameQuery(patterns
572: .getAbstractName()));
573: } else {
574: patternSet = patterns.getPatterns();
575: }
576:
577: for (AbstractNameQuery pattern : patternSet) {
578: ReferenceType.Pattern patternType = new ReferenceType.Pattern();
579: Artifact artifact = pattern.getArtifact();
580:
581: if (artifact != null) {
582: if (artifact.getGroupId() != null) {
583: patternType.setGroupId(artifact.getGroupId());
584: }
585: if (artifact.getArtifactId() != null) {
586: patternType.setArtifactId(artifact
587: .getArtifactId());
588: }
589: if (artifact.getVersion() != null) {
590: patternType.setVersion(artifact.getVersion()
591: .toString());
592: }
593: if (artifact.getType() != null) {
594: patternType.setType(artifact.getType());
595: }
596: }
597:
598: Map nameMap = pattern.getName();
599: if (nameMap.get("module") != null) {
600: patternType.setModule((String) nameMap
601: .get("module"));
602: }
603:
604: if (nameMap.get("name") != null) {
605: patternType.setName((String) nameMap.get("name"));
606: }
607: reference.getPattern().add(patternType);
608: }
609: gbean.getAttributeOrReference().add(reference);
610: }
611:
612: // cleared references
613: for (String name : clearReferences) {
614: ReferenceType reference = new ReferenceType();
615: reference.setName(name);
616: gbean.getAttributeOrReference().add(reference);
617: }
618:
619: return gbean;
620: }
621:
622: protected String getAsText(String attributeName, Object value,
623: String type, ClassLoader classLoader)
624: throws InvalidAttributeException {
625: try {
626: if (null == value || value instanceof String) {
627: return (String) value;
628: }
629:
630: Class typeClass = ClassLoading.loadClass(type, classLoader);
631: PropertyEditor editor = PropertyEditors.findEditor(value
632: .getClass());
633: if (null == editor) {
634: editor = PropertyEditors.findEditor(typeClass);
635: if (null == editor) {
636: throw new InvalidAttributeException(
637: "Unable to format attribute of type "
638: + type + "; no editor found");
639: }
640: }
641:
642: if (!type.equals(value.getClass().getName())
643: && !typeClass.isPrimitive()
644: && !Collection.class.isAssignableFrom(typeClass)) {
645: propertyEditors.put(attributeName, editor.getClass()
646: .getName());
647: }
648:
649: editor.setValue(value);
650: return editor.getAsText();
651: } catch (ClassNotFoundException e) {
652: //todo: use the Configuration's ClassLoader to load the attribute, if this ever becomes an issue
653: throw (InvalidAttributeException) new InvalidAttributeException(
654: "Unable to store attribute type " + type)
655: .initCause(e);
656: }
657: }
658: }
|