View Javadoc

    1   /***************************************************************************************
    2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
    3    * http://aspectwerkz.codehaus.org                                                    *
    4    * ---------------------------------------------------------------------------------- *
    5    * The software in this package is published under the terms of the LGPL license      *
    6    * a copy of which has been included with this distribution in the license.txt file.  *
    7    **************************************************************************************/
    8   package org.codehaus.aspectwerkz.transform.inlining.deployer;
    9
    10  import java.util.Iterator;
    11  import java.util.HashSet;
    12  import java.util.Set;
    13  import java.lang.reflect.Method;
    14  import java.io.InputStream;
    15
    16  import org.codehaus.aspectwerkz.expression.ExpressionInfo;
    17  import org.codehaus.aspectwerkz.definition.AspectDefinition;
    18  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    19  import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
    20  import org.codehaus.aspectwerkz.definition.AdviceDefinition;
    21  import org.codehaus.aspectwerkz.definition.DeploymentScope;
    22  import org.codehaus.aspectwerkz.definition.XmlParser;
    23  import org.codehaus.aspectwerkz.definition.DocumentParser;
    24  import org.codehaus.aspectwerkz.joinpoint.management.AdviceInfoContainer;
    25  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointManager;
    26  import org.codehaus.aspectwerkz.annotation.AspectAnnotationParser;
    27  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
    28  import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
    29  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    30  import org.codehaus.aspectwerkz.exception.DefinitionException;
    31  import org.codehaus.aspectwerkz.transform.inlining.compiler.MatchingJoinPointInfo;
    32  import org.codehaus.aspectwerkz.transform.inlining.compiler.JoinPointFactory;
    33  import org.codehaus.aspectwerkz.transform.inlining.compiler.CompilationInfo;
    34  import org.codehaus.aspectwerkz.transform.inlining.AspectModelManager;
    35  import org.objectweb.asm.ClassReader;
    36  import org.dom4j.Document;
    37  import org.dom4j.DocumentException;
    38
    39  /***
    40   * Manages deployment and undeployment of aspects. Aspects can be deployed and undeployed into a running system(s).
    41   * <p/>
    42   * Supports annotation defined and XML defined aspects.
    43   *
    44   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    45   */
    46  public class Deployer {
    47
    48      /***
    49       * Deploys an annotation defined aspect.
    50       * <p/>
    51       * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
    52       * <p/>
    53       * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
    54       * then the aspect will not be applied to all intended points, to play safe -
    55       * use <code>deploy(final Class aspect, final DeploymentScope deploymentScope)</code>
    56       *
    57       * @param aspect the aspect class
    58       * @return a unique deployment handle for this deployment
    59       */
    60      public static DeploymentHandle deploy(final Class aspect) {
    61          return deploy(aspect, DeploymentScope.MATCH_ALL);
    62      }
    63
    64      /***
    65       * Deploys an annotation defined aspect.
    66       * <p/>
    67       * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
    68       * <p/>
    69       * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
    70       * then the aspect will not be applied to all intended points, to play safe -
    71       * use <code>deploy(final Class aspect, final DeploymentScope preparedPointcut)</code>
    72       *
    73       * @param aspectClassName the aspect class name
    74       * @return a unique deployment handle for this deployment
    75       */
    76      public static DeploymentHandle deploy(final String aspectClassName) {
    77          return deploy(aspectClassName, DeploymentScope.MATCH_ALL);
    78      }
    79
    80      /***
    81       * Deploys an annotation defined aspect.
    82       * <p/>
    83       * Deploys the aspect in all systems in the class loader that is specified.
    84       * <p/>
    85       * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
    86       * then the aspect will not be applied to all intended points, to play safe -
    87       * use <code>deploy(final Class aspect, final DeploymentScope preparedPointcut)</code>
    88       *
    89       * @param aspect       the aspect class
    90       * @param deployLoader
    91       * @return a unique deployment handle for this deployment
    92       */
    93      public static DeploymentHandle deploy(final Class aspect, final ClassLoader deployLoader) {
    94          return deploy(aspect, DeploymentScope.MATCH_ALL, deployLoader);
    95      }
    96
    97      /***
    98       * Deploys an annotation defined aspect.
    99       * <p/>
    100      * Deploys the aspect in all systems in the class loader that is specified.
    101      * <p/>
    102      * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
    103      * then the aspect will not be applied to all intended points, to play safe -
    104      * use <code>deploy(final Class aspect, final DeploymentScope preparedPointcut)</code>
    105      *
    106      * @param aspectClassName the aspect class name
    107      * @param deployLoader
    108      * @return a unique deployment handle for this deployment
    109      */
    110     public static DeploymentHandle deploy(final String aspectClassName, final ClassLoader deployLoader) {
    111         return deploy(aspectClassName, DeploymentScope.MATCH_ALL, deployLoader);
    112     }
    113
    114     /***
    115      * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
    116      * <p/>
    117      * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
    118      *
    119      * @param aspect          the aspect class
    120      * @param deploymentScope
    121      * @return a unique deployment handle for this deployment
    122      */
    123     public static DeploymentHandle deploy(final Class aspect, final DeploymentScope deploymentScope) {
    124         return deploy(aspect, deploymentScope, Thread.currentThread().getContextClassLoader());
    125     }
    126
    127     /***
    128      * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
    129      * <p/>
    130      * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
    131      *
    132      * @param aspectClassName the aspect class name
    133      * @param deploymentScope
    134      * @return a unique deployment handle for this deployment
    135      */
    136     public static DeploymentHandle deploy(final String aspectClassName, final DeploymentScope deploymentScope) {
    137         return deploy(aspectClassName, deploymentScope, Thread.currentThread().getContextClassLoader());
    138     }
    139
    140     /***
    141      * TODO allow deployment in other systems than virtual system?
    142      * <p/>
    143      * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
    144      * <p/>
    145      * Deploys the aspect in the class loader that is specified.
    146      *
    147      * @param aspect          the aspect class
    148      * @param deployLoader    the loader to deploy the aspect in
    149      * @param deploymentScope the prepared pointcut
    150      * @return a unique deployment handle for this deployment
    151      */
    152     public static DeploymentHandle deploy(final Class aspect,
    153                                           final DeploymentScope deploymentScope,
    154                                           final ClassLoader deployLoader) {
    155         if (aspect == null) {
    156             throw new IllegalArgumentException("aspect to deploy can not be null");
    157         }
    158         if (deploymentScope == null) {
    159             throw new IllegalArgumentException("prepared pointcut can not be null");
    160         }
    161         if (deployLoader == null) {
    162             throw new IllegalArgumentException("class loader to deploy aspect in can not be null");
    163         }
    164
    165         final String className = aspect.getName();
    166         return deploy(className, deploymentScope, deployLoader);
    167
    168     }
    169
    170     /***
    171      * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
    172      * <p/>
    173      * Deploys the aspect in the class loader that is specified.
    174      *
    175      * @param className
    176      * @param deploymentScope
    177      * @param deployLoader
    178      * @return
    179      */
    180     public synchronized static DeploymentHandle deploy(final String className,
    181                                                        final DeploymentScope deploymentScope,
    182                                                        final ClassLoader deployLoader) {
    183         logDeployment(className, deployLoader);
    184
    185         Class aspectClass = null;
    186         try {
    187             aspectClass = Class.forName(className, false, deployLoader);
    188         } catch (ClassNotFoundException e) {
    189             throw new RuntimeException(
    190                     "could not load class [" + className + "] in class loader [" + deployLoader + "]"
    191             );
    192         }
    193
    194         final DeploymentHandle deploymentHandle = new DeploymentHandle(aspectClass, deployLoader);
    195
    196         final ClassInfo aspectClassInfo = JavaClassInfo.getClassInfo(aspectClass);
    197
    198         // create a new aspect def and fill it up with the annotation def from the aspect class
    199         final SystemDefinition systemDef = SystemDefinitionContainer.getVirtualDefinitionAt(deployLoader);
    200         final AspectDefinition newAspectDef = new AspectDefinition(className, aspectClassInfo, systemDef);
    201         final Set newExpressions = getNewExpressionsForAspect(
    202                 aspectClass, newAspectDef, systemDef, deploymentScope, deploymentHandle
    203         );
    204
    205         redefine(newExpressions);
    206         return deploymentHandle;
    207     }
    208
    209     /***
    210      * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
    211      * <p/>
    212      * If the aspect class has annotations, those will be read but the XML definition will override the
    213      * annotation definition.
    214      * <p/>
    215      * Deploys the aspect in the class loader that has loaded the aspect.
    216      *
    217      * @param aspect the aspect class
    218      * @param xmlDef
    219      * @return
    220      */
    221     public static DeploymentHandle deploy(final Class aspect, final String xmlDef) {
    222         return deploy(aspect, xmlDef, DeploymentScope.MATCH_ALL);
    223     }
    224
    225     /***
    226      * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
    227      * <p/>
    228      * If the aspect class has annotations, those will be read but the XML definition will override the
    229      * annotation definition.
    230      * <p/>
    231      * Deploys the aspect in the class loader that has loaded the aspect.
    232      *
    233      * @param aspect          the aspect class
    234      * @param xmlDef
    235      * @param deploymentScope
    236      * @return
    237      */
    238     public static DeploymentHandle deploy(final Class aspect,
    239                                           final String xmlDef,
    240                                           final DeploymentScope deploymentScope) {
    241         return deploy(aspect, xmlDef, deploymentScope, aspect.getClassLoader());
    242     }
    243
    244     /***
    245      * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
    246      * <p/>
    247      * If the aspect class has annotations, those will be read but the XML definition will override the
    248      * annotation definition.
    249      * <p/>
    250      * Deploys the aspect in the class loader that is specified.
    251      *
    252      * @param aspect       the aspect class
    253      * @param xmlDef
    254      * @param deployLoader
    255      * @return
    256      */
    257     public static DeploymentHandle deploy(final Class aspect, final String xmlDef, final ClassLoader deployLoader) {
    258         return deploy(aspect, xmlDef, DeploymentScope.MATCH_ALL, deployLoader);
    259     }
    260
    261     /***
    262      * TODO allow deployment in other systems than virtual system?
    263      * <p/>
    264      * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
    265      * <p/>
    266      * If the aspect class has annotations, those will be read but the XML definition will override the
    267      * annotation definition.
    268      * <p/>
    269      * Deploys the aspect in the class loader that is specified.
    270      *
    271      * @param aspect          the aspect class
    272      * @param deploymentScope
    273      * @param xmlDef
    274      * @param deployLoader
    275      * @return
    276      */
    277     public synchronized static DeploymentHandle deploy(final Class aspect,
    278                                                        final String xmlDef,
    279                                                        final DeploymentScope deploymentScope,
    280                                                        final ClassLoader deployLoader) {
    281         if (aspect == null) {
    282             throw new IllegalArgumentException("aspect to deploy can not be null");
    283         }
    284         if (deploymentScope == null) {
    285             throw new IllegalArgumentException("prepared pointcut can not be null");
    286         }
    287         if (xmlDef == null) {
    288             throw new IllegalArgumentException("xml definition can not be null");
    289         }
    290         if (deployLoader == null) {
    291             throw new IllegalArgumentException("class loader to deploy aspect in can not be null");
    292         }
    293         final String className = aspect.getName();
    294         logDeployment(className, deployLoader);
    295
    296         final DeploymentHandle deploymentHandle = new DeploymentHandle(aspect, deployLoader);
    297
    298         final SystemDefinition systemDef = SystemDefinitionContainer.getVirtualDefinitionAt(deployLoader);
    299         try {
    300             final Document document = XmlParser.createDocument(xmlDef);
    301             final AspectDefinition newAspectDef = DocumentParser.parseAspectDefinition(document, systemDef, aspect);
    302             final Set newExpressions = getNewExpressionsForAspect(
    303                     aspect, newAspectDef, systemDef, deploymentScope, deploymentHandle
    304             );
    305
    306             redefine(newExpressions);
    307         } catch (DocumentException e) {
    308             throw new DefinitionException("XML definition for aspect is not well-formed: " + xmlDef);
    309         }
    310         return deploymentHandle;
    311     }
    312
    313     /***
    314      * Undeploys an aspect from the same loader that has loaded the class.
    315      *
    316      * @param aspect the aspect class
    317      */
    318     public static void undeploy(final Class aspect) {
    319         undeploy(aspect, aspect.getClassLoader());
    320     }
    321
    322     /***
    323      * Undeploys an aspect from a specific class loader.
    324      *
    325      * @param aspect the aspect class
    326      * @param loader the loader that you want to undeploy the aspect from
    327      */
    328     public static void undeploy(final Class aspect, final ClassLoader loader) {
    329         if (aspect == null) {
    330             throw new IllegalArgumentException("aspect to undeploy can not be null");
    331         }
    332         if (loader == null) {
    333             throw new IllegalArgumentException("loader to undeploy aspect from can not be null");
    334         }
    335         undeploy(aspect.getName(), loader);
    336     }
    337
    338     /***
    339      * Undeploys an aspect from a specific class loader.
    340      *
    341      * @param className the aspect class name
    342      * @param loader    the loader that you want to undeploy the aspect from
    343      */
    344     public static void undeploy(final String className, final ClassLoader loader) {
    345         logUndeployment(className, loader);
    346
    347         //TODO: this one should acquire lock or something
    348
    349         // lookup only in the given classloader scope
    350         // since the system hierarchy holds reference, they will see the change
    351         Set systemDefs = SystemDefinitionContainer.getDefinitionsAt(loader);
    352
    353         for (Iterator it = systemDefs.iterator(); it.hasNext();) {
    354             SystemDefinition systemDef = (SystemDefinition) it.next();
    355             final AspectDefinition aspectDef = systemDef.getAspectDefinition(className);
    356             if (aspectDef != null) {
    357
    358                 final Set newExpressions = new HashSet();
    359                 for (Iterator it2 = aspectDef.getAdviceDefinitions().iterator(); it2.hasNext();) {
    360                     AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
    361                     ExpressionInfo oldExpression = adviceDef.getExpressionInfo();
    362                     if (oldExpression == null) { // if null, then already undeployed
    363                         continue;
    364                     }
    365                     adviceDef.setExpressionInfo(null);
    366                     newExpressions.add(oldExpression);
    367                 }
    368                 redefine(newExpressions);
    369             }
    370         }
    371     }
    372
    373     /***
    374      * Undeploys an aspect in the same way that it has been deployed in in the previous deploy event
    375      * defined by the deployment handle.
    376      *
    377      * @param deploymentHandle the handle to the previous deployment event
    378      */
    379     public static void undeploy(final DeploymentHandle deploymentHandle) {
    380         if (deploymentHandle == null) {
    381             throw new IllegalArgumentException("deployment handle can not be null");
    382         }
    383
    384         deploymentHandle.revertChanges();
    385
    386         final Class aspectClass = deploymentHandle.getAspectClass();
    387         if (aspectClass == null) {
    388             return; // already undeployed
    389         }
    390         undeploy(aspectClass);
    391     }
    392
    393     /***
    394      * Redefines all join points that are affected by the system redefinition.
    395      *
    396      * @param expressions the expressions that will pick out the join points that are affected
    397      */
    398     private static void redefine(final Set expressions) {
    399         final Set allMatchingJoinPoints = new HashSet();
    400         for (Iterator itExpr = expressions.iterator(); itExpr.hasNext();) {
    401             ExpressionInfo expression = (ExpressionInfo) itExpr.next();
    402             Set matchingJoinPoints = JoinPointFactory.getJoinPointsMatching(expression);
    403             allMatchingJoinPoints.addAll(matchingJoinPoints);
    404         }
    405
    406         final ChangeSet changeSet = new ChangeSet();
    407         for (Iterator it = allMatchingJoinPoints.iterator(); it.hasNext();) {
    408             final MatchingJoinPointInfo joinPointInfo = (MatchingJoinPointInfo) it.next();
    409
    410             final CompilationInfo compilationInfo = joinPointInfo.getCompilationInfo();
    411             compilationInfo.incrementRedefinitionCounter();
    412
    413             changeSet.addElement(new ChangeSet.Element(compilationInfo, joinPointInfo));
    414         }
    415
    416         doRedefine(changeSet);
    417     }
    418
    419     /***
    420      * Do the redefinition of the existing join point and the compilation of the new join point.
    421      *
    422      * @param changeSet
    423      */
    424     private static void doRedefine(final ChangeSet changeSet) {
    425         for (Iterator it = changeSet.getElements().iterator(); it.hasNext();) {
    426             compileNewJoinPoint((ChangeSet.Element) it.next());
    427         }
    428         redefineInitialJoinPoints(changeSet);
    429     }
    430
    431     /***
    432      * Compiles a completely new join point instance based on the new redefined model.
    433      *
    434      * @param changeSetElement the change set item
    435      */
    436     private static void compileNewJoinPoint(final ChangeSet.Element changeSetElement) {
    437         final CompilationInfo compilationInfo = changeSetElement.getCompilationInfo();
    438         final MatchingJoinPointInfo joinPointInfo = changeSetElement.getJoinPointInfo();
    439         final ClassLoader loader = joinPointInfo.getJoinPointClass().getClassLoader();
    440         final AdviceInfoContainer newAdviceContainer = JoinPointManager.getAdviceInfoContainerForJoinPoint(
    441                 joinPointInfo.getExpressionContext(),
    442                 loader
    443         );
    444         final CompilationInfo.Model redefinedModel = new CompilationInfo.Model(
    445                 compilationInfo.getInitialModel().getEmittedJoinPoint(), // copy the reference since it is the same
    446                 newAdviceContainer,
    447                 compilationInfo.getRedefinitionCounter(),
    448                 compilationInfo.getInitialModel().getThisClassInfo()
    449         );
    450         JoinPointFactory.compileJoinPointAndAttachToClassLoader(redefinedModel, loader);
    451
    452         compilationInfo.setRedefinedModel(redefinedModel);
    453         JoinPointFactory.addCompilationInfo(joinPointInfo.getJoinPointClass(), compilationInfo);
    454     }
    455
    456     /***
    457      * Redefines the intial (weaved in) join point to delegate to the newly compiled "real" join point which is
    458      * based on the new redefined model.
    459      *
    460      * @param changeSet the change set
    461      */
    462     private static void redefineInitialJoinPoints(final ChangeSet changeSet) {
    463         // TODO type should be pluggable
    464         RedefinerFactory.newRedefiner(RedefinerFactory.Type.HOTSWAP).redefine(changeSet);
    465     }
    466
    467     /***
    468      * Returns a set with the new expressions for the advice in the aspect to deploy.
    469      *
    470      * @param aspectClass      s     * @param newAspectDef
    471      * @param systemDef
    472      * @param deploymentScope
    473      * @param deploymentHandle
    474      * @return a set with the new expressions
    475      */
    476     private static Set getNewExpressionsForAspect(final Class aspectClass,
    477                                                   final AspectDefinition newAspectDef,
    478                                                   final SystemDefinition systemDef,
    479                                                   final DeploymentScope deploymentScope,
    480                                                   final DeploymentHandle deploymentHandle) {
    481         final ClassLoader aspectLoader = aspectClass.getClassLoader();
    482         final String aspectName = aspectClass.getName();
    483
    484         final ClassInfo classInfo = AsmClassInfo.getClassInfo(aspectName, aspectLoader);
    485
    486         AspectModelManager.defineAspect(classInfo, newAspectDef, aspectLoader);
    487
    488         AspectAnnotationParser.parse(classInfo, newAspectDef, aspectLoader);
    489
    490         AspectDefinition aspectDef = systemDef.getAspectDefinition(aspectName);
    491         if (aspectDef != null) {
    492             // if in def already reuse some of the settings that can have been overridded by XML def
    493             newAspectDef.setContainerClassName(aspectDef.getContainerClassName());
    494             newAspectDef.setDeploymentModel(aspectDef.getDeploymentModel());
    495         }
    496
    497         systemDef.addAspectOverwriteIfExists(newAspectDef);
    498
    499         final Set newExpressions = new HashSet();
    500         for (Iterator it2 = newAspectDef.getAdviceDefinitions().iterator(); it2.hasNext();) {
    501             AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
    502             ExpressionInfo oldExpression = adviceDef.getExpressionInfo();
    503             if (oldExpression == null) {
    504                 continue;
    505             }
    506             deploymentHandle.registerDefinitionChange(adviceDef, oldExpression);
    507
    508             final ExpressionInfo newExpression = deploymentScope.newExpressionInfo(oldExpression);
    509             adviceDef.setExpressionInfo(newExpression);
    510             newExpressions.add(newExpression);
    511         }
    512         return newExpressions;
    513     }
    514
    515     /***
    516      * Imports a class from one class loader to another one.
    517      *
    518      * @param clazz    the class to import
    519      * @param toLoader the loader to import to
    520      */
    521     private static void importClassIntoLoader(final Class clazz, final ClassLoader toLoader) {
    522         final ClassLoader fromLoader = clazz.getClassLoader();
    523         if (toLoader == fromLoader) {
    524             return;
    525         }
    526         final String className = clazz.getName();
    527         try {
    528             Class.forName(className, false, toLoader);
    529         } catch (ClassNotFoundException cnfe) {
    530             try {
    531                 InputStream stream = null;
    532                 byte[] bytes;
    533                 try {
    534                     stream = fromLoader.getResourceAsStream(className.replace('.', '/') + ".class");
    535                     bytes = new ClassReader(stream).b;
    536                 } finally {
    537                     try {
    538                         stream.close();
    539                     } catch (Exception e) {
    540                         ;
    541                     }
    542                 }
    543                 Class klass = Class.forName("java.lang.ClassLoader", false, toLoader);
    544                 Method method = klass.getDeclaredMethod(
    545                         "defineClass",
    546                         new Class[]{String.class, byte[].class, int.class, int.class}
    547                 );
    548                 method.setAccessible(true);
    549                 Object[] args = new Object[]{
    550                     clazz.getName(), bytes, new Integer(0), new Integer(bytes.length)
    551                 };
    552                 method.invoke(toLoader, args);
    553                 method.setAccessible(false);
    554             } catch (Exception e) {
    555                 throw new RuntimeException(
    556                         new StringBuffer().append("could not deploy aspect [").
    557                         append(className).append("] in class loader [").append(toLoader)
    558                         .append(']').toString()
    559                 );
    560             }
    561         }
    562     }
    563
    564     /***
    565      * Logs undeployment.
    566      * <p/>
    567      * TODO unified way or at least format for logging
    568      *
    569      * @param className
    570      * @param loader
    571      */
    572     private static void logUndeployment(final String className, final ClassLoader loader) {
    573         System.out.println(
    574                 new StringBuffer().append("Deployer::INFO - undeploying aspect [").
    575                 append(className).append("] from class loader [").
    576                 append(loader).append(']').toString()
    577         );
    578     }
    579
    580     /***
    581      * Logs deployment.
    582      * <p/>
    583      * TODO unified way or at least format for logging
    584      *
    585      * @param className
    586      * @param loader
    587      */
    588     private static void logDeployment(final String className, final ClassLoader loader) {
    589         System.out.println(
    590                 new StringBuffer().append("Deployer::INFO - deploying aspect [").
    591                 append(className).append("] in class loader [").
    592                 append(loader).append(']').toString()
    593         );
    594     }
    595 }