All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.healthmarketscience.common.util.ReflectionFactory Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
/*
Copyright (c) 2007 Health Market Science, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.healthmarketscience.common.util;

import java.util.Collections;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;

/**
 * Instead of writing custom factories for custom types, this provides a 
 * single factory implementation that uses reflection to instantiate the 
 * desired objects.  The factory supports two-stage construction of the
 * objects by defining a "configure" method name.  One custom exception type
 * will be thrown by the creation method.
 * 

* Note, there are a few methods that may be overridden to further customize * the factory's behavior, see {@link #getClassLoader}, {@link #expandType}, * and {@link #throwException}. * * @author James Ahlborn */ public class ReflectionFactory { private final Class _requiredType; private final Class _exType; private final String _constructorMethodName; private final Class[] _constructorParamTypes; private final String _configureMethodName; private final Class[] _configureParamTypes; /** * @param requiredType created classes must be of this type * @param exType the type of exception to throw on failure. must have * public constructor of the form * {@code (String, Throwable)} * @param constructorParamTypes the constructor parameter types for the * instantiated classes, may be * {@code null} */ public ReflectionFactory(Class requiredType, Class exType, Class... constructorParamTypes) { this(requiredType, exType, null, constructorParamTypes, null, null); } /** * @param requiredType created classes must be of this type * @param exType the type of exception to throw on failure. must have * public constructor of the form * {@code (String, Throwable)} * @param configureMethodName iff non-{@code null} the name of a public * method that must be declared on the created * instance, which does second-stage * initialization. otherwise, no second-stage * initialization is attempted */ public ReflectionFactory(Class requiredType, Class exType, String configureMethodName) { this(requiredType, exType, null, null, configureMethodName, null); } /** * @param requiredType created classes must be of this type * @param exType the type of exception to throw on failure. must have * public constructor of the form * {@code (String, Throwable)} * @param constructorParamTypes the constructor parameter types for the * instantiated classes, may be * {@code null} * @param configureMethodName iff non-{@code null} the name of a public * method that must be declared on the created * instance, which does second-stage * initialization. otherwise, no second-stage * initialization is attempted * @param configureParamTypes the second-stage initialization parameter * types for the instantiated classes, may be * {@code null} */ public ReflectionFactory(Class requiredType, Class exType, Class[] constructorParamTypes, String configureMethodName, Class[] configureParamTypes) { this(requiredType, exType, null, constructorParamTypes, configureMethodName, configureParamTypes); } /** * @param requiredType created classes must be of this type * @param exType the type of exception to throw on failure. must have * public constructor of the form * {@code (String, Throwable)} * @param constructorMethodName iff non-{@code null} the name of a public * static method that must be declared on the * classes, which is used to construct the * instances. otherwise, a constructor is used * @param constructorParamTypes the constructor parameter types for the * instantiated classes, may be * {@code null}. The caller should not modify * this array after passing it in. * @param configureMethodName iff non-{@code null} the name of a public * method that must be declared on the created * instance, which does second-stage * initialization. otherwise, no second-stage * initialization is attempted * @param configureParamTypes the second-stage initialization parameter * types for the instantiated classes, may be * {@code null}. The caller should not modify this * array after passing it in. */ public ReflectionFactory(Class requiredType, Class exType, String constructorMethodName, Class[] constructorParamTypes, String configureMethodName, Class[] configureParamTypes) { _requiredType = requiredType; _exType = exType; _constructorMethodName = constructorMethodName; _constructorParamTypes = constructorParamTypes; _configureMethodName = configureMethodName; _configureParamTypes = configureParamTypes; } protected Class getRequiredType() { return _requiredType; } protected Class getExceptionType() { return _exType; } protected String getConstructorMethodName() { return _constructorMethodName; } protected Class[] getConstructorParamTypes() { return _constructorParamTypes; } protected String getConfigureMethodName() { return _configureMethodName; } protected Class[] getConfigureParamTypes() { return _configureParamTypes; } /** * May be overridden to choose an alternate classloader for loading classes * of the instantiated objects. * @return classloader to use to load classes for reflective object * instantiation */ protected ClassLoader getClassLoader() { return this.getClass().getClassLoader(); } /** * May be overridden to provide "custom" expansions of the given type name * (default package names, etc). All output type names are expected to be * fully-qualified class names. The first type name to result in an actual * Class will be used for the object creation attempt. Default * implementation merely returns the given string. * @return a list of strings which will be tried in order as prefixes for * loading classes */ protected List expandType(String typeName) throws ExType { return Collections.singletonList(typeName); } /** * Constructs an object of the given type with the given constructor params. * @throws ExType due to some failure during object creation */ public RetType create(String typeName, Object... constructorParams) throws ExType { return create(typeName, constructorParams, (Object[])null); } /** * Constructs and optionally configures an object of the given type with the * given constructor params and optional configuration params. * @throws ExType due to some failure during object creation */ public RetType create(String typeName, Object[] constructorParams, Object[] configureParams) throws ExType { if((getConfigureMethodName() == null) && (!ArrayUtils.isEmpty(configureParams))) { throw new IllegalArgumentException( "configure params given but no configure method defined"); } try { List expandedTypeNames = expandType(typeName); Class tmpClazz = null; for(String expandedTypeName : expandedTypeNames) { try { tmpClazz = Class.forName(expandedTypeName, true, getClassLoader()); // found a legitimate class typeName = expandedTypeName; break; } catch(ClassNotFoundException e) { // NOPMD // keep trying } } if(tmpClazz == null) { // we could not find any classes of the given type throwException("failed to find type(s) " + expandedTypeNames, null); // we should never get here throw new UnreachableStatementException(); } // attempt to coerce to required type Class objClazz = tmpClazz.asSubclass(getRequiredType()); // attempt to instantiate using either constructor or static constructor // method RetType obj = ((getConstructorMethodName() == null) ? (RetType)(objClazz.getConstructor(getConstructorParamTypes()) .newInstance(constructorParams)) : (getRequiredType().cast( objClazz.getMethod(getConstructorMethodName(), getConstructorParamTypes()) .invoke(null, constructorParams)))); if(getConfigureMethodName() != null) { // attempt to configure obj.getClass().getMethod( getConfigureMethodName(), getConfigureParamTypes()) .invoke(obj, configureParams); } // all good! return obj; } catch(Exception e) { // wrap in correct exception type throwException("failed instantiating type " + typeName, e); // we should never get here throw new UnreachableStatementException(e); } } /** * Must throw an exception, either of the desired type for this factory or a * RuntimeException (or Error). Default implementation rethrows the given * exception if it is already the correct type, otherwise reflectively * constructs the desired exception type using the * {@code (String, Throwable)} constructor. If that * construction fails, a RuntimeException is thrown. * * @param msg message for the new exception * @param t optional Throwable cause for the new exception * @throws ExType due to some failure during object creation */ @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") protected void throwException(String msg, Throwable t) throws ExType { if(getExceptionType().isInstance(t)) { throw _exType.cast(t); } ExType newEx = null; try { newEx = getExceptionType().getConstructor(String.class, Throwable.class) .newInstance(msg, t); } catch(Exception e) { throw new RuntimeException("failed instantiating exception " + getExceptionType(), e); } throw newEx; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy