Java tutorial
/* * def-proxy * * Copyright (c) 2010-2011 * Tim Watson (watson.timothy@gmail.com), Charles Care (c.p.care@gmail.com). * All Rights Reserved. * * This file is provided to you 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.nebularis.defproxy.configuration; import static java.util.Arrays.asList; import static org.nebularis.defproxy.introspection.ReflectionUtils.isAssignable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.Validate; import org.nebularis.defproxy.annotations.Insertion; import org.nebularis.defproxy.introspection.*; import org.nebularis.defproxy.validation.MethodSignatureValidator; /** * Builder for proxy handler configurations. You use this class to * configure the wiring between your proxy interface and backing/delegate * object types. * <p/> * The framework supports direct mappings based on method signature, * variations in name, arity and argument/return types as well as * custom exception handling policies. */ public class ProxyConfigurationBuilder { private ExceptionHandlingPolicy globalExceptionHandlingPolicy; private final Class<?> interfaceClass; private MethodSignatureValidator interfaceValidator; private final Class<?> delegateClass; private MethodSignatureValidator delegateValidator; private TypeConverterFactory converterFactory; private final Map<MethodSignature, MethodSignature> directMappings = new HashMap<MethodSignature, MethodSignature>(); private final Map<MethodSignature, TypeConverter> conversionMappings = new HashMap<MethodSignature, TypeConverter>(); private final Map<MethodSignature, AdditionalArguments> targetSiteWrappers = new HashMap<MethodSignature, AdditionalArguments>(); private final Map<MethodSignature, ExceptionHandlingPolicy> exceptionHandlingPolicies = new HashMap<MethodSignature, ExceptionHandlingPolicy>(); public ProxyConfigurationBuilder(final Class<?> interfaceClass, final Class<?> delegateClass) { Validate.notNull(interfaceClass, "Interface Class cannot be null"); Validate.notNull(delegateClass, "Delegate Class cannot be null"); this.interfaceClass = interfaceClass; this.delegateClass = delegateClass; setInterfaceValidator(new MethodSignatureValidator(interfaceClass)); setDelegateValidator(new MethodSignatureValidator(delegateClass)); } public ExceptionHandlingPolicy getGlobalExceptionHandlingPolicy() { return globalExceptionHandlingPolicy; } public void setGlobalExceptionHandlingPolicy(final ExceptionHandlingPolicy policy) { globalExceptionHandlingPolicy = policy; } public void setExceptionHandlingPolicy(final MethodSignature interfaceMethod, final ExceptionHandlingPolicy policy) { exceptionHandlingPolicies.put(interfaceMethod, policy); } public TypeConverterFactory getTypeConverterFactory() { return converterFactory; } public void setTypeConverterFactory(final TypeConverterFactory converterFactory) { this.converterFactory = converterFactory; } public MethodSignatureValidator getInterfaceValidator() { return interfaceValidator; } public void setInterfaceValidator(final MethodSignatureValidator interfaceValidator) { this.interfaceValidator = interfaceValidator; } public MethodSignatureValidator getDelegateValidator() { return delegateValidator; } public void setDelegateValidator(final MethodSignatureValidator delegateValidator) { this.delegateValidator = delegateValidator; } /** * Gets the delegated {@link org.nebularis.defproxy.introspection.MethodSignature} for * the supplied interface method. * * @param interfaceMethod * @return */ public MethodSignature getDelegatedMethod(final MethodSignature interfaceMethod) { return directMappings.get(interfaceMethod); } /** * Delegates calls to the supplied {@link org.nebularis.defproxy.introspection.MethodSignature} * directly back to the delegate type, using the exact method signature supplied. * * @param interfaceMethod */ public void delegateMethod(final MethodSignature interfaceMethod) { delegateMethod(interfaceMethod, new MethodSignature(interfaceMethod)); } /** * Delegates calls to the supplied {@link org.nebularis.defproxy.introspection.MethodSignature} * directly back to the delegate type, using the mapped method name, and the return type * and parameter types of the interface method signature supplied. * * @param interfaceMethod * @param mappedMethodName */ public void delegateViaMethod(final MethodSignature interfaceMethod, final String mappedMethodName) { delegateMethod(interfaceMethod, new MethodSignature(interfaceMethod.getReturnType(), mappedMethodName, interfaceMethod.getParameterTypes())); } /** * Delegates calls to the interface method, directly to the supplied delegate method. * * @param interfaceMethod * @param delegateMethod */ public void delegateMethod(final MethodSignature interfaceMethod, final MethodSignature delegateMethod) { Validate.notNull(interfaceMethod, "Interface method cannot be null"); Validate.notNull(delegateMethod, "Delegate method cannot be null"); directMappings.put(interfaceMethod, delegateMethod); } /** * Set a {@link org.nebularis.defproxy.introspection.TypeConverter} for the provided interface method. * Providing a converter means that the return type of the delegate method can differ from * that of the underlying delegate, providing that the type converter can massage values from one to the other. * * @param interfaceMethod * @param converter */ public void setTypeConverter(final MethodSignature interfaceMethod, final TypeConverter converter) { conversionMappings.put(interfaceMethod, converter); } /** * Wrap calls to the supplied delegateMethod with the specified (additional) parameters. * * @param prefix * @param delegateMethod * @param params */ public void wrapDelegate(final Insertion prefix, final MethodSignature delegateMethod, final Object... params) { targetSiteWrappers.put(delegateMethod, new AdditionalArguments(prefix, params)); } /** * Generates a {@link org.nebularis.defproxy.configuration.ProxyConfiguration} for the current * builder state, throwing a checked exception if the mapping is in any way incorrect. * * @return * @throws MappingException */ public ProxyConfiguration generateProxyConfiguration() throws MappingException { final ProxyConfiguration configuration = new ProxyConfiguration(); for (Map.Entry<MethodSignature, MethodSignature> entry : directMappings.entrySet()) { final MethodSignature interfaceMethod = entry.getKey(); final MethodSignature delegateMethod = entry.getValue(); check(interfaceMethod, getInterfaceValidator(), interfaceClass); check(delegateMethod, getDelegateValidator(), delegateClass); checkCompatibility(interfaceMethod, delegateMethod); // Q: where does this code belong? MethodInvoker invoker; if (targetSiteWrappers.containsKey(delegateMethod)) { final AdditionalArguments slot = targetSiteWrappers.get(delegateMethod); invoker = new TargetSiteWrapper(delegateMethod, slot.insertion, slot.params); } else { invoker = new MethodInvokerTemplate(delegateMethod); } final ExceptionHandlingPolicy policy = getExceptionHandlingPolicy(interfaceMethod); invoker.setExceptionHandlerPolicy(policy); invoker.setTypeConverter(getTypeConverter(interfaceMethod, delegateMethod)); configuration.registerMethodInvoker(interfaceMethod, invoker); } return configuration; } private ExceptionHandlingPolicy getExceptionHandlingPolicy(final MethodSignature interfaceMethod) { ExceptionHandlingPolicy policy = exceptionHandlingPolicies.get(interfaceMethod); if (policy != null) return policy; return globalExceptionHandlingPolicy; } private void check(final MethodSignature sig, final MethodSignatureValidator validator, final Class<?> checkClass) throws InvalidMethodMappingException { if (!validator.check(sig)) { throw new InvalidMethodMappingException(sig, checkClass); } } public void checkCompatibility(MethodSignature interfaceMethod, MethodSignature delegateMethod) throws IncompatibleMethodMappingException { if (returnTypesAreCompatible(interfaceMethod, delegateMethod)) { if (areParameterTypesCompatible(interfaceMethod, delegateMethod)) { return; } } throw new IncompatibleMethodMappingException(interfaceMethod, delegateMethod); } private boolean returnTypesAreCompatible(final MethodSignature interfaceMethod, final MethodSignature delegateMethod) { final TypeConverter converter = getTypeConverter(interfaceMethod, delegateMethod); if (converter != null && isAssignable(delegateMethod.getReturnType(), converter.getInputType())) { return isAssignable(interfaceMethod.getReturnType(), converter.getOutputType()); } return isAssignable(interfaceMethod.getReturnType(), delegateMethod.getReturnType()); } private TypeConverter getTypeConverter(final MethodSignature interfaceMethod, final MethodSignature delegateMethod) { TypeConverter converter = null; if (conversionMappings.containsKey(interfaceMethod)) { converter = conversionMappings.get(interfaceMethod); } else if (converterFactory != null) { converter = converterFactory.createTypeConverter(delegateMethod.getReturnType(), interfaceMethod.getReturnType()); if (converter != null) { setTypeConverter(interfaceMethod, converter); } } return converter; } private boolean areParameterTypesCompatible(final MethodSignature interfaceMethod, final MethodSignature delegateMethod) { if (targetSiteWrappers.containsKey(delegateMethod)) { final AdditionalArguments slot = targetSiteWrappers.get(delegateMethod); if (slot.insertion.equals(Insertion.Prefix)) { if (slot.params.length < delegateMethod.getParameterTypes().length) { final Class[] inputTypes = new Class[slot.params.length]; final List<Class> inputClassList = asList(delegateMethod.getParameterTypes()); inputClassList.subList(0, inputTypes.length).toArray(inputTypes); return isAssignable(inputTypes, ClassUtils.toClass(slot.params)); } return isAssignable(delegateMethod.getParameterTypes(), ClassUtils.toClass(slot.params)); } else { assert (slot.insertion.equals(Insertion.Suffix)); final Class[] inputTypes = new Class[delegateMethod.getParameterTypes().length]; final List<Class> inputClassList = asList(delegateMethod.getParameterTypes()); inputClassList.toArray(inputTypes); CollectionUtils.reverseArray(inputTypes); final Class[] slotTypes = new Class[slot.params.length]; final List<Class> slotClassList = asList(ClassUtils.toClass(slot.params)); slotClassList.toArray(slotTypes); CollectionUtils.reverseArray(slotTypes); return isAssignable(inputTypes, slotTypes); } } return isAssignable(interfaceMethod.getParameterTypes(), delegateMethod.getParameterTypes()); } }