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 *****************************************************************************/ 010package org.picocontainer.behaviors; 011 012import java.lang.reflect.InvocationHandler; 013import java.lang.reflect.InvocationTargetException; 014import java.lang.reflect.Method; 015import java.lang.reflect.Proxy; 016import java.lang.reflect.Type; 017 018import org.picocontainer.ComponentAdapter; 019import org.picocontainer.ComponentMonitor; 020import org.picocontainer.PicoContainer; 021import 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") 035public 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}