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.behaviors;
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 import java.lang.reflect.Type;
017
018 import org.picocontainer.ComponentAdapter;
019 import org.picocontainer.ComponentMonitor;
020 import org.picocontainer.PicoContainer;
021 import org.picocontainer.PicoCompositionException;
022 import org.picocontainer.behaviors.AbstractBehavior;
023
024 /**
025 * This component adapter makes it possible to hide the implementation
026 * of a real subject (behind a proxy) provided the key is an interface.
027 * <p/>
028 * This class exists here, because a) it has no deps on external jars, b) dynamic proxy is quite easy.
029 * The user is prompted to look at picocontainer-gems for alternate and bigger implementations.
030 *
031 * @author Aslak Hellesøy
032 * @author Paul Hammant
033 * @see org.picocontainer.gems.adapters.HotSwappingComponentAdapter for a more feature-rich version of this class.
034 */
035 @SuppressWarnings("serial")
036 public class HiddenImplementation<T> extends AbstractBehavior<T> {
037
038 /**
039 * Creates an ImplementationHidingComponentAdapter with a delegate
040 * @param delegate the component adapter to which this adapter delegates
041 */
042 public HiddenImplementation(ComponentAdapter<T> delegate) {
043 super(delegate);
044 }
045
046 public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
047
048 ComponentAdapter<T> delegate = getDelegate();
049 Object componentKey = delegate.getComponentKey();
050 Class<?>[] classes;
051 if (componentKey instanceof Class && ((Class<?>) delegate.getComponentKey()).isInterface()) {
052 classes = new Class[]{(Class<?>) delegate.getComponentKey()};
053 } else if (componentKey instanceof Class[]) {
054 classes = (Class[]) componentKey;
055 } else {
056 return delegate.getComponentInstance(container, into);
057 }
058
059 Class<?>[] interfaces = verifyInterfacesOnly(classes);
060 return createProxy(interfaces, container, delegate.getComponentImplementation().getClassLoader());
061 }
062
063 public String getDescriptor() {
064 return "Hidden";
065 }
066
067
068 @SuppressWarnings("unchecked")
069 private T createProxy(Class[] interfaces, final PicoContainer container, final ClassLoader classLoader) {
070 return (T) Proxy.newProxyInstance(classLoader,
071 interfaces, new InvocationHandler() {
072 private Object instance;
073 public synchronized Object invoke(final Object proxy, final Method method,
074 final Object[] args)
075 throws Throwable {
076 if (instance == null) {
077 instance = getDelegate().getComponentInstance(container, NOTHING.class);
078 }
079 return invokeMethod(instance, method, args, container);
080 }
081 });
082 }
083
084 protected Object invokeMethod(Object componentInstance, Method method, Object[] args, PicoContainer container) throws Throwable {
085 ComponentMonitor componentMonitor = currentMonitor();
086 try {
087 componentMonitor.invoking(container, this, method, componentInstance);
088 long startTime = System.currentTimeMillis();
089 Object object = method.invoke(componentInstance, args);
090 componentMonitor.invoked(container,
091 this,
092 method, componentInstance, System.currentTimeMillis() - startTime);
093 return object;
094 } catch (final InvocationTargetException ite) {
095 componentMonitor.invocationFailed(method, componentInstance, ite);
096 throw ite.getTargetException();
097 }
098 }
099
100 private Class<?>[] verifyInterfacesOnly(Class<?>[] classes) {
101 for (Class<?> clazz : classes) {
102 if (!clazz.isInterface()) {
103 throw new PicoCompositionException(
104 "Class keys must be interfaces. " + clazz + " is not an interface.");
105 }
106 }
107 return classes;
108 }
109
110 }