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 verifyInterfacesOnly(classes);
060 return createProxy(classes, container, delegate.getComponentImplementation().getClassLoader());
061 }
062
063 public String getDescriptor() {
064 return "Hidden";
065 }
066
067
068 @SuppressWarnings("unchecked")
069 protected T createProxy(Class[] interfaces, final PicoContainer container, final ClassLoader classLoader) {
070 return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
071 public synchronized Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
072 return invokeMethod(getDelegate().getComponentInstance(container, NOTHING.class), method, args, container);
073 }
074 });
075 }
076
077 protected Object invokeMethod(Object componentInstance, Method method, Object[] args, PicoContainer container) throws Throwable {
078 ComponentMonitor componentMonitor = currentMonitor();
079 try {
080 componentMonitor.invoking(container, this, method, componentInstance, args);
081 long startTime = System.currentTimeMillis();
082 Object rv = method.invoke(componentInstance, args);
083 componentMonitor.invoked(container, this,
084 method, componentInstance, System.currentTimeMillis() - startTime, args, rv);
085 return rv;
086 } catch (final InvocationTargetException ite) {
087 componentMonitor.invocationFailed(method, componentInstance, ite);
088 throw ite.getTargetException();
089 }
090 }
091
092 private void verifyInterfacesOnly(Class<?>[] classes) {
093 for (Class<?> clazz : classes) {
094 if (!clazz.isInterface()) {
095 throw new PicoCompositionException(
096 "Class keys must be interfaces. " + clazz + " is not an interface.");
097 }
098 }
099 }
100
101 }