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
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.adapters.HotSwappingComponentAdapter for a more feature-rich version of this class.
033 */
034 @SuppressWarnings("serial")
035 public class HiddenImplementation<T> extends AbstractBehavior<T> {
036
037 /**
038 * Creates an ImplementationHidingComponentAdapter with a delegate
039 * @param delegate the component adapter to which this adapter delegates
040 */
041 public HiddenImplementation(ComponentAdapter<T> delegate) {
042 super(delegate);
043 }
044
045 public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
046
047 ComponentAdapter<T> delegate = getDelegate();
048 Object componentKey = delegate.getComponentKey();
049 Class<?>[] classes;
050 if (componentKey instanceof Class && ((Class<?>) delegate.getComponentKey()).isInterface()) {
051 classes = new Class[]{(Class<?>) delegate.getComponentKey()};
052 } else if (componentKey instanceof Class[]) {
053 classes = (Class[]) componentKey;
054 } else {
055 return delegate.getComponentInstance(container, into);
056 }
057
058 verifyInterfacesOnly(classes);
059 return createProxy(classes, container, delegate.getComponentImplementation().getClassLoader());
060 }
061
062 public String getDescriptor() {
063 return "Hidden";
064 }
065
066 @SuppressWarnings("unchecked")
067 protected T createProxy(Class[] interfaces, final PicoContainer container, final ClassLoader classLoader) {
068 final PicoContainer container1 = container;
069 return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
070 private final PicoContainer container = container1;
071 private volatile Object instance;
072 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
073 if (instance == null) {
074 synchronized (HiddenImplementation.this) {
075 if (instance == null) {
076 instance = getDelegate().getComponentInstance(container, NOTHING.class);
077 }
078 }
079 }
080 return invokeMethod(instance, method, args, container);
081 }
082 });
083 }
084
085 protected Object invokeMethod(Object componentInstance, Method method, Object[] args, PicoContainer container) throws Throwable {
086 ComponentMonitor componentMonitor = currentMonitor();
087 try {
088 componentMonitor.invoking(container, this, method, componentInstance, args);
089 long startTime = System.currentTimeMillis();
090 Object rv = method.invoke(componentInstance, args);
091 componentMonitor.invoked(container, this,
092 method, componentInstance, System.currentTimeMillis() - startTime, args, rv);
093 return rv;
094 } catch (final InvocationTargetException ite) {
095 componentMonitor.invocationFailed(method, componentInstance, ite);
096 throw ite.getTargetException();
097 }
098 }
099
100 private void 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 }
108
109 }