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 }