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 040 = new ChainedBeanCreator() { 041 public Object create( 042 ElementMapping elementMapping, 043 ReadContext context, 044 BeanCreationChain chain) { 045 046 Log log = context.getLog(); 047 String className 048 = elementMapping 049 .getAttributes().getValue( context.getClassNameAttribute() ); 050 if ( className != null ) { 051 try { 052 // load the class we should instantiate 053 ClassLoader classLoader = context.getClassLoader(); 054 Class clazz = null; 055 if ( classLoader == null ) { 056 log.warn("Read context classloader not set." ); 057 } else { 058 try 059 { 060 clazz = classLoader.loadClass( className ); 061 } catch (ClassNotFoundException e) { 062 log.info("Class not found in context classloader:"); 063 log.debug(clazz, e); 064 } 065 } 066 if (clazz == null) { 067 clazz = Class.forName(className); 068 } 069 return newInstance(clazz, log); 070 071 } catch (Exception e) { 072 // it would be nice to have a pluggable strategy for exception management 073 log.warn( "Could not create instance of type: " + className ); 074 log.debug( "Create new instance failed: ", e ); 075 return null; 076 } 077 078 } else { 079 // pass responsibility down the chain 080 return chain.create( elementMapping, context ); 081 } 082 } 083 }; 084 085 /** 086 * Creates a <code>ChainedBeanCreator</code> that constructs derived beans. 087 * These have their classname set by an xml attribute. 088 * @return <code>ChainedBeanCreator</code> that implements Derived beans logic, not null 089 */ 090 public static final ChainedBeanCreator createDerivedBeanCreator() { 091 return derivedBeanCreator; 092 } 093 094 /** 095 * Constructs a new instance of the given class. 096 * Access is forced. 097 * @param theClass <code>Class</code>, not null 098 * @param log <code>Log</code>, not null 099 * @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 }