001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.core.component.proxy;
019:
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.HashMap;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.sape.carbon.core.component.Component;
029: import org.sape.carbon.core.component.FunctionalInterface;
030: import org.sape.carbon.core.util.reflection.ClassUtil;
031: import org.sape.carbon.core.util.reflection.GenericProxy;
032: import org.sape.carbon.core.util.thread.ReadWriteLock;
033: import org.sape.carbon.core.util.thread.ReentrantWriterPreferenceReadWriteLock;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037:
038: /**
039: * Provides the glue for the Component, and supporting container functionality.
040: * <code>DefaultComponentProxyInvocationHandler</code> is a Dynamic Proxy
041: * Invocation Handler. Its job is to masquerade as either the Component, or
042: * any one of a number of container-feature-providers. called 'Assistants'.
043: * Mappings from interfaces to delegates (being the component instance, or
044: * interceptor instances) are stored. Invocations on those interfaces are passed
045: * to the corresponding delegates. In addition, Assistants may listen for
046: * <code>ProxyEvent</code>s - which are distributed immediately before and after
047: * an invocation being passed to a delegate. Listeners are notified in the order
048: * they are registered, according to the <code>ComponentTemplateConfiguration
049: * </code>.
050: *
051: * Copyright 2002 Sapient
052: * @see java.lang.reflect.Proxy
053: * @see org.sape.carbon.core.component.proxy.Decorator
054: * @see org.sape.carbon.core.component.proxy.Interceptor
055: * @see org.sape.carbon.core.component.factory.ComponentTemplateConfiguration
056: * @since carbon 1.0
057: * @author Chris Herron, February 2002
058: * @version $Revision: 1.44 $($Author: dvoet $ / $Date: 2003/05/05 21:21:14 $)
059: */
060: public class DefaultComponentProxyInvocationHandler extends
061: GenericProxy implements ComponentProxyInvocationHandler {
062:
063: /**
064: * Map of interfaces to their respective delegates. Upon calls to this
065: * component, the target object that will have the supplied method
066: * executed will come from this map.
067: */
068: protected Map delegatesByInterface = new HashMap();
069:
070: /**
071: * This list is a list of interceptors that will be called in the
072: * execution of the represented component. These interceptors call
073: * eachother, in order of this list. The head interceptor is executed
074: * by this handler.
075: */
076: protected List interceptors = new ArrayList();
077:
078: /**
079: * A reference to the first interceptor in the chain. This interceptor
080: * is always called first.
081: */
082: protected Interceptor headInterceptor;
083:
084: /** Method that gets the component name if such method exists. */
085: private static Method GET_COMPONENT_NAME_METHOD;
086:
087: /** Provides a handle to Apache-commons logger. */
088: private Log log = LogFactory.getLog(this .getClass());
089:
090: static {
091: try {
092: GET_COMPONENT_NAME_METHOD = Component.class.getMethod(
093: "getComponentName", new Class[] {});
094: } catch (NoSuchMethodException nsme) {
095: // Do nothing
096: }
097: }
098:
099: /**
100: * The monitor object that coordinates read/write calls to
101: * the represented component. Read calls are considered to
102: * be calls to the component's Functional Interface and should
103: * not change the fundamental lifecylcle of the component.
104: * Write calls are considered calls to the Component Assistant
105: * Delegates that may have effect on the configuration or
106: * lifecycle of the component. Multiple readers are allowed at
107: * one time, but only a single writer (with no readers active)
108: * is allowed.
109: */
110: private final ReadWriteLock monitor = new ReentrantWriterPreferenceReadWriteLock();
111:
112: /**
113: * The implementation of the true component.
114: */
115: protected FunctionalInterface functionalImplementation;
116:
117: /** The name of wrapped component. */
118: protected String componentName = null;
119:
120: /**
121: * Method used during creation of the proxy object to add Assistants
122: * to the component. This mehtod should be called only from the
123: * ComponentFactory when it is creating a new component.
124: *
125: * @see ComponentFactory
126: * @since carbon 2.0
127: */
128: public void addDecorator(Decorator decorator) {
129: addDelegate(ClassUtil.getSuperInterfaces(decorator
130: .getExposedInterfaces()), decorator);
131: }
132:
133: /**
134: * Method used during creation of the proxy object to add the
135: * functional implementation
136: * to the component. This mehtod should be called only from the
137: * ComponentFactory when it is creating a new component.
138: *
139: * @see ComponentFactory
140: *
141: * @param implementedInterfaces all the interfaces (including
142: * super interfaces) implemented by functionalImplementation's
143: * FunctionalInterface
144: * @param functionalImplementation the object that implements the
145: * components FunctionalInterface
146: */
147: public void setFunctionalImplementation(
148: Class[] implementedInterfaces,
149: FunctionalInterface functionalImplementation) {
150:
151: addDelegate(implementedInterfaces, functionalImplementation);
152:
153: this .functionalImplementation = functionalImplementation;
154:
155: }
156:
157: /**
158: * Add a delegate.
159: *
160: * @param representedInterfaces an array of interfaces for which this
161: * delegate may be invoked
162: * @param delegate an instance of the delegate to be delegated to
163: */
164: protected void addDelegate(Class[] representedInterfaces,
165: Object delegate) {
166:
167: if (log.isTraceEnabled()) {
168: log.trace("Adding delegate ["
169: + delegate.getClass().getName()
170: + "] for interfaces "
171: + Arrays.asList(representedInterfaces));
172: }
173:
174: for (int i = 0; i < representedInterfaces.length; i++) {
175: Class currentInterface = representedInterfaces[i];
176:
177: //add the interface->delegate mapping
178: Object previousDelegate = this .delegatesByInterface.put(
179: currentInterface, delegate);
180:
181: // check to make sure another delegate is not linked to this
182: // interface
183: if (previousDelegate != null) {
184:
185: if (log.isWarnEnabled()) {
186: log
187: .warn("Multiple delegates represent the same interface. "
188: + "Conflicting delegates: ["
189: + previousDelegate.getClass()
190: .getName()
191: + ","
192: + delegate.getClass().getName()
193: + "], using the latter");
194: }
195: }
196: }
197:
198: // Setup interceptor support if the delegate is an interceptor
199: if (delegate instanceof Interceptor) {
200: if (log.isTraceEnabled()) {
201: log.trace("Recognized interceptor ["
202: + delegate.getClass().getName()
203: + "], adding to chain.");
204: }
205: addInterceptor((Interceptor) delegate);
206: }
207: }
208:
209: /**
210: * Adds an interceptor the chain.
211: *
212: * @param interceptor the interceptor to add
213: */
214: private void addInterceptor(Interceptor interceptor) {
215:
216: if (log.isTraceEnabled()) {
217: log.trace("Adding Interceptor to chain ["
218: + interceptor.getClass().getName() + "]");
219: }
220: if (!this .interceptors.isEmpty()) {
221: Interceptor previousInterceptor = (Interceptor) this .interceptors
222: .get(this .interceptors.size() - 1);
223:
224: previousInterceptor.setNextInterceptor(interceptor);
225: }
226:
227: this .interceptors.add(interceptor);
228:
229: if (this .headInterceptor == null) {
230: this .headInterceptor = interceptor;
231: }
232:
233: }
234:
235: /**
236: * Gets the delegate based on the given interface.
237: *
238: * @param delegateInterface the interface to get the delegate for
239: * @return the delegate for the given interface
240: */
241: public Object getDelegate(Class delegateInterface) {
242: return this .delegatesByInterface.get(delegateInterface);
243: }
244:
245: /**
246: * Gets the read-lock monitor.
247: *
248: * @return the read-lock monitor
249: */
250: public ReadWriteLock getMonitor() {
251: return this .monitor;
252: }
253:
254: /**
255: * Gets the component name.
256: *
257: * @return the component name
258: */
259: public String getComponentName() {
260: return this .componentName;
261: }
262:
263: /**
264: * Sets the name of the component.
265: *
266: * @param componentName the name of the component
267: */
268: public void setComponentName(String componentName) {
269: this .componentName = componentName;
270: }
271:
272: /**
273: * This method implements the delegation model for the represented
274: * component. It will pass component calls to the implementation object
275: * for a component as well as passing other calls to configured
276: * Component Assistant instances.
277: *
278: * @param proxy the object that is being represented
279: * @param method the method descriptor for the method called
280: * @param args an array of arguments passed to that method
281: * @throws Throwable when there is an exception thrown from the
282: * delegated method. This may be a Checked
283: * exception if the implemented interface declares
284: * the exception. Otherwise checked exceptions will
285: * be automatically wrapped in an
286: * <code>UndeclaredThrowableException</code>.
287: * Runtime exceptions are thrown as is.
288: * @return the return value of the delegated method.
289: */
290: protected Object handleInvoke(Object proxy, Method method,
291: Object[] args) throws Throwable {
292:
293: // special handling for getComponentName
294: if (GET_COMPONENT_NAME_METHOD.equals(method)) {
295: return getComponentName();
296: }
297:
298: Class subjectInterface = method.getDeclaringClass();
299:
300: Object target = getDelegate(subjectInterface);
301: if (target == null) {
302: // Should never happen
303: }
304:
305: Invocation invocation = new Invocation(target, method, args,
306: method.getParameterTypes(),
307: target == this .functionalImplementation);
308:
309: Object result = null;
310:
311: boolean lockAquired = false;
312: if (target == this .functionalImplementation) {
313: this .monitor.readLock().acquire();
314: lockAquired = true;
315: }
316:
317: try {
318:
319: result = this .headInterceptor.invoke(invocation);
320:
321: } catch (InvocationTargetException ite) {
322: // unwrap the exception
323: throw ite.getTargetException();
324:
325: } finally {
326: if (lockAquired) {
327: this .monitor.readLock().release();
328: }
329: }
330:
331: return result;
332: }
333:
334: /**
335: * Returns out the name of the component
336: *
337: * @param proxy the proxy object to toString
338: * @return the String representation of the proxy object
339: */
340: protected String proxyToString(Object proxy) {
341: return getComponentName();
342: }
343:
344: }
|