Java tutorial
/* * Copyright 2013, Unitils.org * * 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 org.unitils.core.context; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.unitils.core.Factory; import org.unitils.core.UnitilsException; import org.unitils.core.annotation.Classifier; import org.unitils.core.annotation.Property; import org.unitils.core.config.Configuration; import org.unitils.core.util.ReflectionUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Type; import java.util.*; /** * @author Tim Ducheyne */ public class Context { /* The logger instance for this class */ protected static Log logger = LogFactory.getLog(Context.class); protected Configuration configuration; protected Map<Key, Object> instances = new LinkedHashMap<Key, Object>(); protected Map<Key, Class<?>> defaultImplementationTypes = new HashMap<Key, Class<?>>(); public Context(Configuration configuration) { this.configuration = configuration; setInstanceOfType(Configuration.class, configuration); setInstanceOfType(Context.class, this); } public Configuration getConfiguration() { return configuration; } public void setDefaultImplementationType(Class<?> type, Class<?> implementationType, String... classifiers) { Key key = new Key(type, classifiers); defaultImplementationTypes.put(key, implementationType); } public Class<?> getDefaultImplementationType(Class<?> type, String... classifiers) { Key key = new Key(type, classifiers); return defaultImplementationTypes.get(key); } @SuppressWarnings({ "unchecked" }) public <T> T getInstanceOfType(Class<T> type, String... classifiers) { Key key = new Key(type, classifiers); T instance = (T) instances.get(key); if (instance == null) { assertNoCycleDependency(key); instances.put(key, null); try { instance = createInstanceOfType(type, classifiers); logger.debug("Created instance for type " + key + ": " + instance.getClass().getName()); } catch (Exception e) { throw new UnitilsException("Unable to create instance for type " + key + ".", e); } instances.put(key, instance); } return instance; } protected <T> void setInstanceOfType(Class<T> type, T implementationType, String... classifiers) { Key key = new Key(type, classifiers); instances.put(key, implementationType); } @SuppressWarnings("unchecked") protected <T> T createInstanceOfType(Class<T> type, String... classifiers) { Class<?> implementationType = getImplementationType(type, classifiers); Constructor<?> constructor = getConstructor(implementationType); Object[] arguments; Class<?>[] parameterTypes; if (constructor == null) { arguments = new Object[0]; parameterTypes = new Class[0]; } else { parameterTypes = constructor.getParameterTypes(); Type[] genericParameterTypes = constructor.getGenericParameterTypes(); Annotation[][] argumentAnnotations = constructor.getParameterAnnotations(); arguments = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { arguments[i] = getArgumentInstance(parameterTypes[i], genericParameterTypes[i], argumentAnnotations[i]); } } Object instance = ReflectionUtils.createInstanceOfType(implementationType, true, parameterTypes, arguments); if (instance instanceof Factory) { instance = ((Factory) instance).create(); } if (!type.isAssignableFrom(instance.getClass())) { throw new UnitilsException( "Implementation type " + instance.getClass().getName() + " is not of type " + type.getName()); } return (T) instance; } protected Constructor<?> getConstructor(Class<?> implementationType) { Constructor<?>[] constructors = implementationType.getConstructors(); // use default constructor if there are no constructors if (constructors.length == 0) { return null; } // try no-args constructor if there is more than one constructor if (constructors.length > 1) { for (Constructor<?> constructor : constructors) { if (constructor.getParameterTypes().length == 0) { return null; } } throw new UnitilsException( "Found more than 1 constructor in implementation type " + implementationType.getName()); } return constructors[0]; } protected Object getArgumentInstance(Class<?> argumentType, Type genericArgumentType, Annotation[] argumentAnnotations) { Property propertyAnnotation = getPropertyAnnotation(argumentAnnotations); Classifier classifierAnnotation = getClassifierAnnotation(argumentAnnotations); String[] argumentClassifiers = classifierAnnotation == null ? new String[0] : classifierAnnotation.value(); Object instance; if (propertyAnnotation != null) { instance = getPropertyValue(propertyAnnotation, argumentType, genericArgumentType, argumentClassifiers); } else { instance = getInstanceOfType(argumentType, argumentClassifiers); } return instance; } protected Object getPropertyValue(Property propertyAnnotation, Class<?> argumentType, Type genericArgumentType, String[] argumentClassifiers) { String propertyName = propertyAnnotation.value(); boolean optional = propertyAnnotation.optional(); if (List.class.isAssignableFrom(argumentType)) { Class<?> elementType = ReflectionUtils.getGenericParameterClass(genericArgumentType); if (elementType == null) { elementType = String.class; } if (optional) { return configuration.getOptionalValueListOfType(elementType, propertyName, argumentClassifiers); } return configuration.getValueListOfType(elementType, propertyName, argumentClassifiers); } if (optional) { return configuration.getOptionalValueOfType(argumentType, propertyName, argumentClassifiers); } return configuration.getValueOfType(argumentType, propertyName, argumentClassifiers); } protected Property getPropertyAnnotation(Annotation[] argumentAnnotations) { for (Annotation argumentAnnotation : argumentAnnotations) { if (argumentAnnotation instanceof Property) { return (Property) argumentAnnotation; } } return null; } protected Classifier getClassifierAnnotation(Annotation[] argumentAnnotations) { for (Annotation argumentAnnotation : argumentAnnotations) { if (argumentAnnotation instanceof Classifier) { return (Classifier) argumentAnnotation; } } return null; } protected Class<?> getImplementationType(Class<?> type, String... classifiers) { // try configured type Class<?> implementationType = getConfiguredImplementationType(type, classifiers); // no type configured, check set default type if (implementationType == null) { implementationType = getDefaultImplementationType(type, classifiers); } // no default found, try to find a factory for the type if (implementationType == null) { implementationType = getDefaultFactoryType(type); } // no default found, use the given type if it is not an interface if (implementationType == null) { if (type.isInterface()) { throw new UnitilsException( "No implementation type configured for given interface type " + type.getName()); } implementationType = type; } if (implementationType.isInterface()) { throw new UnitilsException("Interface found as implementation type of " + implementationType.getName()); } if (!type.isAssignableFrom(implementationType) && !Factory.class.isAssignableFrom(implementationType)) { throw new UnitilsException("Implementation type " + implementationType.getName() + " is not of type " + type.getName() + " or " + Factory.class.getName() + "<" + type.getName() + ">"); } return implementationType; } protected Class<?> getConfiguredImplementationType(Class<?> type, String... classifiers) { String propertyName = type.getName(); String value = configuration.getOptionalString(propertyName, classifiers); if (value == null) { return null; } try { return Class.forName(value); } catch (Exception e) { throw new UnitilsException("Invalid implementation type " + value, e); } } protected Class<?> getDefaultFactoryType(Class<?> type) { try { return Class.forName(type.getName() + "Factory"); } catch (ClassNotFoundException e) { return null; } } protected <T> void assertNoCycleDependency(Key key) { if (!instances.containsKey(key)) { return; } StringBuilder message = new StringBuilder("Unable to create instance for type " + key + ": cyclic dependency detected between following types:\n"); for (Map.Entry<Key, Object> entry : instances.entrySet()) { if (entry.getValue() == null) { message.append(entry.getKey()); message.append('\n'); } } throw new UnitilsException(message.toString()); } protected static class Key { protected Class<?> type; protected String[] classifiers; public Key(Class<?> type, String... classifiers) { this.type = type; if (classifiers == null) { this.classifiers = new String[0]; } else { this.classifiers = classifiers; } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Key key = (Key) o; if (type != null ? !type.equals(key.type) : key.type != null) { return false; } if (!Arrays.equals(classifiers, key.classifiers)) { return false; } return true; } @Override public int hashCode() { int result = type != null ? type.hashCode() : 0; result = 31 * result + (classifiers != null ? Arrays.hashCode(classifiers) : 0); return result; } @Override public String toString() { String typeName = type == null ? "null" : type.getName(); if (classifiers == null || classifiers.length == 0) { return typeName; } return typeName + " (classifiers: " + Arrays.toString(classifiers) + ")"; } } }