KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > config > format > DefaultConfigurationFormatService


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.core.config.format;
19
20
21 import java.beans.BeanInfo JavaDoc;
22 import java.beans.IntrospectionException JavaDoc;
23 import java.beans.Introspector JavaDoc;
24 import java.beans.PropertyDescriptor JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.lang.reflect.Array JavaDoc;
28 import java.lang.reflect.InvocationTargetException JavaDoc;
29 import java.lang.reflect.Method JavaDoc;
30 import java.lang.reflect.Proxy JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35
36 import org.jdom.Document;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 import org.sape.carbon.core.config.Config;
42 import org.sape.carbon.core.config.Configuration;
43 import org.sape.carbon.core.config.ConfigurationRuntimeException;
44 import org.sape.carbon.core.config.InvalidConfigurationException;
45 import org.sape.carbon.core.config.PropertyConfiguration;
46 import org.sape.carbon.core.config.format.jdom.JDOMConfigurationFactory;
47 import org.sape.carbon.core.config.format.jdom.JDOMConfigurationProxy;
48 import org.sape.carbon.core.config.format.jdom.JDOMPropertyConfiguration;
49 import org.sape.carbon.core.config.node.Node;
50 import org.sape.carbon.core.config.type.ConfigurationTypeService;
51 import org.sape.carbon.core.config.type.ConfigurationTypeServiceFactory;
52 import org.sape.carbon.core.exception.ExceptionUtility;
53 import org.sape.carbon.core.exception.InvalidParameterException;
54 import org.sape.carbon.core.util.string.StringUtil;
55
56
57 /**
58  * <P>This default implementation of the configuration format serivce provides
59  * an XML based configuration to implemented interface form of configurations.
60  * </P>
61  *
62  * Copyright 2002 Sapient
63  * @since carbon 1.0
64  * @author Greg Hinkle, January 2002
65  * @version $Revision: 1.34 $($Author: dvoet $ / $Date: 2003/07/29 18:57:25 $)
66  */

67 public class DefaultConfigurationFormatService
68     implements ConfigurationFormatService {
69
70     /**
71      * Provides a handle to Apache-commons logger
72      */

73     private Log log =
74         LogFactory.getLog(this.getClass());
75
76     /**
77      * <p>
78      * Creates a new configuration object of the type specified.
79      * </p>
80      * @param configurationClass type of the configuration object to build
81      * @return the newly created configuration
82      */

83     public Configuration newConfiguration(Class JavaDoc configurationClass) {
84         if (!Configuration.class.isAssignableFrom(configurationClass)) {
85             throw new org.sape.carbon.core.exception.InvalidParameterException(
86                 this.getClass(),
87                 "The supplied type ["
88                     + configurationClass.getName()
89                     + "] was not assignable from "
90                     + Configuration.class.getName());
91         }
92
93         if (configurationClass == PropertyConfiguration.class) {
94             return new JDOMPropertyConfiguration();
95
96         } else {
97             JDOMConfigurationProxy proxy =
98                 new JDOMConfigurationProxy(configurationClass);
99
100             Configuration config =
101                 (Configuration) Proxy.newProxyInstance(
102                     this.getClass().getClassLoader(),
103                     new Class JavaDoc[] {configurationClass},
104                     proxy);
105
106             return config;
107         }
108     }
109
110     /**
111      * <P>Loads a <code>Configuration</code> object from the given
112      * <code>InputStream</code>. This Configuration object will represent
113      * the full object-graph depiction of a live configuration.</P>
114      *
115      * @param name The name of the configuration node
116      * @param in the {@link java.io.InputStream} from which
117      * the configuration will be read
118      * @throws ConfigurationFormatException when there is a formatting error
119      * with the input stream
120      * @return The Configuration object representing a live
121      * object graph of the data from the input stream
122      */

123     public Configuration readConfigurationStream(String JavaDoc name, InputStream JavaDoc in)
124         throws ConfigurationFormatException {
125
126         ConfigurationDataFormatService dataFormat =
127             new JDOMConfigurationFactory();
128
129         Document dataNode = dataFormat.readConfigurationStreamToData(name, in);
130
131         String JavaDoc className = null;
132         Class JavaDoc configurationClass = null;
133         try {
134             className =
135                 dataNode.getRootElement().getAttributeValue(
136                     "ConfigurationInterface");
137
138             if (className == null) {
139                 throw new InvalidConfigurationException(
140                     this.getClass(),
141                     name,
142                     "ConfigurationInterface",
143                     "Document did not supply a valid ConfigurationInterface "
144                         + "class name. All configuration documents must supply "
145                         + "the Java interface which represents them.");
146             } else {
147                 configurationClass = Class.forName(className);
148             }
149         } catch (ClassNotFoundException JavaDoc cnfe) {
150             throw new InvalidConfigurationException(
151                 this.getClass(),
152                 name,
153                 "ConfigurationInterface",
154                 "Document did not supply a valid ConfigurationInterface "
155                     + "class name. The class ["
156                     + className
157                     + "] could not be found.");
158         }
159
160         Configuration config = null;
161
162         if (configurationClass.equals(PropertyConfiguration.class)) {
163             String JavaDoc extendedConfigurationName =
164                 dataNode.getRootElement().getAttributeValue("Extends");
165
166             if (extendedConfigurationName == null) {
167                 config = new JDOMPropertyConfiguration(dataNode);
168             } else {
169                 try {
170                     PropertyConfiguration extendedConfiguration =
171                         (PropertyConfiguration) Config
172                             .getInstance()
173                             .fetchConfiguration(
174                             extendedConfigurationName);
175
176                     config =
177                         new JDOMPropertyConfiguration(
178                             dataNode,
179                             extendedConfiguration);
180
181                 } catch (ConfigurationRuntimeException cre) {
182                     throw new InvalidConfigurationException(
183                         this.getClass(),
184                         name,
185                         "Extends",
186                         "Could not reference parent configuration: ["
187                             + extendedConfigurationName
188                             + "]",
189                         cre);
190
191                 } catch (ClassCastException JavaDoc cce) {
192                     throw new InvalidConfigurationException(
193                         this.getClass(),
194                         name,
195                          "Extends",
196                         "Parent was not assignable from "
197                             + PropertyConfiguration.class.getClass(),
198                         cce);
199                 }
200             }
201         } else {
202             JDOMConfigurationProxy proxy =
203                 new JDOMConfigurationProxy(dataNode, configurationClass);
204
205             if (log.isTraceEnabled()) {
206                 log.trace("Instantiating new Configuration for document ["
207                     + name + "] with class of ["
208                     + configurationClass.getName()
209                     + "]");
210             }
211             try {
212                 config =
213                     (Configuration) Proxy.newProxyInstance(
214                         this.getClass().getClassLoader(),
215                         new Class JavaDoc[] {configurationClass},
216                         proxy);
217             } catch (ClassCastException JavaDoc cce) {
218                 throw new InvalidConfigurationException(
219                     this.getClass(),
220                     name,
221                     "ConfigurationInterface",
222                     "Configuration class [" +
223                         configurationClass.getName() +
224                         "] was not assignable from [" +
225                         Configuration.class.getName() + "]");
226             }
227         }
228         config.setConfigurationName(name);
229
230         return config;
231     }
232
233     /**
234      * <P>Stores the raw version of the provided <code>Configuration</code>
235      * object in the format that this format service implementation
236      * understands.</P>
237      *
238      * @param out The output stream to which the raw configuration
239      * data should be written
240      * @param configuration The Configuration object to be stored; may be any
241      * subclass of Configuration
242      * @throws ConfigurationFormatException When unable to write a
243      * configuration's raw format to the output stream
244      */

245     public void writeConfigurationStream(
246         Configuration configuration,
247         OutputStream JavaDoc out)
248         throws ConfigurationFormatException {
249
250         Document dataNode = configuration.getDataStructure();
251
252         ConfigurationDataFormatService dataFormat =
253             new JDOMConfigurationFactory();
254
255         dataFormat.writeConfigurationStreamToData(dataNode, out);
256     }
257
258     /**
259      * Creates an indexed name for use within the format service. If
260      * parentName is not null, an absolute name is created, otherwise,
261      * parentName is ignored.
262      *
263      * @param parentName for use when creating absolute indexed names, can be
264      * null for creating local names
265      * @param name name of indexed attribute, cannot be null
266      * @param index cannot be negative
267      * @return String indexed name
268      * @since carbon 1.1
269      */

270     protected String JavaDoc constructIndexedName(
271         String JavaDoc parentName,
272         String JavaDoc name,
273         int index) {
274
275         if (name == null) {
276             throw new InvalidParameterException(
277                 this.getClass(), "Name cannot be null");
278         }
279
280         if (index < 0) {
281             throw new InvalidParameterException(
282                 this.getClass(), "Index cannot be negative");
283         }
284
285         StringBuffer JavaDoc indexedName = new StringBuffer JavaDoc();
286         if (parentName != null) {
287             indexedName.append(parentName).append(Node.DELIMITER);
288         }
289         indexedName.append(name).append("[").append(index).append("]");
290         return indexedName.toString();
291     }
292
293     /**
294      * Creates an indexed name for use within the format service. If
295      * parentName is not null, an absolute name is created, otherwise,
296      * parentName is ignored.
297      *
298      * @param parentName for use when creating absolute indexed names, can be
299      * null for creating local names
300      * @param name name of indexed attribute, cannot be null
301      * @param key they lookup key for the map attribute
302      * @return String indexed name
303      * @since carbon 2.0
304      */

305     protected String JavaDoc constructMapName(
306         String JavaDoc parentName,
307         String JavaDoc name,
308         String JavaDoc key) {
309
310         if (name == null) {
311             throw new InvalidParameterException(
312                 this.getClass(), "Name cannot be null");
313         }
314
315         if (key == null) {
316             throw new InvalidParameterException(
317                 this.getClass(), "Key must not be null.");
318         }
319
320         StringBuffer JavaDoc indexedName = new StringBuffer JavaDoc();
321         if (parentName != null) {
322             indexedName.append(parentName).append(Node.DELIMITER);
323         }
324         indexedName.append(name).append("[").append(key).append("]");
325         return indexedName.toString();
326     }
327
328
329     /**
330      * @see ConfigurationFormatService#getChildConfiguration
331      */

332     public Configuration getChildConfiguration(
333         Configuration parentConfig,
334         String JavaDoc childName) {
335
336         if (parentConfig == null) {
337             throw new InvalidParameterException(
338                 this.getClass(),
339                 "parentConfig cannot be null");
340         }
341
342         if (childName == null) {
343             throw new InvalidParameterException(
344                 this.getClass(),
345                 "childName cannot be null");
346         }
347
348         if (!Proxy.isProxyClass(parentConfig.getClass())) {
349             return null;
350         }
351
352         AbstractConfigurationProxy configProxy =
353             (AbstractConfigurationProxy) Proxy.getInvocationHandler(
354                 parentConfig);
355
356
357
358         if (childName.endsWith("]")) {
359             // the child name is indexed, parse childName to determine
360
// the actual name and the index
361

362             int rightBracketIndex = childName.lastIndexOf("[");
363             String JavaDoc elementName = childName.substring(0, rightBracketIndex);
364             String JavaDoc key =
365                 childName.substring(
366                         rightBracketIndex + 1,
367                         childName.length() - 1);
368
369             Class JavaDoc childType = configProxy.getChildType(elementName);
370             if (Map JavaDoc.class.isAssignableFrom(childType)) {
371                 Map JavaDoc map =
372                     configProxy.getMap(elementName,
373                         configProxy.getCollectionComponentType(elementName));
374                 return (Configuration) map.get(key);
375             } else if (childType.isArray()) {
376                 int index =
377                     Integer.parseInt(key);
378
379                 Object JavaDoc array =
380                     configProxy.getArray(
381                         elementName, Configuration.class);
382
383                 return (Configuration) Array.get(array, index);
384             } else {
385                 throw new InvalidParameterException(
386                     this.getClass(), "Unknown child type.");
387             }
388         } else {
389             // lookup the attribute, pass in Configuration.class for the
390
// required type to ensure we get a Configuration back
391
return (Configuration) configProxy.lookupAttribute(
392                 childName,
393                 Configuration.class);
394         }
395     }
396
397     /**
398      * This implementation uses Introspection to determine the list of
399      * all attributes the parentConfig contains. It then uses the
400      * ConfigurationTypeService to determine which of those attributes
401      * are actually sub-configurations (i.e. complex types). If an attribute
402      * is a Map or Array, the actual data is retrieved from parentConfig
403      * and a name is returned for each element.
404      *
405      * @inherit
406      * @see ConfigurationFormatService#getChildConfigurationNames
407      */

408     public Set JavaDoc getChildConfigurationNames(Configuration parentConfig) {
409         if (parentConfig == null) {
410             throw new InvalidParameterException(
411                 this.getClass(),
412                 "parentConfig cannot be null");
413         }
414
415         final ConfigurationTypeService typeService =
416             ConfigurationTypeServiceFactory.getInstance();
417         Class JavaDoc configType = parentConfig.getConfigurationInterface();
418         Set JavaDoc childConfigurationNames = new HashSet JavaDoc();
419
420         BeanInfo JavaDoc configBeanInfo = null;
421         try {
422             configBeanInfo = Introspector.getBeanInfo(configType);
423         } catch (IntrospectionException JavaDoc ie) {
424             log.info("Could not introspect confgiuration ["
425                 + parentConfig.getConfigurationName()
426                 + "], with config interface ["
427                 + configType.getName()
428                 + "], no children will be listed: cause: "
429                 + ExceptionUtility.printStackTracesToString(ie));
430
431             return childConfigurationNames;
432         }
433
434         PropertyDescriptor JavaDoc[] properties =
435             configBeanInfo.getPropertyDescriptors();
436
437         for (int i = 0; i < properties.length; i++) {
438             PropertyDescriptor JavaDoc property = properties[i];
439             Class JavaDoc returnType = property.getPropertyType();
440             String JavaDoc attributeName = StringUtil.capitalize(property.getName());
441
442             try {
443                 Object JavaDoc attributeValue =
444                     property.getReadMethod().invoke(parentConfig, null);
445                 
446                 if (returnType == Map JavaDoc.class) {
447                     Class JavaDoc contentType =
448                         getContentType(parentConfig.getClass(), attributeName);
449
450                     if (contentType != null &&
451                         (typeService.isComplexType(contentType) ||
452                         Configuration.class.isAssignableFrom(contentType))) {
453
454                         Map JavaDoc attributeMap = (Map JavaDoc) attributeValue;
455                         Set JavaDoc keySet = attributeMap.keySet();
456                         for (Iterator JavaDoc keyIter = keySet.iterator();
457                             keyIter.hasNext();
458                             ) {
459
460                             String JavaDoc key = (String JavaDoc) keyIter.next();
461                             childConfigurationNames.add(
462                                 constructMapName(null, attributeName, key));
463                         }
464                     }
465
466                 } else if (returnType.isArray()) {
467                     Class JavaDoc contentType =
468                         attributeValue.getClass().getComponentType();
469
470                     if (typeService.isComplexType(contentType)
471                         || Configuration.class.isAssignableFrom(contentType)) {
472
473                         int arrayLength = Array.getLength(attributeValue);
474                         for (int count = 0; count < arrayLength; count++) {
475                             childConfigurationNames.add(
476                                 constructIndexedName(
477                                     null, attributeName, count));
478                         }
479                     }
480
481                 } else if (attributeValue != null
482                     && (typeService.isComplexType(returnType)
483                     || Configuration.class.isAssignableFrom(returnType))) {
484
485                     childConfigurationNames.add(attributeName);
486                 }
487
488             } catch (IllegalAccessException JavaDoc iae) {
489                 log.info("Could not read attribute ["
490                     + attributeName
491                     + "] from confgiuration ["
492                     + parentConfig.getConfigurationName()
493                     + "], attribute will not be listed as a "
494                     + "child configuration: cause: "
495                     + ExceptionUtility.printStackTracesToString(iae));
496
497             } catch (IllegalArgumentException JavaDoc iae) {
498                 log.info("Could not read attribute ["
499                     + attributeName
500                     + "] from confgiuration ["
501                     + parentConfig.getConfigurationName()
502                     + "], attribute will not be listed as a "
503                     + "child configuration: cause: "
504                     + ExceptionUtility.printStackTracesToString(iae));
505
506             } catch (InvocationTargetException JavaDoc ite) {
507                 log.info("Could not read attribute ["
508                     + attributeName
509                     + "] from confgiuration ["
510                     + parentConfig.getConfigurationName()
511                     + "], attribute will not be listed as a "
512                     + "child configuration: cause: "
513                     + ExceptionUtility.printStackTracesToString(
514                         ite.getTargetException()));
515             }
516         }
517
518         return childConfigurationNames;
519     }
520
521     /**
522      * @inherit
523      * @see ConfigurationFormatService#alterChildConfiguration
524      */

525     public void alterChildConfiguration(
526         Configuration parentConfig,
527         String JavaDoc childName,
528         Configuration newConfig) {
529
530         if (parentConfig == null) {
531             throw new InvalidParameterException(
532                 this.getClass(),
533                 "parentConfig cannot be null");
534         }
535
536         if (childName == null) {
537             throw new InvalidParameterException(
538                 this.getClass(),
539                 "childName cannot be null");
540         }
541
542         if (!Proxy.isProxyClass(parentConfig.getClass())) {
543             return;
544         }
545
546         AbstractConfigurationProxy configProxy =
547             (AbstractConfigurationProxy) Proxy.getInvocationHandler(
548                 parentConfig);
549
550         Class JavaDoc configurationInterface = Configuration.class;
551         if (newConfig != null) {
552             configurationInterface = newConfig.getConfigurationInterface();
553         }
554
555         if (childName.endsWith("]")) {
556             // indexed child name, parse out actual name and index
557
int rightBracketIndex = childName.lastIndexOf("[");
558             String JavaDoc elementName = childName.substring(0, rightBracketIndex);
559             String JavaDoc key =
560                 childName.substring(
561                         rightBracketIndex + 1,
562                         childName.length() - 1);
563
564             Class JavaDoc childType = configProxy.getChildType(elementName);
565             if (Map JavaDoc.class.isAssignableFrom(childType)) {
566                 Class JavaDoc childComponentType =
567                     configProxy.getCollectionComponentType(elementName);
568                 configProxy.setMapValue(
569                     elementName,
570                     childComponentType,
571                     key,
572                     newConfig);
573
574             } else if (childType.isArray()) {
575                 int index = Integer.parseInt(key);
576
577                 // set the array value
578
configProxy.setArrayValue(
579                     elementName,
580                     configurationInterface,
581                     index,
582                     newConfig);
583             } else {
584                 throw new InvalidParameterException(
585                     this.getClass(),
586                     "Unknown component type");
587             }
588
589         } else {
590             // alter the existing value
591
configProxy.alterAttribute(
592                 childName,
593                 configurationInterface,
594                 newConfig);
595         }
596     }
597
598     /**
599      * Gets the type of class contained within the given map.
600      * This assumes that all elements in the map are the same
601      * class.
602      *
603      * @param contentMap map to check the content class
604      * @return the type of class contained within the map.
605      */

606     private Class JavaDoc getContentType(Class JavaDoc configurationType, String JavaDoc attrName) {
607         Class JavaDoc componentType = null;
608         
609         try {
610             Method JavaDoc readMethod = configurationType.getMethod(
611                 "get" + attrName, new Class JavaDoc[] { String JavaDoc.class });
612                 
613             componentType = readMethod.getReturnType();
614         } catch (NoSuchMethodException JavaDoc e) {
615             // read method does not exist, so leave componentType as null
616
}
617         
618         return componentType;
619     }
620 }
621
Popular Tags