001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak, Lionel Seinturier.
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,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU 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    import java.lang.reflect.InvocationTargetException;
022    import java.lang.reflect.Method;
023    import java.util.Arrays;
024    import java.util.Collection;
025    import java.util.HashMap;
026    import java.util.Hashtable;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Vector;
031    import org.aopalliance.intercept.ConstructorInterceptor;
032    import org.aopalliance.intercept.Interceptor;
033    import org.aopalliance.intercept.MethodInterceptor;
034    import org.apache.log4j.Logger;
035    import org.objectweb.jac.core.rtti.AbstractMethodItem;
036    import org.objectweb.jac.core.rtti.ClassItem;
037    import org.objectweb.jac.core.rtti.ClassRepository;
038    import org.objectweb.jac.core.rtti.ConstructorItem;
039    import org.objectweb.jac.core.rtti.MethodItem;
040    import org.objectweb.jac.core.rtti.MixinMethodItem;
041    import org.objectweb.jac.core.rtti.NamingConventions;
042    import org.objectweb.jac.util.Strings;
043    import org.objectweb.jac.util.WrappedThrowableException;
044    
045    /**
046     * This class provides a set of useful static methods that allow the
047     * wrapping of wrappable objects (wrappee) by some wrapper objects.
048     *
049     * @author <a href="mailto:pawlak@cnam.fr">Renaud Pawlak</a>
050     * @author <a href="mailto:Lionel.Seinturier@lip6.fr">Lionel Seinturier</a> */
051    
052    public class Wrapping {
053        static Logger logger = Logger.getLogger("wrapping");
054        static Logger loggerExc = Logger.getLogger("exceptions");
055        static Logger loggerRole = Logger.getLogger("rolemethod");
056        static Logger loggerWuni = Logger.getLogger("wuni");
057    
058        /** The local Aspect Component manager for this container (on
059            optimization purpose). */
060        /** The wrapping chains for the base methods
061            (wrappingChains[object]->hastable[method]->vector). */
062        static transient Map wrappingChains = new java.util.WeakHashMap();
063        /** The wrapping chains for the static base methods
064            (staticWrappingChains[method]->vector). */
065        static transient Map staticWrappingChains = new Hashtable();
066    
067        /** The exceptions handlers for the base methods. */
068        static transient Map catchingChains = new java.util.WeakHashMap();
069    
070        /** The exceptions handlers for the static base methods. */
071        static transient Map staticCatchingChains = new Hashtable();
072    
073        /** The wrappers for the base object. */
074        static transient Map wrappers = new java.util.WeakHashMap();
075    
076        /** The wrappers for the base classes. */
077        static transient Map staticWrappers = new Hashtable();
078    
079        /** The wrapper classes for the base object. */
080        static transient Map wrapperClasses = new java.util.WeakHashMap();
081    
082        /** The wrapper classes for the base classes. */
083        static transient Map staticWrapperClasses = new Hashtable();
084    
085        public static final Object[] emptyArray = new Object[0];
086    
087        /**
088         * Returns the wrapping chain that wraps the given method.
089         *
090         * @param wrappee the wrappee (if null, the given method is
091         * nesserally a static method)
092         * @param method the wrapped method (can be static, then wrappee is
093         * null) 
094         */
095        public static WrappingChain getWrappingChain(
096            Wrappee wrappee,
097            AbstractMethodItem method) 
098        {
099            //         System.out.println("getting wrapping chain for "+method);
100    
101            if (method.isStatic()) {
102                WrappingChain result =
103                    (WrappingChain) staticWrappingChains.get(method);
104                if (result == null) {
105                    result = new WrappingChain();
106                    staticWrappingChains.put(method, result);
107                }
108                //System.out.println("return "+result);
109                return result;
110            } else {
111                Map wrappeeChains = (Map) wrappingChains.get(wrappee);
112                if (wrappeeChains == null) {
113                    wrappeeChains = new Hashtable();
114                    wrappingChains.put(wrappee, wrappeeChains);
115                }
116                WrappingChain result = (WrappingChain) wrappeeChains.get(method);
117                if (result == null) {
118                    result = new WrappingChain();
119                    wrappeeChains.put(method, result);
120                }
121                return result;
122            }
123        }
124    
125        /**
126         * Returns the catching chain that wraps the given method.
127         *
128         * @param wrappee the wrappee (if null, the given method is
129         * nesserally a static method)
130         * @param method the wrapped method (can be static, then wrappee is
131         * null) 
132         */
133        public static List getCatchingChain(
134            Wrappee wrappee,
135            AbstractMethodItem method) 
136        {
137            if (method.isStatic()) {
138                List chain = (List) staticCatchingChains.get(method);
139                if (chain == null) {
140                    chain = new Vector();
141                    staticCatchingChains.put(method, chain);
142                }
143                return chain;
144            } else {
145                Map wrappeeChain = (Map) catchingChains.get(wrappee);
146                if (wrappeeChain == null) {
147                    wrappeeChain = new Hashtable();
148                    catchingChains.put(wrappee, wrappeeChain);
149                }
150                List chain = (List) wrappeeChain.get(method);
151                if (chain == null) {
152                    chain = new Vector();
153                    wrappeeChain.put(method, chain);
154                }
155                return chain;
156            }
157        }
158    
159        /**
160         * Returns the wrappers that wrap the given wrappee or wrappee's
161         * class.
162         *
163         * @param wrappee the wrappee (can be null if wrappeeClass is not
164         * null)
165         * @param wrappeeClass the wrappee's class (can be null if wrappee
166         * is not null) 
167         */
168        public static List getWrappers(Wrappee wrappee, ClassItem wrappeeClass) {
169            if (wrappee == null) {
170                List wrappers = (List) staticWrappers.get(wrappeeClass);
171                if (wrappers == null) {
172                    wrappers = new Vector();
173                    staticWrappers.put(wrappeeClass, wrappers);
174                }
175                return wrappers;
176            } else {
177                List result = (List) wrappers.get(wrappee);
178                if (result == null) {
179                    result = new Vector();
180                    wrappers.put(wrappee, result);
181                }
182                return result;
183            }
184        }
185    
186        /**
187         * Returns the classes of the wrappers that wrap the given wrappee
188         * or wrappee's class.
189         *
190         * @param wrappee the wrappee (can be null if wrappeeClass is not
191         * null)
192         * @param wrappeeClass the wrappee's class (can be null if wrappee
193         * is not null) 
194         */
195        public static List getWrapperClasses(
196            Wrappee wrappee,
197            ClassItem wrappeeClass) 
198        {
199            if (wrappee == null) {
200                List result = (List) staticWrapperClasses.get(wrappeeClass);
201                if (result == null) {
202                    result = new Vector();
203                    staticWrapperClasses.put(wrappeeClass, result);
204                }
205                return result;
206            } else {
207                List result = (List) wrapperClasses.get(wrappee);
208                if (result == null) {
209                    result = new Vector();
210                    wrapperClasses.put(wrappee, result);
211                }
212                return result;
213            }
214        }
215    
216        /**
217         * Adds a wrapper to the current wrappee.
218         *
219         * <p>Any method of this wrapper can then be used on the wrappee
220         * with <code>invokeRoleMethod</code>.
221         *
222         * <p>To precise which method of this wrapper should actually wrap
223         * the current wrappee methods, use the <code>wrap</code> methods.
224         *
225         * @param wrappee the wrappee (can be null if wrappeeClass is not
226         * null, then wraps all the instances of the class in a static
227         * mode)
228         * @param wrappeeClass the wrappee's class (can be null if wrappee
229         * is not null)
230         * @param wrapper the new wrapper */
231    
232        public static void wrap(
233            Wrappee wrappee,
234            ClassItem wrappeeClass,
235            Wrapper wrapper) 
236        {
237            logger.debug("wrapping " + wrappee + "(" + wrappeeClass + ") with " + wrapper);
238            List wrappers = getWrappers(wrappee, wrappeeClass);
239            if (wrappers.contains(wrapper)) {
240                logger.debug("  ignoring already present wrapper");
241                return;
242            }
243    
244            wrappers.add(wrapper);
245            getWrapperClasses(wrappee, wrappeeClass).add(wrapper.getClass());
246    
247            /*
248            String ac = (String) Collaboration.get().getCurAC();
249            if ( ac != null ) {
250               AspectComponent temp_ac = (AspectComponent) 
251                  ACManager.get().getObject( ac );
252               if ( temp_ac != null ) { 
253                  temp_ac.addWrapper( wrapper );
254                  wrapper.setAspectComponent( ac );
255               }
256            }
257            */
258        }
259    
260        static private HashMap wrappableMethods = new HashMap();
261    
262        /**
263         * Tells if a given method is wrappable.
264         *
265         * @param method the method to check */
266    
267        public static boolean isWrappable(AbstractMethodItem method) {
268            // We use a cache because it is too slow otherwise
269            Boolean ret = (Boolean) wrappableMethods.get(method);
270            if (ret == null) {
271                try {
272                    //            getClass().getField( "_" + methodName + "_method_name" );
273                    ((ClassItem) method.getParent()).getActualClass().getField(
274                        "__JAC_TRANSLATED");
275                } catch (Exception e) {
276                    //Log.trace("wrappable","* isWrappable("+method+") -> false");
277                    wrappableMethods.put(method, Boolean.FALSE);
278                    return false;
279                }
280                //Log.trace("wrappable","* isWrappable("+method+") -> true");
281                wrappableMethods.put(method, new Boolean(true));
282                return true;
283            } else {
284                //Log.trace("wrappable","isWrappable("+method+") -> "+ret);
285                return ret.booleanValue();
286            }
287        }
288    
289        public static String printWrappingChain(Interceptor[] wrappingChain) {
290            String result = "[";
291            for (int i = 0; i < wrappingChain.length; i++) {
292                if (i != 0) {
293                    result += ",";
294                }
295                result += wrappingChain[i].toString();
296            }
297    
298            result += "]";
299            return result;
300        }
301    
302        static CompositionAspect ca = null;
303    
304        /**
305         * Wrap a method of an object.
306         *
307         * @param wrappee the object to wrap
308         * @param wrapper the wrapper
309         * @param wrappedMethod the method of wrappee to wrap 
310         */
311        public static void wrap(
312            Wrappee wrappee,
313            Wrapper wrapper,
314            AbstractMethodItem wrappedMethod) 
315        {
316            if (wrapMethod(wrappee,wrapper,wrappedMethod)) {
317                // Add to list of wrappers
318                Wrapping.wrap(wrappee, wrappedMethod.getClassItem(), wrapper);
319            }
320        }
321    
322        /**
323         * Wrap a method of an object. Only updates the wrapping chain of
324         * the method.
325         *
326         * @param wrappee the object to wrap
327         * @param wrapper the wrapper
328         * @param wrappedMethod the method of wrappee to wrap 
329         *
330         * @return true if the method was actually wrapped
331         */
332        public static boolean wrapMethod(
333            Wrappee wrappee,
334            Wrapper wrapper,
335            AbstractMethodItem wrappedMethod) 
336        {
337            if (isWrappable(wrappedMethod)) {
338                logger.debug(wrappedMethod + Strings.hash(wrappedMethod)+" is wrapped by " + wrapper);
339    
340                WrappingChain wrappingChain =
341                    getWrappingChain(wrappee, wrappedMethod.getConcreteMethod());
342                logger.debug("  "+wrappedMethod.getConcreteMethod()+" -> "+wrappingChain+
343                             Strings.hash(wrappingChain));
344    
345                if (wrappingChain.contains(wrapper)) {
346                    logger.debug("  skipping "+wrapper);
347                    return false;
348                }
349    
350                int rank = 0;
351    
352                ACManager acm = ACManager.getACM();
353                if (acm != null) {
354                    if (ca == null)
355                        ca = (CompositionAspect) acm.objects.get(
356                            "JAC_composition_aspect");
357                    if (ca != null) {
358                        rank = ca.getWeaveTimeRank(wrappingChain, wrapper);
359                        //Log.trace("wrap.wrap","found rank="+rank+"/"+wrappingChain.size());
360                    } else {
361                        /*
362                        if( wrappingChain.size() > 0 ) {
363                        Log.warning( "no composition aspect found when wrapping "+
364                                     wrappedMethod + " with " + 
365                                     wrapper.getClass().getName() );
366                        }
367                        */
368                    }
369                }
370    
371                wrappingChain.add(rank, wrapper);
372                return true;
373                //System.out.println("WrappingChain = "+printWrappingChain(wrappingChain));
374            } else {
375                //logger.debug(wrappedMethod+" is not wrappable");
376                return false;
377            }
378    
379        }
380    
381        /**
382         * A nicer way to write <code>wrap</code> when several base methods
383         * need to be wrapped.
384         *
385         * @param wrappee the wrappee (can be null if the wrappeed methods
386         * are static)
387         * @param wrapper the wrapper where the wrapping method is
388         * implemented
389         * @param wrappedMethods the names of the wrapped methods 
390         */
391        public static void wrap(
392            Wrappee wrappee,
393            Wrapper wrapper,
394            AbstractMethodItem[] wrappedMethods) 
395        {
396            if (wrappedMethods == null)
397                return;
398            for (int i = 0; i < wrappedMethods.length; i++) {
399                if (wrappedMethods[i] != null) {
400                    wrap(wrappee, wrapper, wrappedMethods[i]);
401                }
402            }
403        }
404    
405        /**
406         * A string based version of wrap (for simplification purpose).
407         *
408         * @param wrappee the wrappee (cannot be null, this version only
409         * works for non-static methods)
410         * @param wrapper the wrapper where the wrapping method is
411         * implemented
412         * @param wrappedMethods the names of the wrapped methods
413         *
414         * @see #wrap(Wrappee,Wrapper,AbstractMethodItem[]) 
415         */
416        public static void wrap(
417            Wrappee wrappee,
418            Wrapper wrapper,
419            String[] wrappedMethods) 
420        {
421            ClassItem cli = ClassRepository.get().getClass(wrappee);
422            if (wrappedMethods == null)
423                return;
424            for (int i = 0; i < wrappedMethods.length; i++) {
425                if (wrappedMethods[i] != null) {
426                    try {
427                        MethodItem[] methods = cli.getMethods(wrappedMethods[i]);
428                        for (int j = 0; j < methods.length; j++) {
429                            wrap(wrappee, wrapper, methods[j]);
430                        }
431                    } catch (org.objectweb.jac.core.rtti.NoSuchMethodException e) {
432                        logger.error("wrap "+wrappee+","+wrapper+","+Arrays.asList(wrappedMethods)+": "+e);
433                    }
434                }
435            }
436        }
437    
438        /**
439         * Wraps all the wrappable (public) methods of the current wrappee.
440         *
441         * @param wrappee the wrappee (can be null, then only wrap static
442         * methods)
443         * @param wrappeeClass the wrappee's class (can be null if wrappee
444         * is not null)
445         * @param wrapper the wrapper
446         */
447        public static void wrapAll(
448            Wrappee wrappee,
449            ClassItem wrappeeClass,
450            Wrapper wrapper) 
451        {
452            Collection methods = null;
453            if (wrappeeClass != null) {
454                methods = wrappeeClass.getAllMethods();
455            } else {
456                methods =
457                    ClassRepository
458                        .get()
459                        .getClass(wrappee.getClass())
460                        .getAllMethods();
461            }
462            Iterator i = methods.iterator();
463            while (i.hasNext()) {
464                wrap(wrappee, wrapper, (AbstractMethodItem) i.next());
465            }
466        }
467    
468        /**
469         * Wraps all the writer wrappable (public) methods of the current
470         * wrappee.
471         *
472         * @param wrappee the wrappee (cannot be null, this version only
473         * works for non-static methods)
474         * @param wrapper the wrapper
475         */
476        public static void wrapModifiers(Wrappee wrappee, Wrapper wrapper) {
477            try {
478                Collection methods =
479                    ClassRepository.get().getClass(wrappee).getAllModifiers();
480                Iterator i = methods.iterator();
481                while (i.hasNext()) {
482                    MethodItem method = (MethodItem) i.next();
483                    wrap(wrappee, wrapper, method);
484                }
485            } catch (Exception e) {
486                e.printStackTrace();
487            }
488        }
489    
490        /**
491         * Definitively removes a wrapper.
492         *
493         * <p>When this method is called, none of the methods of the
494         * wrapper can be used as role methods anymore.
495         *
496         * <p>This method before calls the <code>unwrapAll</code> to ensure
497         * that none of the current wrappee methods are yet wrapped.
498         *
499         * @param wrappee the wrappee (can be null, then only wrap static
500         * methods)
501         * @param wrappeeClass the wrappee's class (can be null if wrappee
502         * is not null)
503         * @param wrapper the wrapper to remove
504         *
505         * @see #unwrapAll(Wrappee,ClassItem,Wrapper)
506         * @see #invokeRoleMethod(Wrappee,String,Object[]) 
507         */
508        public static void unwrap(
509            Wrappee wrappee,
510            ClassItem wrappeeClass,
511            Wrapper wrapper) 
512        {
513            List wrappers = getWrappers(wrappee, wrappeeClass);
514            if (!wrappers.contains(wrapper))
515                return;
516            logger.debug("unwrapping " + wrappee + " with " + wrapper);
517            wrappers.remove(wrapper);
518            getWrapperClasses(wrappee, wrappeeClass).remove(wrapper.getClass());
519            unwrapAll(wrappee, wrappeeClass, wrapper);
520        }
521    
522        /**
523         * Removes all wrappers that belong to list of wrappers
524         *
525         * @param wrappee the wrappee to unwrap (may be null for static methods)
526         * @param wrappeeClass the class of the wrappee to unwrap
527         * @param acWrappers the wrappers to remove (the wrappers of an aspect component)
528         */
529        public static void unwrap(
530            Wrappee wrappee,
531            ClassItem wrappeeClass,
532            Collection acWrappers) 
533        {
534            List wrappers = getWrappers(wrappee, wrappeeClass);
535            // Since acWrappers.size() >> wrappers.size(), we iterate on wappers
536            Iterator it = wrappers.iterator();
537            while(it.hasNext()) {
538                Wrapper wrapper = (Wrapper)it.next();
539                if (acWrappers.contains(wrapper)) {
540                    logger.debug("unwrapping " + wrappee + " with " + wrapper);
541                    it.remove();
542                    getWrapperClasses(wrappee, wrappeeClass).remove(wrapper.getClass());
543                    unwrapAll(wrappee, wrappeeClass, wrapper);
544                } else {
545                    logger.debug("leaving "+wrapper);
546                }
547            }
548        }
549    
550        /**
551         * Unwraps a single method.
552         *
553         * <p>The wrapper must implement <code>wrappingMethod</code>. If
554         * the wrapped method was not actually wrapped at the time this
555         * method is called, then this code has no effect.
556         *
557         * <p>To definitively remove the wrapper so that none of its method
558         * will not be considered as role methods anymore, use the
559         * <code>unwrap(Wrappee,ClassItem,Wrapper)</code> method.
560         *
561         * @param wrappee the wrappee (can be null, then the wrappedMethod
562         * must be static)
563         * @param wrapper the wrapper
564         * @param wrappedMethod the name of the method to unwrap
565         *
566         * @see #unwrap(Wrappee,ClassItem,Wrapper) 
567         */
568        public static void unwrap(
569            Wrappee wrappee,
570            Wrapper wrapper,
571            AbstractMethodItem wrappedMethod) 
572        {
573            logger.debug("unwrapping " + (wrappee != null ? wrappee.getClass().getName() : "-")+
574                         "."+wrappedMethod+"("+Strings.hex(wrappedMethod)+"-"+
575                         Strings.hex(wrappedMethod.getClassItem())+")"+
576                         " with "+wrapper+"???");
577            WrappingChain wrappingChain = getWrappingChain(wrappee, wrappedMethod.getConcreteMethod());
578            for (int i=0; i<wrappingChain.size(); i++) {
579                if (wrappingChain.get(i) == wrapper) {
580                    logger.debug("unwrapping "+wrappedMethod+"("+Strings.hex(wrappedMethod)+")"+
581                                 " with "+wrapper);
582                    wrappingChain.remove(i);
583                }
584            }
585        }
586    
587        public static void unwrapAll(
588            Wrappee wrappee,
589            ClassItem wrappeeClass,
590            Wrapper wrapper) 
591        {
592            logger.debug("unwrapAll "+wrappeeClass+"-"+Strings.hex(wrappeeClass)+
593                         "-"+wrappeeClass.getClass().getClassLoader());
594            Collection methods = wrappeeClass.getAllMethods();
595            Iterator i = methods.iterator();
596            while (i.hasNext()) {
597                AbstractMethodItem m = (AbstractMethodItem)i.next();
598                if (!(m instanceof MixinMethodItem))
599                    unwrap(wrappee, wrapper, m);
600            }
601        }
602    
603        /**
604         * Tells wether a wrapper wraps a wrappee or a class
605         */
606        public static boolean isWrappedBy(
607            Wrappee wrappee,
608            ClassItem wrappeeClass,
609            Wrapper wrapper) 
610        {
611            return getWrappers(wrappee, wrappeeClass).contains(wrapper);
612        }
613    
614        /**
615         * Returns true if the wrappee or the wrappeeClass is wrapped by a
616         * wrapper class.
617         */
618        public static boolean isWrappedBy(
619            Wrappee wrappee,
620            ClassItem wrappeeClass,
621            Class wrapperClass) 
622        {
623            return getWrapperClasses(wrappee, wrappeeClass).contains(wrapperClass);
624        }
625    
626        /**
627         * Tells wether a wrappee has a wrapper whose class can be
628         * assigned to a given wrapper class.
629         */
630        public static boolean isExtendedBy(
631            Wrappee wrappee,
632            ClassItem wrappeeClass,
633            Class wrapperClass) 
634        {
635            Iterator i = getWrapperClasses(wrappee, wrappeeClass).iterator();
636            while (i.hasNext()) {
637                Class cl = (Class) i.next();
638                if (wrapperClass.isAssignableFrom(cl))
639                    return true;
640            }
641            return false;
642        }
643    
644        public static void addExceptionHandler(
645            Wrappee wrappee,
646            Wrapper wrapper,
647            String method,
648            AbstractMethodItem listenedMethod) 
649        {
650            Vector catchingChain =
651                (Vector) getCatchingChain(wrappee, listenedMethod);
652    
653            Object[] catchingMethod = new Object[2];
654            catchingMethod[0] = wrapper;
655            Method[] methods =
656                ClassRepository.getDirectMethodAccess(wrapper.getClass(), method);
657            if (methods.length > 0) {
658                catchingMethod[1] = methods[0];
659                catchingChain.add(catchingMethod);
660            } else {
661                throw new NoSuchMethodError(
662                    "No such method "
663                        + method
664                        + " in class "
665                        + wrapper.getClass().getName());
666            }
667        }
668    
669        public static void addExceptionHandler(
670            Wrappee wrappee,
671            Wrapper wrapper,
672            String method) 
673        {
674            Collection meths =
675                ClassRepository.get().getClass(wrappee.getClass()).getAllMethods();
676            Iterator i = meths.iterator();
677            while (i.hasNext()) {
678                addExceptionHandler(
679                    wrappee,
680                    wrapper,
681                    method,
682                    (AbstractMethodItem) i.next());
683            }
684        }
685    
686        /**
687         * Invokes a role method on the wrappee. The first wrapper which
688         * defines a role method with that name is used, so in order to
689         * avoid ambiguity, it is preferable to use
690         * invokeRoleMethod(Wrappee,Class,String,Object[])
691         * 
692         * @param wrappee the wrappee (must be wrapped by a wrapper that
693         * supports the role method)
694         * @param methodName the name of the role method to invoke.
695         * @param parameters the parameters.
696         * @return the returned object
697         * 
698         * @see #invokeRoleMethod(Wrappee,Class,String,Object[]) 
699         */
700        public static Object invokeRoleMethod(
701            Wrappee wrappee,
702            String methodName,
703            Object[] parameters) 
704        {
705            Iterator wrappers = getWrappers(wrappee, null).iterator();
706            Object ret = null;
707            MethodItem method = null;
708            Object wrapper = null;
709    
710            // Seek the role method
711            while (wrappers.hasNext()) {
712                wrapper = wrappers.next();
713                try {
714                    method =
715                        (MethodItem) ClassRepository.get().getClass(
716                            wrapper).getAbstractMethods(
717                            methodName)[0];
718                } catch (org.objectweb.jac.core.rtti.NoSuchMethodException e) {
719                }
720                if (method != null)
721                    break;
722            }
723            if (method == null) {
724                logger.warn(
725                    "no such role method " + methodName
726                        + " found on " + wrappee.getClass());
727                return ret;
728            }
729    
730            try {
731    
732                // Starts a new interaction
733                //Collaboration.get().newInteraction();
734                Object[] actualParameters;
735                if (method.getParameterTypes().length > 0
736                    && method.getParameterTypes()[0] == Wrappee.class) {
737                    actualParameters =
738                        new Object[method.getParameterTypes().length];
739                    actualParameters[0] = wrappee;
740                    System.arraycopy(
741                        parameters,
742                        0,
743                        actualParameters,
744                        1,
745                        actualParameters.length - 1);
746                } else {
747                    actualParameters = parameters;
748                }
749                // invoke...
750                ret = method.invoke(wrapper, actualParameters);
751    
752            } finally {
753                //Collaboration.get().endOfInteraction();
754            }
755            return ret;
756        }
757    
758        /**
759         * Invokes a role method on the wrappee.
760         *
761         * @param wrappee the wrappee (must be wrapped by a wrapper that
762         * supports the role method).
763         * @param wrapperClass the class of the role method to invoke.
764         * @param methodName the name of the role method to invoke.
765         * @param parameters the parameters.
766         * @return the returned object
767         */
768        public static Object invokeRoleMethod(
769            Wrappee wrappee,
770            Class wrapperClass,
771            String methodName,
772            Object[] parameters) 
773        {
774            return invokeRoleMethod(
775                wrappee,
776                null,
777                wrapperClass,
778                methodName,
779                parameters);
780        }
781    
782        /**
783         * Invokes a role method on the wrappee.
784         *
785         * @param wrappee the wrappee (must be wrapped by a wrapper that
786         * supports the role method).
787         * @param wrappeeClass the class of the wrapper (for static methods)
788         * @param wrapperClass the class of the role method to invoke.
789         * @param methodName the name of the role method to invoke.
790         * @param parameters the parameters.
791         * @return the returned object
792         */
793    
794        public static Object invokeRoleMethod(
795            Wrappee wrappee,
796            ClassItem wrappeeClass,
797            Class wrapperClass,
798            String methodName,
799            Object[] parameters) 
800        {
801            loggerRole.debug("invokeRoleMethod "    + wrapperClass + "." + methodName
802                             + " on " + wrappee + "(" + wrappeeClass    + ")");
803            if (wrappee == null)
804                ACManager.getACM().whenUsingNewClass(wrappeeClass);
805    
806            Iterator wrappers = getWrappers(wrappee, wrappeeClass).iterator();
807            Object ret = null;
808            MethodItem method = null;
809            Object wrapper = null;
810    
811            loggerRole.debug("wrappers for "    + wrappee + "(" + wrappeeClass + ") : "
812                             + getWrappers(wrappee, wrappeeClass));
813    
814            // Seek the role method
815            while (wrappers.hasNext()) {
816                wrapper = wrappers.next();
817                if (wrapperClass.isAssignableFrom(wrapper.getClass())) {
818                    try {
819                        method =
820                            (MethodItem) ClassRepository.get().getClass(
821                                wrapper).getAbstractMethods(
822                                methodName)[0];
823                    } catch (org.objectweb.jac.core.rtti.NoSuchMethodException e) {
824                        logger.warn(
825                            "no such role method " + methodName
826                                + " found on " + wrappee.getClass());
827                        return ret;
828                    }
829                }
830                if (method != null)
831                    break;
832            }
833    
834            if (method == null) {
835                logger.warn(
836                    "no such role method "+ methodName + 
837                    " found on " + wrappee.getClass());
838                return ret;
839            }
840            try {
841    
842                // Starts a new interaction
843                //Collaboration.get().newInteraction();
844                Object[] actualParameters;
845                Class parameterTypes[] = method.getParameterTypes();
846                if (parameterTypes.length > 0
847                    && parameterTypes[0] == Wrappee.class) {
848                    actualParameters = new Object[parameterTypes.length];
849                    actualParameters[0] = wrappee;
850                    System.arraycopy(
851                        parameters,
852                        0,
853                        actualParameters,
854                        1,
855                        actualParameters.length - 1);
856                } else {
857                    actualParameters = parameters;
858                }
859    
860                // invoke...
861                ret = method.invoke(wrapper, actualParameters);
862    
863            } finally {
864                //Collaboration.get().endOfInteraction();
865            }
866            return ret;
867        }
868    
869        /**
870         * This method can be used to shortcut the wrapping chain and
871         * directly call the original method.
872         * 
873         * <p><b>NOTE</b>: avoid doing this unless you really know what you
874         * are doing. It is not clean to shortcut the whole wrapping
875         * chain. Instead, use the <code>invoke</code> method or, if you
876         * really need to,
877         * <code>AspectComponent.before/afterRunningWrapper()</code> to
878         * skip some wrappers.
879         *
880         * @param wrappee the object that supports the method
881         * @param name the name of the method to call
882         * @param parameters the argument values
883         * @return the called method return value as an object
884         *
885         * @see AspectComponent#beforeRunningWrapper(Wrapper,String)
886         * @see AspectComponent#afterRunningWrapper(Wrapper,String)
887         **/
888    
889        public static Object invokeOrg(
890            Wrappee wrappee,
891            String name,
892            Object[] parameters) 
893        {
894            Object ret = null;
895    
896            Method[] methods = wrappee.getClass().getMethods();
897            boolean ok = false;
898            boolean found = false;
899            String orgName =
900                "_org_"
901                    + name
902                    + "_"
903                    + NamingConventions.getShortClassName(wrappee.getClass());
904            for (int i = 0; i < methods.length; i++) {
905                if (methods[i].getParameterTypes().length == parameters.length
906                    && methods[i].getName().equals(orgName)) {
907                    found = true;
908                    try {
909                        ok = false;
910                        ret = methods[i].invoke(wrappee, parameters);
911                        ok = true;
912                    } catch (IllegalArgumentException e) {
913                    } catch (InvocationTargetException e) {
914                        Throwable t = e.getTargetException();
915                        if (t instanceof RuntimeException) {
916                            throw (RuntimeException) t;
917                        }
918                        throw new RuntimeException(
919                            "invokeOrg("
920                                + wrappee + ","
921                                + name + ","
922                                + Arrays.asList(parameters) + ") failed: " + e);
923                    } catch (IllegalAccessException e) {
924                        throw new RuntimeException(
925                            "invokeOrg("
926                                + wrappee + ","
927                                + name + ","
928                                + Arrays.asList(parameters) + ") failed: " + e);
929                    }
930                    if (ok)
931                        break;
932                }
933            }
934            // if there was no "_org_" prefixed method, try to call the method directly
935            if (!found) {
936                logger.warn(
937                    "original method "
938                        + orgName
939                        + " was not found in "
940                        + wrappee.getClass().getName());
941                for (int i = 0; i < methods.length; i++) {
942                    if (methods[i].getName().equals(name)
943                        && methods[i].getParameterTypes().length
944                            == parameters.length) {
945                        try {
946                            ok = false;
947                            ret = methods[i].invoke(wrappee, parameters);
948                            ok = true;
949                        } catch (IllegalArgumentException e) {
950                        } catch (InvocationTargetException e) {
951                            Throwable t = e.getTargetException();
952                            if (t instanceof RuntimeException) {
953                                throw (RuntimeException) t;
954                            }
955                            throw new RuntimeException(
956                                "invokeOrg("
957                                    + wrappee + ","
958                                    + name + ","
959                                    + Arrays.asList(parameters) + ") failed: " + e);
960                        } catch (IllegalAccessException e) {
961                            throw new RuntimeException(
962                                "invokeOrg("
963                                    + wrappee + ","
964                                    + name + ","
965                                    + Arrays.asList(parameters) + ") failed: " + e);
966                        }
967                        if (ok)
968                            break;
969                    }
970                }
971            }
972            if (!ok) {
973                throw new IllegalArgumentException(
974                    "No such original method has been found: "
975                        + wrappee.getClass().getName()
976                        + "."
977                        + name);
978            }
979    
980            return ret;
981        }
982    
983    
984        public static Object invokeOrg(
985            Wrappee wrappee,
986            MethodItem method,
987            Object[] parameters) 
988        {
989            try {
990                return method.getOrgMethod().invoke(wrappee, parameters);
991            } catch (InvocationTargetException e) {
992                Throwable t = e.getTargetException();
993                if (t instanceof RuntimeException) {
994                    throw (RuntimeException) t;
995                }
996                throw new RuntimeException(
997                    "invokeOrg("
998                    + wrappee + "," + method.getName() + "," + Arrays.asList(parameters)
999                    + ") failed: " + e);
1000            } catch (IllegalAccessException e) {
1001                throw new RuntimeException(
1002                    "invokeOrg("
1003                    + wrappee + "," + method.getName() + "," + Arrays.asList(parameters)
1004                    + ") failed: " + e);
1005            }
1006        }
1007    
1008        public static Object clone(Object org) {
1009            return org;
1010        }
1011    
1012        // -------
1013        // Internal JAC method used to initialize or execute wrapping chains
1014    
1015        /**
1016         * This method factorizes the common code that is used when the
1017         * next wrapper is called.
1018         *
1019         * @param interaction the method call being intercepted
1020         */
1021        public static Object nextWrapper(Interaction interaction)
1022        //      throws Throwable
1023        {
1024            Logger methodLogger = Logger.getLogger("wrapping." + interaction.method.getName());
1025            if (methodLogger.isDebugEnabled())
1026                methodLogger.debug(
1027                    "nextWrapper "
1028                    + interaction.method.getParent()+"."+interaction.method
1029                    + ", rank=" + interaction.rank
1030                    + ", isConstructor=" + (interaction.method instanceof ConstructorItem));
1031            Object ret = null;
1032            //boolean start_rank = interaction.rank == 0;
1033            Method m = null; // invoked method (wrapping method or _org_)
1034    
1035            //Collaboration collaboration = Collaboration.get();
1036            int rank = interaction.rank;
1037            Wrappee wrappee = interaction.wrappee;
1038            AbstractMethodItem method = interaction.method;
1039            String forcedName = null;
1040    
1041            try {
1042    
1043                if (rank == 0) {
1044                    //collaboration.newInteraction();
1045    
1046                    //Log.trace("jac", "done.");
1047                    // here, the attribute is persistent for optimization purpose
1048                    // we save it to restore it at the end
1049    
1050                    //oldApplicationName = collaboration.getCurApp();
1051                    if (method instanceof ConstructorItem) {
1052                        // only call whenUsingNewInstance once for each method
1053                        if (((ClassItem) method.getParent()).getActualClass()
1054                            == wrappee.getClass()) {
1055                            ObjectRepository.register(wrappee);
1056                            if (ACManager.getACM() != null) {
1057                                loggerWuni.debug("calling WUNI on " + Strings.hex(wrappee));
1058                                ACManager.getACM().whenUsingNewInstance(
1059                                    interaction);
1060                                interaction.wrappingChain =
1061                                    getWrappingChain(wrappee, method).chain;
1062                            }
1063                            // <HACK>
1064                        } else {
1065                            // We have to save the forced name if there's one,
1066                            // or it could be used for some object created by
1067                            // the super constructor (like collections's vector)
1068                            Collaboration collab = Collaboration.get();
1069                            forcedName = (String) collab.getAttribute(Naming.FORCE_NAME);
1070                            if (forcedName != null) {
1071                                collab.removeAttribute(Naming.FORCE_NAME);
1072                            }
1073                        }
1074                        // </HACK>
1075                    } else if (method.isStatic() && ACManager.getACM() != null) {
1076                        //wrappingChain=getWrappingChain(wrappee,method);
1077                        loggerWuni.debug("calling WUNI for static method "
1078                                         + method.getParent() + "." + method);
1079                        ACManager.getACM().whenUsingNewClass(
1080                            interaction.getClassItem());
1081                        interaction.wrappingChain =
1082                            getWrappingChain(wrappee, method).chain;
1083                    }
1084                }
1085    
1086                if (methodLogger.isDebugEnabled())
1087                    methodLogger.debug("wrapping chain: "
1088                                       + printWrappingChain(interaction.wrappingChain));
1089                //System.out.println("===>"+interaction.wrappingChain);
1090    
1091                if (rank < interaction.wrappingChain.length) {
1092                    Interceptor to_invoke =
1093                        interaction.wrappingChain[rank];
1094                    if (method instanceof ConstructorItem){
1095                        ret = ((ConstructorInterceptor)to_invoke).construct(interaction);
1096                    } else {
1097                        ret = ((MethodInterceptor)to_invoke).invoke(interaction);
1098                    }
1099                    //Log.trace("jac", "wrapping method returns " + ret );
1100                } else {
1101                    //Log.trace("jac", "calling org " + method );
1102                    //               collaboration.setCurAC(null);
1103                    if (method instanceof ConstructorItem || method.isStatic()) {
1104                        try {
1105                            ret =
1106                                ((ClassItem) method.getParent())
1107                                    .getActualClass()
1108                                    .getMethod(
1109                                        "_org_" + method.getName(),
1110                                        method.getParameterTypes())
1111                                    .invoke(wrappee, interaction.args);
1112                        } catch (java.lang.NoSuchMethodException e) {
1113                            throw new RuntimeException(
1114                                "Failed to invoke org method "
1115                                    + "_org_" + method.getName()
1116                                    + " on " + wrappee
1117                                    + ": " + e);
1118                        }
1119                    } else {
1120                        m = ((MethodItem) method).getOrgMethod();
1121                        if (m != null) {
1122                            ret = m.invoke(wrappee, interaction.args);
1123                        } else {
1124                            try {
1125                                m =
1126                                    ClassRepository.getDirectMethodAccess(
1127                                        wrappee.getClass(),
1128                                        "_org_" + method.getName())[0];
1129                                ret = m.invoke(wrappee, interaction.args);
1130                            } catch (IllegalArgumentException ill) {
1131                                try {
1132                                    ret =
1133                                        ClassRepository.invokeDirect(
1134                                            wrappee.getClass(),
1135                                            "_org_" + method.getName(),
1136                                            wrappee,
1137                                            interaction.args);
1138                                } catch (java.lang.NoSuchMethodException e) {
1139                                    throw new RuntimeException(
1140                                        "Failed to invoke org method "
1141                                            + "_org_" + method.getName()
1142                                            + " on " + wrappee
1143                                            + ": " + e);
1144                                }
1145                            }
1146                        }
1147                    }
1148    
1149                    //Log.trace("jac", "end of calling org " + method + " ret="+ret);
1150    
1151                }
1152    
1153            } catch (Throwable t) {
1154                if(t instanceof InvocationTargetException)
1155                    t=((InvocationTargetException)t).getTargetException();
1156                loggerExc.info(interaction.wrappee+ "."+ method
1157                          + "(" + m + ") catching " + t);
1158                WrappedThrowableException wrapped = null;
1159                if (t instanceof WrappedThrowableException) {
1160                    wrapped = (WrappedThrowableException) t;
1161                    t = ((WrappedThrowableException) t).getWrappedThrowable();
1162                }
1163                // loggerExc.info("wrapped exception is "+t);
1164                /*
1165                if (start_rank) {
1166                   collaboration.endOfInteraction();            
1167                }            
1168                */
1169    
1170                boolean caught = false;
1171    
1172                List catchingChain = getCatchingChain(wrappee, method);
1173    
1174                if (catchingChain != null) {
1175                    Iterator it = catchingChain.iterator();
1176                    loggerExc.info("trying to find catcher...");
1177                    while (it.hasNext()) {
1178                        try {
1179                            Object[] wm = (Object[]) it.next();
1180                            loggerExc.info("trying " + wm[1]
1181                                      + " on " + wm[0]
1182                                      + " (" + t + ")"
1183                                      + " ->" + t.getClass());
1184                            ((Method) wm[1]).invoke(wm[0], new Object[] { t });
1185                            loggerExc.info("caught.");
1186                            caught = true;
1187                            // several handlers on the same exception can cause trouble!!!
1188                            // e.g.: InputWrapper.inputCanceled + TransactionWrapper.rollback!
1189                            // => handlers should be correctly ordered!
1190                            break;
1191                        } catch (Exception e1) {
1192                            if ((e1 instanceof InvocationTargetException)) {
1193                                //logger.warn("oops! Exception handler generates an exception");
1194                                throw new WrappedThrowableException(
1195                                    ((InvocationTargetException) e1)
1196                                        .getTargetException());
1197                            }
1198                        }
1199                    }
1200                }
1201    
1202                if (!caught) {
1203                    loggerExc.info("forwarding exception " + t);
1204                    loggerExc.debug("forwarding exception",t);
1205                    if (wrapped != null) {
1206                        throw wrapped;
1207                    } else {
1208                        throw new WrappedThrowableException(t);
1209                    }
1210                } else {
1211                    loggerExc.info("silent exception");
1212                }
1213            }
1214    
1215            if (forcedName != null) {
1216                Collaboration.get().addAttribute(Naming.FORCE_NAME, forcedName);
1217            }
1218            return ret;
1219        }
1220    
1221        public static Object methodNextWrapper(Interaction interaction) {
1222            //Collaboration collaboration = Collaboration.get();
1223            Logger methodLogger = Logger.getLogger("wrapping." + interaction.method.getName());
1224            if (methodLogger.isDebugEnabled())
1225                methodLogger.debug("methodNextWrapper " + interaction.method.getLongName());
1226            try {
1227                if (interaction.wrappingChain.length > 0) {
1228                    return (
1229                        (MethodInterceptor) interaction.wrappingChain[0]).invoke(
1230                        interaction);
1231                } else {
1232                    if (methodLogger.isDebugEnabled())
1233                        methodLogger.debug(
1234                            "invoke org method "
1235                            + ((MethodItem) interaction.method).getOrgMethod());
1236                    return ((MethodItem) interaction.method).getOrgMethod().invoke(
1237                        interaction.wrappee,
1238                        interaction.args);
1239                }
1240            } catch (Throwable t) {
1241                if(t instanceof InvocationTargetException)
1242                    t=((InvocationTargetException)t).getTargetException();
1243                loggerExc.info("Catching " + t + " (stack trace follows)",t);
1244                WrappedThrowableException wrapped = null;
1245                if (t instanceof WrappedThrowableException) {
1246                    wrapped = (WrappedThrowableException) t;
1247                    t = ((WrappedThrowableException) t).getWrappedThrowable();
1248                }
1249                boolean caught = false;
1250    
1251                List catchingChain =
1252                    getCatchingChain(interaction.wrappee, interaction.method);
1253    
1254                if (catchingChain != null) {
1255                    Iterator it = catchingChain.iterator();
1256                    while (it.hasNext()) {
1257                        try {
1258                            Object[] wm = (Object[]) it.next();
1259                            ((Method) wm[1]).invoke(wm[0], new Object[] { t });
1260                            caught = true;
1261                            // several handlers on the same exception can cause trouble!!!
1262                            // e.g.: InputWrapper.inputCanceled + TransactionWrapper.rollback!
1263                            // => handlers should be correctly ordered!
1264                            break;
1265                        } catch (Exception e1) {
1266                            if ((e1 instanceof InvocationTargetException)) {
1267                                //logger.warn("oops! Exception handler generates an exception");
1268                                throw new WrappedThrowableException(
1269                                    ((InvocationTargetException) e1)
1270                                        .getTargetException());
1271                            }
1272                        }
1273                    }
1274                }
1275    
1276                if (!caught) {
1277                    loggerExc.info("forwarding exception " + t);
1278                    loggerExc.debug("forwarding exception",t);
1279                    if (wrapped != null) {
1280                        throw wrapped;
1281                    } else {
1282                        throw new WrappedThrowableException(t);
1283                    }
1284                }
1285    
1286                return null;
1287            }
1288    
1289        }
1290    }