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: */
017: package org.apache.commons.betwixt.io.read;
018:
019: import java.beans.IntrospectionException;
020: import java.lang.reflect.Constructor;
021:
022: import org.apache.commons.betwixt.ElementDescriptor;
023: import org.apache.commons.betwixt.XMLBeanInfo;
024: import org.apache.commons.logging.Log;
025:
026: /**
027: * Group of factory methods for <code>ChainedBeanCreator</code>'s.
028: * The standard implementations used by Betwixt are present here.
029: *
030: * @author Robert Burrell Donkin
031: * @since 0.5
032: */
033: public class ChainedBeanCreatorFactory {
034:
035: private static final Class[] EMPTY_CLASS_ARRAY = {};
036: private static final Object[] EMPTY_OBJECT_ARRAY = {};
037:
038: /** Singleton instance for creating derived beans */
039: private static final ChainedBeanCreator derivedBeanCreator = new ChainedBeanCreator() {
040: public Object create(ElementMapping elementMapping,
041: ReadContext context, BeanCreationChain chain) {
042:
043: Log log = context.getLog();
044: String className = elementMapping.getAttributes().getValue(
045: context.getClassNameAttribute());
046: if (className != null) {
047: try {
048: // load the class we should instantiate
049: ClassLoader classLoader = context.getClassLoader();
050: Class clazz = null;
051: if (classLoader == null) {
052: log.warn("Read context classloader not set.");
053: } else {
054: try {
055: clazz = classLoader.loadClass(className);
056: } catch (ClassNotFoundException e) {
057: log
058: .info("Class not found in context classloader:");
059: log.debug(clazz, e);
060: }
061: }
062: if (clazz == null) {
063: clazz = Class.forName(className);
064: }
065: return newInstance(clazz, log);
066:
067: } catch (Exception e) {
068: // it would be nice to have a pluggable strategy for exception management
069: log.warn("Could not create instance of type: "
070: + className);
071: log.debug("Create new instance failed: ", e);
072: return null;
073: }
074:
075: } else {
076: // pass responsibility down the chain
077: return chain.create(elementMapping, context);
078: }
079: }
080: };
081:
082: /**
083: * Creates a <code>ChainedBeanCreator</code> that constructs derived beans.
084: * These have their classname set by an xml attribute.
085: * @return <code>ChainedBeanCreator</code> that implements Derived beans logic, not null
086: */
087: public static final ChainedBeanCreator createDerivedBeanCreator() {
088: return derivedBeanCreator;
089: }
090:
091: /**
092: * Constructs a new instance of the given class.
093: * Access is forced.
094: * @param theClass <code>Class</code>, not null
095: * @param log <code>Log</code>, not null
096: * @return <code>Object</code>, an instance of the given class
097: * @throws Exception
098: */
099: private static final Object newInstance(Class theClass, Log log)
100: throws Exception {
101: Object result = null;
102: try {
103: Constructor constructor = theClass
104: .getConstructor(EMPTY_CLASS_ARRAY);
105: if (!constructor.isAccessible()) {
106: constructor.setAccessible(true);
107: }
108: result = constructor.newInstance(EMPTY_OBJECT_ARRAY);
109: } catch (SecurityException e) {
110: log.debug("Cannot force accessibility to constructor", e);
111:
112: } catch (NoSuchMethodException e) {
113: if (log.isDebugEnabled()) {
114: log.debug("Class " + theClass
115: + " 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 = new ChainedBeanCreator() {
127: public Object create(ElementMapping element,
128: ReadContext context, BeanCreationChain chain) {
129:
130: Log log = context.getLog();
131: Class theClass = null;
132:
133: ElementDescriptor descriptor = element.getDescriptor();
134: if (descriptor != null) {
135: // check for polymorphism
136: theClass = context.resolvePolymorphicType(element);
137:
138: if (theClass == null) {
139: // created based on implementation class
140: theClass = descriptor.getImplementationClass();
141: }
142: }
143:
144: if (theClass == null) {
145: // create based on type
146: theClass = element.getType();
147: }
148:
149: if (descriptor != null && descriptor.isPolymorphic()) {
150: // check that the type is suitably named
151: try {
152: XMLBeanInfo xmlBeanInfo = context
153: .getXMLIntrospector().introspect(theClass);
154: String namespace = element.getNamespace();
155: String name = element.getName();
156: if (namespace == null) {
157: if (!name.equals(xmlBeanInfo
158: .getElementDescriptor()
159: .getQualifiedName())) {
160: context
161: .getLog()
162: .debug(
163: "Polymorphic type does not match element");
164: return null;
165: }
166: } else if (!namespace.equals(xmlBeanInfo
167: .getElementDescriptor().getURI())
168: || !name.equals(xmlBeanInfo
169: .getElementDescriptor()
170: .getLocalName())) {
171: context
172: .getLog()
173: .debug(
174: "Polymorphic type does not match element");
175: return null;
176: }
177: } catch (IntrospectionException e) {
178: context.getLog().warn(
179: "Could not introspect type to test introspection: "
180: + theClass.getName());
181: context.getLog().debug("Introspection failed: ", e);
182: return null;
183: }
184:
185: }
186:
187: if (log.isTraceEnabled()) {
188: log.trace("Creating instance of class "
189: + theClass.getName() + " for element "
190: + element.getName());
191: }
192:
193: try {
194:
195: Object result = newInstance(theClass, log);
196: return result;
197:
198: } catch (Exception e) {
199: // it would be nice to have a pluggable strategy for exception management
200: context.getLog().warn(
201: "Could not create instance of type: "
202: + theClass.getName());
203: context.getLog().debug("Create new instance failed: ",
204: e);
205: return null;
206: }
207: }
208: };
209:
210: /**
211: * Creates a <code>ChainedBeanCreator</code> that constructs beans based on element type.
212: * @return <code>ChainedBeanCreator</code> that implements load by type beans logic, not null
213: */
214: public static final ChainedBeanCreator createElementTypeBeanCreator() {
215: return elementTypeBeanCreator;
216: }
217:
218: /** Singleton instance that creates beans based on IDREF */
219: private static final ChainedBeanCreator idRefBeanCreator = new ChainedBeanCreator() {
220: public Object create(ElementMapping elementMapping,
221: ReadContext context, BeanCreationChain chain) {
222: if (context.getMapIDs()) {
223: String idref = elementMapping.getAttributes().getValue(
224: "idref");
225: if (idref != null) {
226: // XXX need to check up about ordering
227: // XXX this is a very simple system that assumes that
228: // XXX id occurs before idrefs
229: // XXX would need some thought about how to implement a fuller system
230: context.getLog().trace("Found IDREF");
231: Object bean = context.getBean(idref);
232: if (bean != null) {
233: if (context.getLog().isTraceEnabled()) {
234: context.getLog().trace(
235: "Matched bean " + bean);
236: }
237: return bean;
238: }
239: context.getLog().trace("No match found");
240: }
241: }
242: return chain.create(elementMapping, context);
243: }
244: };
245:
246: /**
247: * Creates a <code>ChainedBeanCreator</code> that finds existing beans based on their IDREF.
248: * @return <code>ChainedBeanCreator</code> that implements IDREF beans logic, not null
249: */
250: public static final ChainedBeanCreator createIDREFBeanCreator() {
251: return idRefBeanCreator;
252: }
253: }
|