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 }