View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */ 
17  package org.apache.commons.betwixt.io.read;
18  
19  import java.beans.IntrospectionException;
20  import java.lang.reflect.Constructor;
21  
22  import org.apache.commons.betwixt.ElementDescriptor;
23  import org.apache.commons.betwixt.XMLBeanInfo;
24  import org.apache.commons.logging.Log;
25  
26  /**  
27    * Group of factory methods for <code>ChainedBeanCreator</code>'s.
28    * The standard implementations used by Betwixt are present here.
29    *
30    * @author Robert Burrell Donkin
31    * @since 0.5
32    */
33  public class ChainedBeanCreatorFactory {
34      
35      private static final Class[] EMPTY_CLASS_ARRAY = {};
36      private static final Object[] EMPTY_OBJECT_ARRAY = {};
37      
38      /** Singleton instance for creating derived beans */
39      private static final ChainedBeanCreator derivedBeanCreator 
40          = new ChainedBeanCreator() {
41              public Object create(
42                                  ElementMapping elementMapping, 
43                                  ReadContext context, 
44                                  BeanCreationChain chain) {
45                                  
46                  Log log = context.getLog();
47                  String className 
48                      = elementMapping
49                          .getAttributes().getValue( context.getClassNameAttribute() );
50                  if ( className != null ) {
51                      try {
52                          // load the class we should instantiate
53                          ClassLoader classLoader = context.getClassLoader();
54                          Class clazz = null;
55                          if ( classLoader == null ) {
56                              log.warn("Read context classloader not set." );
57                          } else {
58                              try
59                              {
60                                  clazz = classLoader.loadClass( className );
61                              } catch (ClassNotFoundException e) {
62                                  log.info("Class not found in context classloader:");
63                                  log.debug(clazz, e);
64                              }
65                          }
66                          if (clazz == null) {
67                              clazz = Class.forName(className);
68                          }
69                          return newInstance(clazz, log);
70                                          
71                      } catch (Exception e) {
72                          // it would be nice to have a pluggable strategy for exception management
73                          log.warn( "Could not create instance of type: " + className );
74                          log.debug( "Create new instance failed: ", e );
75                          return null;
76                      }
77                      
78                  } else {
79                      // pass responsibility down the chain
80                      return chain.create( elementMapping, context );
81                  }
82              }
83          };
84      
85      /**
86        * Creates a <code>ChainedBeanCreator</code> that constructs derived beans.
87        * These have their classname set by an xml attribute.
88        * @return <code>ChainedBeanCreator</code> that implements Derived beans logic, not null
89        */
90      public static final ChainedBeanCreator createDerivedBeanCreator() {
91          return derivedBeanCreator;
92      }
93      
94      /**
95       * Constructs a new instance of the given class.
96       * Access is forced.
97       * @param theClass <code>Class</code>, not null
98       * @param log <code>Log</code>, not null
99       * @return <code>Object</code>, an instance of the given class
100      * @throws Exception
101      */
102     private static final Object newInstance(Class theClass, Log log) throws Exception {
103         Object result = null;
104         try {
105             Constructor constructor = theClass.getConstructor(EMPTY_CLASS_ARRAY);
106             if (!constructor.isAccessible()) {
107                 constructor.setAccessible(true);
108             }
109             result = constructor.newInstance(EMPTY_OBJECT_ARRAY);
110         } catch (SecurityException e) {
111             log.debug("Cannot force accessibility to constructor", e);
112         
113         } catch (NoSuchMethodException e) {
114             if (log.isDebugEnabled()) { 
115                 log.debug("Class " + theClass + " has no empty constructor.");
116             }
117         }
118         
119         if (result == null) {
120             result = theClass.newInstance();
121         }
122         return result; 
123     }
124     
125     /** Singleton instance that creates beans based on type */
126     private static final ChainedBeanCreator elementTypeBeanCreator 
127         = new ChainedBeanCreator() {
128             public Object create(
129                                 ElementMapping element, 
130                                 ReadContext context, 
131                                 BeanCreationChain chain) {
132                 
133                 Log log = context.getLog();
134                 Class theClass = null;
135                 
136                 ElementDescriptor descriptor = element.getDescriptor();
137                 if ( descriptor != null ) {
138                     // check for polymorphism 
139                     theClass = context.resolvePolymorphicType(element);
140                     
141                     if (theClass == null)
142                     {
143                         // created based on implementation class
144                         theClass = descriptor.getImplementationClass();
145                     }
146                 }
147                 
148                 if ( theClass == null ) {
149                     // create based on type
150                     theClass = element.getType();
151                 }
152                 
153                 if (descriptor != null && descriptor.isPolymorphic()) {
154                     // check that the type is suitably named
155                     try {
156                         XMLBeanInfo xmlBeanInfo = context.getXMLIntrospector().introspect(theClass);
157                         String namespace = element.getNamespace();
158                         String name = element.getName();
159                         if (namespace == null) {
160                             if (!name.equals(xmlBeanInfo.getElementDescriptor().getQualifiedName())) {
161                                 context.getLog().debug("Polymorphic type does not match element");
162                                 return null;
163                             }
164                         } else if (!namespace.equals(xmlBeanInfo.getElementDescriptor().getURI())
165                                 || !name.equals(xmlBeanInfo.getElementDescriptor().getLocalName())) {
166                             context.getLog().debug("Polymorphic type does not match element");
167                             return null;
168                         }
169                     } catch (IntrospectionException e) {
170                         context.getLog().warn( 
171                             "Could not introspect type to test introspection: " + theClass.getName() );
172                         context.getLog().debug( "Introspection failed: ", e );
173                         return null;
174                     }
175                     
176                 }
177                 
178                 if ( log.isTraceEnabled() ) {
179                     log.trace(
180                         "Creating instance of class " + theClass.getName() 
181                         + " for element " + element.getName());
182                 }
183                 
184                 try {
185 
186                     Object result = newInstance(theClass, log);
187                     return result;
188                     
189                 } catch (Exception e) {
190                     // it would be nice to have a pluggable strategy for exception management
191                     context.getLog().warn( 
192                         "Could not create instance of type: " + theClass.getName() );
193                     context.getLog().debug( "Create new instance failed: ", e );
194                     return null;
195                 }
196             }
197         }; 
198     
199     /**
200       * Creates a <code>ChainedBeanCreator</code> that constructs beans based on element type.
201       * @return <code>ChainedBeanCreator</code> that implements load by type beans logic, not null
202       */
203     public static final ChainedBeanCreator createElementTypeBeanCreator() {
204         return elementTypeBeanCreator;
205     }
206     
207     /** Singleton instance that creates beans based on IDREF */
208     private static final ChainedBeanCreator idRefBeanCreator 
209         = new ChainedBeanCreator() {
210             public Object create(
211                                 ElementMapping elementMapping, 
212                                 ReadContext context, 
213                                 BeanCreationChain chain) {
214                 if ( context.getMapIDs() ) {
215                     String idref = elementMapping.getAttributes().getValue( "idref" );
216                     if ( idref != null ) {
217                         // XXX need to check up about ordering
218                         // XXX this is a very simple system that assumes that 
219                         // XXX id occurs before idrefs
220                         // XXX would need some thought about how to implement a fuller system
221                         context.getLog().trace( "Found IDREF" );
222                         Object bean = context.getBean( idref );
223                         if ( bean != null ) {
224                             if ( context.getLog().isTraceEnabled() ) {
225                                 context.getLog().trace( "Matched bean " + bean );
226                             }
227                             return bean;
228                         }
229                         context.getLog().trace( "No match found" );
230                     }
231                 }
232                 return chain.create( elementMapping, context );
233             }
234         }; 
235     
236     /**
237       * Creates a <code>ChainedBeanCreator</code> that finds existing beans based on their IDREF.
238       * @return <code>ChainedBeanCreator</code> that implements IDREF beans logic, not null
239       */
240     public static final ChainedBeanCreator createIDREFBeanCreator() {
241         return idRefBeanCreator;
242     }
243 }