001 /*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 * Original code by *
009 *****************************************************************************/
010 package org.picocontainer.defaults;
011
012 import java.lang.reflect.InvocationHandler;
013 import java.lang.reflect.InvocationTargetException;
014 import java.lang.reflect.Method;
015 import java.lang.reflect.Proxy;
016
017 import org.picocontainer.ComponentAdapter;
018 import org.picocontainer.ComponentMonitor;
019 import org.picocontainer.PicoContainer;
020 import org.picocontainer.PicoInitializationException;
021 import org.picocontainer.PicoIntrospectionException;
022
023 /**
024 * This component adapter makes it possible to hide the implementation
025 * of a real subject (behind a proxy) provided the key is an interface.
026 * <p/>
027 * This class exists here, because a) it has no deps on external jars, b) dynamic proxy is quite easy.
028 * The user is prompted to look at picocontainer-gems for alternate and bigger implementations.
029 *
030 * @author Aslak Hellesøy
031 * @author Paul Hammant
032 * @see org.picocontainer.gems.HotSwappingComponentAdapter for a more feature-rich version of this class.
033 * @since 1.2, moved from package {@link org.picocontainer.alternatives}
034 */
035 public class ImplementationHidingComponentAdapter extends DecoratingComponentAdapter {
036 private final boolean strict;
037
038 /**
039 * Creates an ImplementationHidingComponentAdapter with a delegate
040 * @param delegate the component adapter to which this adapter delegates
041 * @param strict the scrict mode boolean
042 */
043 public ImplementationHidingComponentAdapter(ComponentAdapter delegate, boolean strict) {
044 super(delegate);
045 this.strict = strict;
046 }
047
048 public Object getComponentInstance(final PicoContainer container)
049 throws PicoInitializationException, PicoIntrospectionException, AssignabilityRegistrationException, NotConcreteRegistrationException {
050
051 Object componentKey = getDelegate().getComponentKey();
052 Class[] classes = null;
053 if (componentKey instanceof Class && ((Class) getDelegate().getComponentKey()).isInterface()) {
054 classes = new Class[]{(Class) getDelegate().getComponentKey()};
055 } else if (componentKey instanceof Class[]) {
056 classes = (Class[]) componentKey;
057 } else {
058 if(strict) {
059 throw new PicoIntrospectionException("In strict mode, " + getClass().getName() + " only allows components registered with interface keys (java.lang.Class or java.lang.Class[])");
060 }
061 return getDelegate().getComponentInstance(container);
062 }
063
064 Class[] interfaces = verifyInterfacesOnly(classes);
065 return createProxy(interfaces, container, getDelegate().getComponentImplementation().getClassLoader());
066 }
067
068 private Object createProxy(Class[] interfaces, final PicoContainer container, final ClassLoader classLoader) {
069 return Proxy.newProxyInstance(classLoader,
070 interfaces, new InvocationHandler() {
071 public Object invoke(final Object proxy, final Method method,
072 final Object[] args)
073 throws Throwable {
074 Object componentInstance = getDelegate().getComponentInstance(container);
075 ComponentMonitor componentMonitor = currentMonitor();
076 try {
077 componentMonitor.invoking(method, componentInstance);
078 long startTime = System.currentTimeMillis();
079 Object object = method.invoke(componentInstance, args);
080 componentMonitor.invoked(method, componentInstance, System.currentTimeMillis() - startTime);
081 return object;
082 } catch (final InvocationTargetException ite) {
083 componentMonitor.invocationFailed(method, componentInstance, ite);
084 throw ite.getTargetException();
085 }
086 }
087 });
088 }
089
090 private Class[] verifyInterfacesOnly(Class[] classes) {
091 for (int i = 0; i < classes.length; i++) {
092 if(!classes[i].isInterface()) {
093 throw new PicoIntrospectionException("Class keys must be interfaces. " + classes[i] + " is not an interface.");
094 }
095 }
096 return classes;
097 }
098
099 }