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 }