001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.aspectwerkz.transform.inlining.deployer;
005:
006: import java.util.Iterator;
007: import java.util.HashSet;
008: import java.util.Set;
009:
010: import com.tc.aspectwerkz.DeploymentModel;
011: import com.tc.aspectwerkz.expression.ExpressionInfo;
012: import com.tc.aspectwerkz.annotation.AspectAnnotationParser;
013: import com.tc.aspectwerkz.definition.AdviceDefinition;
014: import com.tc.aspectwerkz.definition.AspectDefinition;
015: import com.tc.aspectwerkz.definition.DeploymentScope;
016: import com.tc.aspectwerkz.definition.DocumentParser;
017: import com.tc.aspectwerkz.definition.SystemDefinition;
018: import com.tc.aspectwerkz.definition.SystemDefinitionContainer;
019: import com.tc.aspectwerkz.joinpoint.management.AdviceInfoContainer;
020: import com.tc.aspectwerkz.joinpoint.management.JoinPointManager;
021: import com.tc.aspectwerkz.reflect.impl.asm.AsmClassInfo;
022: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
023: import com.tc.aspectwerkz.reflect.ClassInfo;
024: import com.tc.aspectwerkz.transform.inlining.AspectModelManager;
025: import com.tc.aspectwerkz.transform.inlining.compiler.CompilationInfo;
026: import com.tc.aspectwerkz.transform.inlining.compiler.CompilerHelper;
027: import com.tc.aspectwerkz.transform.inlining.compiler.MatchingJoinPointInfo;
028:
029: /**
030: * Manages deployment and undeployment of aspects. Aspects can be deployed and undeployed into a running system(s).
031: * <p/>
032: * Supports annotation defined and XML defined aspects.
033: *
034: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
035: */
036: public class Deployer {
037:
038: /**
039: * Deploys an annotation defined aspect.
040: * <p/>
041: * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
042: * <p/>
043: * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
044: * then the aspect will not be applied to all intended points, to play safe -
045: * use <code>deploy(final Class aspect, final DeploymentScope deploymentScope)</code>
046: *
047: * @param aspect the aspect class
048: * @return a unique deployment handle for this deployment
049: */
050: public static DeploymentHandle deploy(final Class aspect) {
051: return deploy(aspect, DeploymentScope.MATCH_ALL);
052: }
053:
054: /**
055: * Deploys an annotation defined aspect.
056: * <p/>
057: * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
058: * <p/>
059: * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
060: * then the aspect will not be applied to all intended points, to play safe -
061: * use <code>deploy(final Class aspect, final DeploymentScope preparedPointcut)</code>
062: *
063: * @param aspectClassName the aspect class name
064: * @return a unique deployment handle for this deployment
065: */
066: public static DeploymentHandle deploy(final String aspectClassName) {
067: return deploy(aspectClassName, DeploymentScope.MATCH_ALL);
068: }
069:
070: /**
071: * Deploys an annotation defined aspect.
072: * <p/>
073: * Deploys the aspect in all systems in the class loader that is specified.
074: * <p/>
075: * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
076: * then the aspect will not be applied to all intended points, to play safe -
077: * use <code>deploy(final Class aspect, final DeploymentScope preparedPointcut)</code>
078: *
079: * @param aspect the aspect class
080: * @param deployLoader
081: * @return a unique deployment handle for this deployment
082: */
083: public static DeploymentHandle deploy(final Class aspect,
084: final ClassLoader deployLoader) {
085: return deploy(aspect, DeploymentScope.MATCH_ALL, deployLoader);
086: }
087:
088: /**
089: * Deploys an annotation defined aspect.
090: * <p/>
091: * Deploys the aspect in all systems in the class loader that is specified.
092: * <p/>
093: * <b>CAUTION</b>: use a to own risk, the aspect might have a wider scope than your set of instrumented join points,
094: * then the aspect will not be applied to all intended points, to play safe -
095: * use <code>deploy(final Class aspect, final DeploymentScope preparedPointcut)</code>
096: *
097: * @param aspectClassName the aspect class name
098: * @param deployLoader
099: * @return a unique deployment handle for this deployment
100: */
101: public static DeploymentHandle deploy(final String aspectClassName,
102: final ClassLoader deployLoader) {
103: return deploy(aspectClassName, DeploymentScope.MATCH_ALL,
104: deployLoader);
105: }
106:
107: /**
108: * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
109: * <p/>
110: * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
111: *
112: * @param aspect the aspect class
113: * @param deploymentScope
114: * @return a unique deployment handle for this deployment
115: */
116: public static DeploymentHandle deploy(final Class aspect,
117: final DeploymentScope deploymentScope) {
118: return deploy(aspect, deploymentScope, Thread.currentThread()
119: .getContextClassLoader());
120: }
121:
122: /**
123: * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
124: * <p/>
125: * Deploys the aspect in all systems in the class loader that has loaded the aspect class.
126: *
127: * @param aspectClassName the aspect class name
128: * @param deploymentScope
129: * @return a unique deployment handle for this deployment
130: */
131: public static DeploymentHandle deploy(final String aspectClassName,
132: final DeploymentScope deploymentScope) {
133: return deploy(aspectClassName, deploymentScope, Thread
134: .currentThread().getContextClassLoader());
135: }
136:
137: /**
138: * TODO allow deployment in other systems than virtual system?
139: * <p/>
140: * Deploys an annotation defined aspect in the scope defined by the prepared pointcut.
141: * <p/>
142: * Deploys the aspect in the class loader that is specified.
143: *
144: * @param aspect the aspect class
145: * @param deployLoader the loader to deploy the aspect in
146: * @param deploymentScope the prepared pointcut
147: * @return a unique deployment handle for this deployment
148: */
149: public static DeploymentHandle deploy(final Class aspect,
150: final DeploymentScope deploymentScope,
151: final ClassLoader deployLoader) {
152: if (aspect == null) {
153: throw new IllegalArgumentException(
154: "aspect to deploy can not be null");
155: }
156: if (deploymentScope == null) {
157: throw new IllegalArgumentException(
158: "prepared pointcut can not be null");
159: }
160: if (deployLoader == null) {
161: throw new IllegalArgumentException(
162: "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(
181: final String className,
182: final DeploymentScope deploymentScope,
183: final ClassLoader deployLoader) {
184: logDeployment(className, deployLoader);
185:
186: Class aspectClass = null;
187: try {
188: aspectClass = Class.forName(className, false, deployLoader);
189: } catch (ClassNotFoundException e) {
190: throw new RuntimeException("could not load class ["
191: + className + "] in class loader [" + deployLoader
192: + "]");
193: }
194:
195: final DeploymentHandle deploymentHandle = new DeploymentHandle(
196: aspectClass, deployLoader);
197:
198: final ClassInfo aspectClassInfo = JavaClassInfo
199: .getClassInfo(aspectClass);
200:
201: // create a new aspect def and fill it up with the annotation def from the aspect class
202: final SystemDefinition systemDef = SystemDefinitionContainer
203: .getVirtualDefinitionFor(deployLoader);
204: final AspectDefinition newAspectDef = new AspectDefinition(
205: className, aspectClassInfo, systemDef);
206: final Set newExpressions = getNewExpressionsForAspect(
207: aspectClass, newAspectDef, systemDef, deploymentScope,
208: deploymentHandle);
209:
210: redefine(newExpressions);
211: return deploymentHandle;
212: }
213:
214: /**
215: * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
216: * <p/>
217: * If the aspect class has annotations, those will be read but the XML definition will override the
218: * annotation definition.
219: * <p/>
220: * Deploys the aspect in the class loader that has loaded the aspect.
221: *
222: * @param aspect the aspect class
223: * @param xmlDef
224: * @return
225: */
226: public static DeploymentHandle deploy(final Class aspect,
227: final String xmlDef) {
228: return deploy(aspect, xmlDef, DeploymentScope.MATCH_ALL);
229: }
230:
231: /**
232: * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
233: * <p/>
234: * If the aspect class has annotations, those will be read but the XML definition will override the
235: * annotation definition.
236: * <p/>
237: * Deploys the aspect in the class loader that has loaded the aspect.
238: *
239: * @param aspect the aspect class
240: * @param xmlDef
241: * @param deploymentScope
242: * @return
243: */
244: public static DeploymentHandle deploy(final Class aspect,
245: final String xmlDef, final DeploymentScope deploymentScope) {
246: return deploy(aspect, xmlDef, deploymentScope, aspect
247: .getClassLoader());
248: }
249:
250: /**
251: * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
252: * <p/>
253: * If the aspect class has annotations, those will be read but the XML definition will override the
254: * annotation definition.
255: * <p/>
256: * Deploys the aspect in the class loader that is specified.
257: *
258: * @param aspect the aspect class
259: * @param xmlDef
260: * @param deployLoader
261: * @return
262: */
263: public static DeploymentHandle deploy(final Class aspect,
264: final String xmlDef, final ClassLoader deployLoader) {
265: return deploy(aspect, xmlDef, DeploymentScope.MATCH_ALL,
266: deployLoader);
267: }
268:
269: /**
270: * TODO allow deployment in other systems than virtual system?
271: * <p/>
272: * Deploys an XML defined aspect in the scope defined by the prepared pointcut.
273: * <p/>
274: * If the aspect class has annotations, those will be read but the XML definition will override the
275: * annotation definition.
276: * <p/>
277: * Deploys the aspect in the class loader that is specified.
278: *
279: * @param aspect the aspect class
280: * @param deploymentScope
281: * @param xmlDef
282: * @param deployLoader
283: * @return
284: */
285: public synchronized static DeploymentHandle deploy(
286: final Class aspect, final String xmlDef,
287: final DeploymentScope deploymentScope,
288: final ClassLoader deployLoader) {
289: if (aspect == null) {
290: throw new IllegalArgumentException(
291: "aspect to deploy can not be null");
292: }
293: if (deploymentScope == null) {
294: throw new IllegalArgumentException(
295: "prepared pointcut can not be null");
296: }
297: if (xmlDef == null) {
298: throw new IllegalArgumentException(
299: "xml definition can not be null");
300: }
301: if (deployLoader == null) {
302: throw new IllegalArgumentException(
303: "class loader to deploy aspect in can not be null");
304: }
305: final String className = aspect.getName();
306: logDeployment(className, deployLoader);
307:
308: final DeploymentHandle deploymentHandle = new DeploymentHandle(
309: aspect, deployLoader);
310:
311: final SystemDefinition systemDef = SystemDefinitionContainer
312: .getVirtualDefinitionFor(deployLoader);
313:
314: final AspectDefinition newAspectDef = DocumentParser
315: .parseAspectDefinition(xmlDef, systemDef, aspect);
316: systemDef.addAspect(newAspectDef);
317: final Set newExpressions = getNewExpressionsForAspect(aspect,
318: newAspectDef, systemDef, deploymentScope,
319: deploymentHandle);
320: redefine(newExpressions);
321: return deploymentHandle;
322: }
323:
324: /**
325: * Undeploys an aspect from the same loader that has loaded the class.
326: *
327: * @param aspect the aspect class
328: */
329: public static void undeploy(final Class aspect) {
330: undeploy(aspect, aspect.getClassLoader());
331: }
332:
333: /**
334: * Undeploys an aspect from a specific class loader.
335: *
336: * @param aspect the aspect class
337: * @param loader the loader that you want to undeploy the aspect from
338: */
339: public static void undeploy(final Class aspect,
340: final ClassLoader loader) {
341: if (aspect == null) {
342: throw new IllegalArgumentException(
343: "aspect to undeploy can not be null");
344: }
345: if (loader == null) {
346: throw new IllegalArgumentException(
347: "loader to undeploy aspect from can not be null");
348: }
349: undeploy(aspect.getName(), loader);
350: }
351:
352: /**
353: * Undeploys an aspect from a specific class loader.
354: *
355: * @param className the aspect class name
356: * @param loader the loader that you want to undeploy the aspect from
357: */
358: public static void undeploy(final String className,
359: final ClassLoader loader) {
360: logUndeployment(className, loader);
361:
362: //TODO: this one should acquire lock or something
363:
364: // lookup only in the given classloader scope
365: // since the system hierarchy holds reference, they will see the change
366: Set systemDefs = SystemDefinitionContainer
367: .getAllDefinitionsFor(loader);
368:
369: for (Iterator it = systemDefs.iterator(); it.hasNext();) {
370: SystemDefinition systemDef = (SystemDefinition) it.next();
371: final AspectDefinition aspectDef = systemDef
372: .getAspectDefinition(className);
373: if (aspectDef != null) {
374:
375: final Set newExpressions = new HashSet();
376: for (Iterator it2 = aspectDef.getAdviceDefinitions()
377: .iterator(); it2.hasNext();) {
378: AdviceDefinition adviceDef = (AdviceDefinition) it2
379: .next();
380: ExpressionInfo oldExpression = adviceDef
381: .getExpressionInfo();
382: if (oldExpression == null) { // if null, then already undeployed
383: continue;
384: }
385: adviceDef.setExpressionInfo(null);
386: newExpressions.add(oldExpression);
387: }
388: redefine(newExpressions);
389: }
390: }
391: }
392:
393: /**
394: * Undeploys an aspect in the same way that it has been deployed in in the previous deploy event
395: * defined by the deployment handle.
396: *
397: * @param deploymentHandle the handle to the previous deployment event
398: */
399: public static void undeploy(final DeploymentHandle deploymentHandle) {
400: if (deploymentHandle == null) {
401: throw new IllegalArgumentException(
402: "deployment handle can not be null");
403: }
404:
405: deploymentHandle.revertChanges();
406:
407: final Class aspectClass = deploymentHandle.getAspectClass();
408: if (aspectClass == null) {
409: return; // already undeployed
410: }
411: undeploy(aspectClass);
412: }
413:
414: /**
415: * Redefines all join points that are affected by the system redefinition.
416: *
417: * @param expressions the expressions that will pick out the join points that are affected
418: */
419: private static void redefine(final Set expressions) {
420: final Set allMatchingJoinPoints = new HashSet();
421: for (Iterator itExpr = expressions.iterator(); itExpr.hasNext();) {
422: ExpressionInfo expression = (ExpressionInfo) itExpr.next();
423: Set matchingJoinPoints = CompilerHelper
424: .getJoinPointsMatching(expression);
425: allMatchingJoinPoints.addAll(matchingJoinPoints);
426: }
427:
428: final ChangeSet changeSet = new ChangeSet();
429: for (Iterator it = allMatchingJoinPoints.iterator(); it
430: .hasNext();) {
431: final MatchingJoinPointInfo joinPointInfo = (MatchingJoinPointInfo) it
432: .next();
433:
434: final CompilationInfo compilationInfo = joinPointInfo
435: .getCompilationInfo();
436: compilationInfo.incrementRedefinitionCounter();
437:
438: changeSet.addElement(new ChangeSet.Element(compilationInfo,
439: joinPointInfo));
440: }
441:
442: doRedefine(changeSet);
443: }
444:
445: /**
446: * Do the redefinition of the existing join point and the compilation of the new join point.
447: *
448: * @param changeSet
449: */
450: private static void doRedefine(final ChangeSet changeSet) {
451: for (Iterator it = changeSet.getElements().iterator(); it
452: .hasNext();) {
453: compileNewJoinPoint((ChangeSet.Element) it.next());
454: }
455: redefineInitialJoinPoints(changeSet);
456: }
457:
458: /**
459: * Compiles a completely new join point instance based on the new redefined model.
460: *
461: * @param changeSetElement the change set item
462: */
463: private static void compileNewJoinPoint(
464: final ChangeSet.Element changeSetElement) {
465: final CompilationInfo compilationInfo = changeSetElement
466: .getCompilationInfo();
467: final MatchingJoinPointInfo joinPointInfo = changeSetElement
468: .getJoinPointInfo();
469: final ClassLoader loader = joinPointInfo.getJoinPointClass()
470: .getClassLoader();
471: final AdviceInfoContainer newAdviceContainer = JoinPointManager
472: .getAdviceInfoContainerForJoinPoint(joinPointInfo
473: .getExpressionContext(), loader, null);
474: final CompilationInfo.Model redefinedModel = new CompilationInfo.Model(
475: compilationInfo.getInitialModel().getEmittedJoinPoint(), // copy the reference since it is the same
476: newAdviceContainer, compilationInfo
477: .getRedefinitionCounter(), compilationInfo
478: .getInitialModel().getThisClassInfo());
479: CompilerHelper.compileJoinPointAndAttachToClassLoader(
480: redefinedModel, loader);
481:
482: compilationInfo.setRedefinedModel(redefinedModel);
483: CompilerHelper.addCompilationInfo(joinPointInfo
484: .getJoinPointClass(), compilationInfo);
485: }
486:
487: /**
488: * Redefines the intial (weaved in) join point to delegate to the newly compiled "real" join point which is
489: * based on the new redefined model.
490: *
491: * @param changeSet the change set
492: */
493: private static void redefineInitialJoinPoints(
494: final ChangeSet changeSet) {
495: // TODO type should be pluggable
496: RedefinerFactory.newRedefiner(RedefinerFactory.Type.HOTSWAP)
497: .redefine(changeSet);
498: }
499:
500: /**
501: * Returns a set with the new expressions for the advice in the aspect to deploy.
502: *
503: * @param aspectClass
504: * @param newAspectDef
505: * @param systemDef
506: * @param deploymentScope
507: * @param deploymentHandle
508: * @return a set with the new expressions
509: */
510: private static Set getNewExpressionsForAspect(
511: final Class aspectClass,
512: final AspectDefinition newAspectDef,
513: final SystemDefinition systemDef,
514: final DeploymentScope deploymentScope,
515: final DeploymentHandle deploymentHandle) {
516: final ClassLoader aspectLoader = aspectClass.getClassLoader();
517: final String aspectName = aspectClass.getName();
518:
519: // keep XML settings so that they don't get changed when we read the annotations
520: String keptContainerClassName = newAspectDef
521: .getContainerClassName();
522: DeploymentModel keptModel = newAspectDef.getDeploymentModel();
523:
524: final ClassInfo classInfo = AsmClassInfo.getClassInfo(
525: aspectName, aspectLoader);
526: AspectModelManager.defineAspect(classInfo, newAspectDef,
527: aspectLoader);
528: AspectAnnotationParser.parse(classInfo, newAspectDef,
529: aspectLoader);
530:
531: AspectDefinition aspectDef = systemDef
532: .getAspectDefinition(aspectName);
533: if (aspectDef != null) {
534: // if in def already reuse some of the settings that can have been overridded by XML def
535: //AW-461
536: newAspectDef.setContainerClassName(keptContainerClassName);//aspectDef.getContainerClassName());
537: newAspectDef.setDeploymentModel(keptModel);//aspectDef.getDeploymentModel());
538: }
539:
540: systemDef.addAspectOverwriteIfExists(newAspectDef);
541:
542: final Set newExpressions = new HashSet();
543: for (Iterator it2 = newAspectDef.getAdviceDefinitions()
544: .iterator(); it2.hasNext();) {
545: AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
546: ExpressionInfo oldExpression = adviceDef
547: .getExpressionInfo();
548: if (oldExpression == null) {
549: continue;
550: }
551: deploymentHandle.registerDefinitionChange(adviceDef,
552: oldExpression);
553:
554: final ExpressionInfo newExpression = deploymentScope
555: .newExpressionInfo(oldExpression);
556: adviceDef.setExpressionInfo(newExpression);
557: newExpressions.add(newExpression);
558: }
559: return newExpressions;
560: }
561:
562: /**
563: * Imports a class from one class loader to another one.
564: *
565: * @param clazz the class to import
566: * @param toLoader the loader to import to
567: */
568: // private static void importClassIntoLoader(final Class clazz, final ClassLoader toLoader) {
569: // final ClassLoader fromLoader = clazz.getClassLoader();
570: // if (toLoader == fromLoader) {
571: // return;
572: // }
573: // final String className = clazz.getName();
574: // try {
575: // Class.forName(className, false, toLoader);
576: // } catch (ClassNotFoundException cnfe) {
577: // try {
578: // InputStream stream = null;
579: // byte[] bytes;
580: // try {
581: // stream = fromLoader.getResourceAsStream(className.replace('.', '/') + ".class");
582: // bytes = new ClassReader(stream).b;
583: // } finally {
584: // try {
585: // stream.close();
586: // } catch (Exception e) {
587: // // ignore
588: // }
589: // }
590: // Class klass = Class.forName("java.lang.ClassLoader", false, toLoader);
591: // Method method = klass.getDeclaredMethod(
592: // "defineClass",
593: // new Class[]{String.class, byte[].class, int.class, int.class}
594: // );
595: // method.setAccessible(true);
596: // Object[] args = new Object[]{
597: // clazz.getName(), bytes, new Integer(0), new Integer(bytes.length)
598: // };
599: // method.invoke(toLoader, args);
600: // method.setAccessible(false);
601: // } catch (Exception e) {
602: // throw new RuntimeException(
603: // new StringBuffer().append("could not deploy aspect [").
604: // append(className).append("] in class loader [").append(toLoader)
605: // .append(']').toString()
606: // );
607: // }
608: // }
609: // }
610: /**
611: * Logs undeployment.
612: * <p/>
613: * TODO unified way or at least format for logging
614: *
615: * @param className
616: * @param loader
617: */
618: private static void logUndeployment(final String className,
619: final ClassLoader loader) {
620: System.out.println(new StringBuffer().append(
621: "Deployer::INFO - undeploying aspect [").append(
622: className).append("] from class loader [").append(
623: loader).append(']').toString());
624: }
625:
626: /**
627: * Logs deployment.
628: * <p/>
629: * TODO unified way or at least format for logging
630: *
631: * @param className
632: * @param loader
633: */
634: private static void logDeployment(final String className,
635: final ClassLoader loader) {
636: System.out.println(new StringBuffer().append(
637: "Deployer::INFO - deploying aspect [")
638: .append(className).append("] in class loader [")
639: .append(loader).append(']').toString());
640: }
641: }
|