001    /*
002      Copyright (C) 2001-2002 Renaud Pawlak <renaud@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful, but
010      WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
012      Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.core;
020    
021    
022    import java.io.Serializable;
023    import java.lang.reflect.Method;
024    import java.util.Vector;
025    import org.aopalliance.intercept.ConstructorInterceptor;
026    import org.aopalliance.intercept.ConstructorInvocation;
027    import org.aopalliance.intercept.Invocation;
028    import org.aopalliance.intercept.MethodInterceptor;
029    import org.aopalliance.intercept.MethodInvocation;
030    import org.objectweb.jac.core.rtti.ClassRepository;
031    
032    /**
033     * This class is the root class for all the wrappers. A wrapper is
034     * part of a collaboration point when one or several of its methods
035     * wrap a base program object (a wrappee). The set of wrappers
036     * (methods) that wrap a wrappee (method) it called a wrapping
037     * (method) chain.
038     * 
039     * <p>When a method call occurs on a wrappee, the system creates a new
040     * collaboration point in the current collaboration and all the
041     * wrappers are sequentially called.
042     *
043     * <p>A given wrapper passes the hand to the next wrapper (or to the
044     * wrappee if last) by explicitly calling the <code>proceed()</code>
045     * method.
046     *
047     * <p>For futher details about wrapping semantics, see the
048     * <code>Wrapper.proceed()</code> method and the
049     * <code>Wrappee.wrap()</code> methods.
050     *
051     * @author <a href="http://cedric.cnam.fr/~pawlak/index-english.html">Renaud Pawlak</a> */
052    
053    public abstract class Wrapper
054            implements Serializable, MethodInterceptor, ConstructorInterceptor 
055    {
056    
057        protected static final ClassRepository cr =  ClassRepository.get();
058    
059            /**
060             * The constructor sets the aspect component that owns the wrapper
061             * from the context. */
062    
063            public Wrapper(AspectComponent ac) {
064                    //      this.ac = ACManager.get().getName(ac);
065                    this.ac = ac;
066                    if (ac != null) {
067                            //Log.trace("wrapper","new wrapper for "+ac);
068                            ac.addWrapper(this);
069                            //Log.trace("wrapper","wrappers= "+ac.getWrappers());         
070                    }
071            }
072    
073            /** The AC of the wrapper. */
074            //   protected transient String acName;
075            protected final transient AspectComponent ac;
076    
077            /**
078             * Returns true if the method is defined in the wrapper class.
079             *
080             * @param methodName the method to test
081             * @return true if wrapper method
082             */
083    
084            public static boolean defines(String methodName) {
085                    if (ClassRepository.getDirectMethodAccess(Wrapper.class, methodName)[0]
086                            != null)
087                            return true;
088                    return false;
089            }
090    
091            /**
092             * Returns the exception handlers of the given wrapper class. Are
093             * considered as exception handlers all methods whose first
094             * argument is an Exception.
095             *
096             * @param wrapperClass the wrapper class
097             * @return a vector containing the methods names */
098    
099            public static Vector getExceptionHandlers(Class wrapperClass) {
100                    Method[] methods = wrapperClass.getMethods();
101                    Vector ret = new Vector();
102                    for (int i = 0; i < methods.length; i++) {
103                            Class[] pts = methods[i].getParameterTypes();
104                            if (pts.length > 0) {
105                                    Class cl = pts[0];
106                                    boolean ok = false;
107                                    if (cl != null) {
108                                            while (cl != null
109                                                    && (!cl.isPrimitive())
110                                                    && cl != Object.class) {
111                                                    if (cl == Exception.class) {
112                                                            ok = true;
113                                                    }
114                                                    cl = cl.getSuperclass();
115                                            }
116                                    }
117                                    if (ok) {
118                                            ret.add(methods[i].getName());
119                                    }
120                            }
121                    }
122                    return ret;
123            }
124    
125            /**
126             * Returns the Aspect Component the wrapper belongs to by resolving
127             * the name.
128             * 
129             * @see #getAspectComponentName() */
130    
131            public AspectComponent getAspectComponent() {
132                    return ac;
133                    /*
134                    if (ac==null) return null;
135                    return (AspectComponent)ACManager.get().getObject(ac);
136                    */
137            }
138    
139            /**
140             * Returns the Aspect Component name the wrapper belongs to. By
141             * default, this name is set by the system when an aspect component
142             * wraps a given wrappee.
143             *
144             * @see Wrappee */
145    
146            public String getAspectComponentName() {
147                    return ACManager.get().getName(ac);
148            }
149    
150            /**
151             * Set the aspect component name that owns the wrapper. The name of
152             * the aspect component is the one registered in
153             * <code>ACManager</code> (see <code>ACManager.get()</code>). This
154             * method should not be called directly since the aspect component
155             * is automatically set by the system when the wrap method is
156             * called on a wrappee.
157             * 
158             * @param ac the name of the aspect component
159             * @see Wrappee 
160             */
161        /*
162            public void setAspectComponent(AspectComponent ac) {
163                    this.ac = ac;
164            }
165        */
166            /**
167             * String representation of a wrapper (eg <code>{wrapper
168             * className owned by aspectComponentName}</code>). */
169    
170            public String toString() {
171                    if (ac == null) {
172                            return super.toString() + "(nobody)";
173                    }
174                    return super.toString() + "(" + getAspectComponentName() + ")";
175            }
176    
177            /**
178             * Run the next wrapper of the collaboration point.
179             *
180             * <p>A new collaboration point is initiated by the system when a
181             * method call occurs on a base program object (a wrappee). If the
182             * called method is wrapped by one or several wrapping methods
183             * (wrappers methods - see <code>Wrappee.wrap()</code>), then the
184             * system first upcalls all the wrapping methods.
185             *
186             * <p>A wrapping method is a regular Java method that takes no
187             * argument and returns an object. It can access the currently
188             * called method runtime caracteristics by using the
189             * <code>CollaborationParticipant</code> interface methods
190             * implemented by the wrapper (see <code>method()</code> or
191             * <code>args</code>).
192             *
193             * <p>The set of wrapping methods that wraps a wrappee method is
194             * called a wrapping method chain. The wrapping methods of a
195             * wrapping chain are sequentially called. A given wrapping method
196             * of the wrapping chain passes the hand to the next wrapping
197             * method of this chain by calling <code>proceed()</code>.
198             * 
199             * <p>A wrapper should ALWAYS call the proceed method except if it
200             * delibaratly wants to replace all the upcoming wrappers and base
201             * method functionalities (e.g. the
202             * <code>org.objectweb.jac.aspects.binding.ForwardingWrapper</code>).
203             *
204             * <p>The wrapping methods are called in a well-defined order. A
205             * wrapper cannot change this order by himself since it is the
206             * composition aspect that is responsible for setting this order
207             * (see <code>Wrappee.wrap()</code> and
208             * <code>CompositionAspect.getWeaveTimeRank()</code>).
209             * 
210             * <p>Within a clean aspect-oriented design, the wrapper can be
211             * shortcutted by its aspect component if needed (see
212             * <code>AspectComponent.beforeRunningWrapper()</code>).
213             *
214             * <p>In a wrapping method, proceed returns the value returned by
215             * the wrappee method. A wrapper can modify this return value if
216             * needed. The wrapping method code that is placed before the
217             * <code>proceed()</code> call is called <i>before</i> code (since
218             * it is executed before the wrapped method execution), and the
219             * code that is placed after the proceed call is called
220             * <i>after</i> code (since it is executed after).
221             *
222             * <p>A typical wrapping method looks like the following:
223             *
224             * <ul><pre>
225             * public class MyWrapper extends Wrapper {
226             *   public MyWrapper(AspectComponent ac) { super(ac); }
227             *   public Object invoke(MethodInvocation mi) {
228             *     // before code (can access the called method infos)
229             *     ...
230             *     Object result = proceed(mi);
231             *     // after code (can modify the returned value)
232             *     ...
233             *     // must return a value (most of the time the proceed() value)
234             *     return result;
235             *   }
236             *   public Object construct(ConstructorInvocation ci) {
237             *     return proceed(ci);
238             *   }
239             * } 
240             * </pre></ul>
241             *
242             * @return the final value returned by the current method call
243             * @see Wrapping#wrap(Wrappee,Wrapper,AbstractMethodItem)
244             * @see CollaborationParticipant
245             * @see CompositionAspect#getWeaveTimeRank(WrappingChain,Wrapper)
246             * @see AspectComponent#beforeRunningWrapper(Wrapper,String) 
247         */
248            public final Object proceed(Invocation invocation) {
249                    // The AOP Alliance Invocation interface is implemented by the Interaction class 
250                    Interaction interaction = (Interaction) invocation;
251                    interaction.rank += 1;
252                    return Wrapping.nextWrapper(interaction);
253            }
254    
255            // following methods implement the CollaborationParticipant interface
256    
257            public final void attrdef(String name, Object value) {
258                    Collaboration.get().addAttribute(name, value);
259            }
260    
261            public final Object attr(String name) {
262                    return Collaboration.get().getAttribute(name);
263            }
264    
265            public Object invoke(MethodInvocation invocation) throws Throwable {
266            throw new Exception("Wrapper "+this+" does not support method interception.");
267            }
268        
269            public Object construct(ConstructorInvocation invocation) throws Throwable 
270        {
271            throw new Exception("Wrapper "+this+" does not support construction interception.");
272        }
273    
274    }