001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.core.framework.resourceloader;
017    
018    import org.apache.commons.beanutils.ConstructorUtils;
019    import org.apache.commons.beanutils.PropertyUtils;
020    import org.apache.log4j.Logger;
021    import org.kuali.rice.core.api.reflect.DataDefinition;
022    import org.kuali.rice.core.api.reflect.ObjectDefinition;
023    import org.kuali.rice.core.api.reflect.PropertyDefinition;
024    import org.kuali.rice.core.api.resourceloader.ResourceLoaderException;
025    import org.kuali.rice.core.api.util.ClassLoaderUtils;
026    import org.kuali.rice.core.api.util.ContextClassLoaderBinder;
027    
028    import java.lang.reflect.InvocationTargetException;
029    import java.util.Collection;
030    import java.util.Iterator;
031    import java.util.List;
032    import java.util.concurrent.Callable;
033    
034    /**
035     * Resolves object definitions into java Objects that are wrapped in a proxy whose classloader is the current
036     * context classloader.
037     *
038     * @author Kuali Rice Team (rice.collab@kuali.org)
039     *
040     */
041    public class ObjectDefinitionResolver {
042    
043            private static final Logger LOG = Logger.getLogger(ObjectDefinitionResolver.class);
044    
045            /**
046             * Wraps the given object in a proxy which switches the context classloader appropriately.  The classloader
047             * of this resource loader is used.
048             */
049            public static Object wrap(Object object) {
050                    return wrap(object, ClassLoaderUtils.getDefaultClassLoader());
051            }
052    
053            /**
054             * Wraps the given object in a proxy which switches the context classloader appropriately.  The given classloader
055             * is used as the context classloader.
056             */
057            public static Object wrap(Object object, ClassLoader classLoader) {
058    //              return ContextClassLoaderProxy.wrap(object, classLoader);
059                    return object;
060            }
061    
062            public static Object createObject(String className, ClassLoader classLoader) {
063                    return createObject(new ObjectDefinition(className), classLoader, true);
064            }
065    
066            public static Object createObject(final ObjectDefinition definition, final ClassLoader classLoader, final boolean wrap) {
067                    Object result = null;
068                    final String className = definition.getClassName();
069    
070            try {
071                result = ContextClassLoaderBinder.doInContextClassLoader(classLoader, new Callable() {
072                    public Object call() throws Exception {
073                        Object object = null;
074                        Class<?>[] constructorParamTypes = buildConstructorParamTypes(definition.getConstructorParameters());
075                        Object[] constructorParams = buildConstructorParams(definition.getConstructorParameters());
076                        try {
077                            Class<?> objectClass = Class.forName(className, true, classLoader);
078                            object = loadObject(objectClass, constructorParams, constructorParamTypes);
079                        } catch (ClassNotFoundException e) {
080                            return null;
081                        }
082                        invokeProperties(object, definition.getProperties());
083                        if (wrap) {
084                            return wrap(object, classLoader);
085                        }
086                        return object;
087    
088                    }
089                });
090            } catch (Exception e) {
091                handleException(className, e);
092            }
093    
094                    return result;
095            }
096    
097            protected static Object loadObject(Class<?> objectClass, Object[] constructorParams, Class<?>[] constructorParamTypes) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
098                    return ConstructorUtils.invokeConstructor(objectClass, constructorParams, constructorParamTypes);
099            }
100    
101            private static void handleException(String className, Exception e) {
102                    if (e instanceof RuntimeException) {
103                            throw (RuntimeException) e;
104                    }
105                    throw new ResourceLoaderException("Could not materialize object from definition, using classname: " + className, e);
106            }
107    
108            protected static Class<?>[] buildConstructorParamTypes(List<DataDefinition> constructorParameters) {
109                    Class<?>[] params = new Class[constructorParameters.size()];
110                    int index = 0;
111                    for (Iterator iterator = constructorParameters.iterator(); iterator.hasNext();) {
112                            DataDefinition dataDef = (DataDefinition) iterator.next();
113                            params[index++] = dataDef.getType();
114                    }
115                    return params;
116            }
117    
118            protected static Object[] buildConstructorParams(List<DataDefinition> constructorParameters) {
119                    Object[] params = new Object[constructorParameters.size()];
120                    int index = 0;
121                    for (Iterator iterator = constructorParameters.iterator(); iterator.hasNext();) {
122                            DataDefinition dataDef = (DataDefinition) iterator.next();
123                            params[index++] = dataDef.getValue();
124                    }
125                    return params;
126            }
127    
128            protected static void invokeProperties(Object object, Collection<PropertyDefinition> properties) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
129                    for (Iterator iterator = properties.iterator(); iterator.hasNext();) {
130                            PropertyDefinition propertyDef = (PropertyDefinition) iterator.next();
131                            invokeProperty(object, propertyDef);
132                    }
133            }
134    
135            protected static void invokeProperty(Object object, PropertyDefinition definition) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
136                    PropertyUtils.setProperty(object, definition.getName(), definition.getData().getValue());
137            }
138    
139    
140    }