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.definition; 9 10 import gnu.trove.TObjectIntHashMap; 11 import org.codehaus.aspectwerkz.aspect.CFlowSystemAspect; 12 import org.codehaus.aspectwerkz.expression.ExpressionContext; 13 import org.codehaus.aspectwerkz.expression.ExpressionInfo; 14 import org.codehaus.aspectwerkz.expression.ExpressionVisitor; 15 import org.codehaus.aspectwerkz.util.SequencedHashMap; 16 import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor; 17 18 import java.util.ArrayList; 19 import java.util.Collection; 20 import java.util.HashMap; 21 import java.util.HashSet; 22 import java.util.Iterator; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Set; 26 27 /*** 28 * Abstraction of the system definition, defines the aspect system. 29 * 30 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a> 31 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a> 32 */ 33 public class SystemDefinition { 34 public static final String PER_JVM = "perJVM"; 35 36 public static final String PER_CLASS = "perClass"; 37 38 public static final String PER_INSTANCE = "perInstance"; 39 40 public static final String PER_THREAD = "perThread"; 41 42 /*** 43 * Empty hash map. 44 */ 45 public static final Map EMPTY_HASH_MAP = new HashMap(); 46 47 /*** 48 * Holds the indexes for the aspects. The aspect indexes are needed here (instead of in the AspectWerkz class like 49 * the advice indexes) since they need to be available to the transformers before the AspectWerkz system has been 50 * initialized. 51 */ 52 private final TObjectIntHashMap m_aspectIndexes = new TObjectIntHashMap(); 53 54 /*** 55 * Holds the indexes for the mixins. The mixin indexes are needed here (instead of in the AspectWerkz class like the 56 * advice indexes) since they need to be available to the transformers before the AspectWerkz system has been 57 * initialized. 58 */ 59 private final TObjectIntHashMap m_introductionIndexes = new TObjectIntHashMap(); 60 61 /*** 62 * Maps the aspects to it's name. 63 */ 64 private final Map m_aspectMap = new SequencedHashMap(); 65 66 /*** 67 * Maps the mixins to it's name. 68 */ 69 private final Map m_introductionMap = new HashMap(); 70 71 /*** 72 * Maps the interface mixins to it's name. 73 */ 74 private final Map m_interfaceIntroductionMap = new HashMap(); 75 76 /*** 77 * The UUID for this definition. 78 */ 79 private String m_uuid = "default"; 80 81 /*** 82 * The include packages. 83 */ 84 private final Set m_includePackages = new HashSet(); 85 86 /*** 87 * The exclude packages. 88 */ 89 private final Set m_excludePackages = new HashSet(); 90 91 /*** 92 * The prepare packages. 93 */ 94 private final Set m_preparePackages = new HashSet(); 95 96 /*** 97 * The parameters passed to the aspects. 98 */ 99 private final Map m_parametersToAspects = new HashMap(); 100 101 /*** 102 * Creates a new instance, creates and sets the system cflow aspect. 103 */ 104 public SystemDefinition(final String uuid) { 105 setUuid(uuid); 106 AspectDefinition systemAspect = new AspectDefinition( 107 CFlowSystemAspect.CLASS_NAME, 108 CFlowSystemAspect.CLASS_NAME, 109 m_uuid 110 ); 111 systemAspect.setDeploymentModel(CFlowSystemAspect.DEPLOYMENT_MODEL); 112 m_aspectMap.put(CFlowSystemAspect.CLASS_NAME, systemAspect); 113 } 114 115 /*** 116 * Sets the UUID for the definition. 117 * 118 * @param uuid the UUID 119 */ 120 private void setUuid(final String uuid) { 121 m_uuid = uuid; 122 } 123 124 /*** 125 * Returns the UUID for the definition. 126 * 127 * @return the UUID 128 */ 129 public String getUuid() { 130 return m_uuid; 131 } 132 133 /*** 134 * Returns the include packages. 135 * 136 * @return the include packages 137 */ 138 public Set getIncludePackages() { 139 return m_includePackages; 140 } 141 142 /*** 143 * Returns the exclude packages. 144 * 145 * @return the exclude packages 146 */ 147 public Set getExcludePackages() { 148 return m_excludePackages; 149 } 150 151 /*** 152 * Returns a collection with the aspect definitions registered. 153 * 154 * @return the aspect definitions 155 */ 156 public Collection getAspectDefinitions() { 157 Collection clone = new ArrayList(m_aspectMap.size()); 158 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) { 159 clone.add(it.next()); 160 } 161 return clone; 162 } 163 164 /*** 165 * Returns a collection with the introduction definitions registered. 166 * 167 * @return the introduction definitions 168 */ 169 public Collection getIntroductionDefinitions() { 170 Collection clone = new ArrayList(m_introductionMap.size()); 171 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) { 172 clone.add(it.next()); 173 } 174 return clone; 175 } 176 177 /*** 178 * Returns a collection with the advice definitions registered. 179 * 180 * @return the advice definitions 181 */ 182 public Collection getAdviceDefinitions() { 183 final Collection adviceDefs = new ArrayList(); 184 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) { 185 AspectDefinition aspectDef = (AspectDefinition) it.next(); 186 adviceDefs.addAll(aspectDef.getAroundAdvices()); 187 adviceDefs.addAll(aspectDef.getBeforeAdvices()); 188 adviceDefs.addAll(aspectDef.getAfterAdvices()); 189 } 190 return adviceDefs; 191 } 192 193 /*** 194 * Returns a specific aspect definition. 195 * 196 * @param name the name of the aspect definition 197 * @return the aspect definition 198 */ 199 public AspectDefinition getAspectDefinition(final String name) { 200 return (AspectDefinition) m_aspectMap.get(name); 201 } 202 203 /*** 204 * Returns a specific advice definition. 205 * 206 * @param name the name of the advice definition 207 * @return the advice definition 208 */ 209 public AdviceDefinition getAdviceDefinition(final String name) { 210 Collection adviceDefs = getAdviceDefinitions(); 211 for (Iterator it = adviceDefs.iterator(); it.hasNext();) { 212 AdviceDefinition adviceDef = (AdviceDefinition) it.next(); 213 if (adviceDef.getName().equals(name)) { 214 return adviceDef; 215 } 216 } 217 return null; 218 } 219 220 /*** 221 * Returns the introduction definitions for a specific class. 222 * 223 * @param ctx the expression context 224 * @return a list with the introduction definitions 225 */ 226 public List getIntroductionDefinitions(final ExpressionContext ctx) { 227 final List introDefs = new ArrayList(); 228 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) { 229 IntroductionDefinition introDef = (IntroductionDefinition) it.next(); 230 for (int i = 0; i < introDef.getExpressionInfos().length; i++) { 231 if (introDef.getExpressionInfos()[i].getExpression().match(ctx)) { 232 introDefs.add(introDef); 233 } 234 } 235 } 236 return introDefs; 237 } 238 239 /*** 240 * Returns the interface introductions for a certain class merged with the implementation based introductions as 241 * well. 242 * 243 * @param ctx the expression context 244 * @return the names 245 */ 246 public List getInterfaceIntroductionDefinitions(final ExpressionContext ctx) { 247 if (ctx == null) { 248 throw new IllegalArgumentException("context can not be null"); 249 } 250 List interfaceIntroductionDefs = new ArrayList(); 251 for (Iterator it = m_interfaceIntroductionMap.values().iterator(); it.hasNext();) { 252 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next(); 253 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos(); 254 for (int i = 0; i < expressionInfos.length; i++) { 255 ExpressionInfo expressionInfo = expressionInfos[i]; 256 ExpressionVisitor expression = expressionInfo.getExpression(); 257 if (expression.match(ctx)) { 258 interfaceIntroductionDefs.add(introDef); 259 } 260 } 261 } 262 return interfaceIntroductionDefs; 263 } 264 265 /*** 266 * Returns the index for a specific introduction. 267 * 268 * @param aspectName the name of the aspect 269 * @return the index 270 */ 271 public int getAspectIndexByName(final String aspectName) { 272 if (aspectName == null) { 273 throw new IllegalArgumentException("aspect name can not be null"); 274 } 275 int index = m_aspectIndexes.get(aspectName); 276 if (index < 1) { 277 throw new RuntimeException( 278 "aspect [" + aspectName + "] does not exist, failed in retrieving aspect index" 279 ); 280 } 281 return index; 282 } 283 284 /*** 285 * Returns the index for a specific introduction. 286 * 287 * @param mixinName the name of the mixin 288 * @return the index 289 */ 290 public int getMixinIndexByName(final String mixinName) { 291 if (mixinName == null) { 292 throw new IllegalArgumentException("mixin name can not be null"); 293 } 294 int index = m_introductionIndexes.get(mixinName); 295 if (index < 1) { 296 throw new RuntimeException("mixin [" + mixinName + "] does not exist, failed in retrieving mixin index"); 297 } 298 return index; 299 } 300 301 /*** 302 * Adds a new aspect definition. 303 * 304 * @param aspectDef the aspect definition 305 */ 306 public void addAspect(final AspectDefinition aspectDef) { 307 if (aspectDef == null) { 308 throw new IllegalArgumentException("aspect definition can not be null"); 309 } 310 if (m_aspectIndexes.containsKey(aspectDef.getName())) { 311 return; 312 } 313 synchronized (m_aspectMap) { 314 synchronized (m_aspectIndexes) { 315 final int index = m_aspectMap.values().size() + 1; 316 m_aspectIndexes.put(aspectDef.getName(), index); 317 m_aspectMap.put(aspectDef.getName(), aspectDef); 318 } 319 } 320 } 321 322 /*** 323 * Adds a new mixin definition. 324 * 325 * @param introDef the mixin definition 326 */ 327 public void addIntroductionDefinition(final IntroductionDefinition introDef) { 328 if (introDef == null) { 329 throw new IllegalArgumentException("introduction definition can not be null"); 330 } 331 if (m_introductionIndexes.containsKey(introDef.getName())) { 332 IntroductionDefinition def = (IntroductionDefinition) m_introductionMap.get(introDef.getName()); 333 def.addExpressionInfos(introDef.getExpressionInfos()); 334 return; 335 } 336 synchronized (m_introductionMap) { 337 synchronized (m_introductionIndexes) { 338 final int index = m_introductionMap.values().size() + 1; 339 m_introductionIndexes.put(introDef.getName(), index); 340 m_introductionMap.put(introDef.getName(), introDef); 341 } 342 } 343 } 344 345 /*** 346 * Adds a new pure interface mixin definition. 347 * 348 * @param introDef the mixin definition 349 */ 350 public void addInterfaceIntroductionDefinition(final InterfaceIntroductionDefinition introDef) { 351 if (introDef == null) { 352 throw new IllegalArgumentException("introduction definition can not be null"); 353 } 354 synchronized (m_interfaceIntroductionMap) { 355 m_interfaceIntroductionMap.put(introDef.getName(), introDef); 356 } 357 } 358 359 /*** 360 * Adds a new include package. 361 * 362 * @param includePackage the new include package 363 */ 364 public void addIncludePackage(final String includePackage) { 365 synchronized (m_includePackages) { 366 m_includePackages.add(includePackage + '.'); 367 } 368 } 369 370 /*** 371 * Adds a new exclude package. 372 * 373 * @param excludePackage the new exclude package 374 */ 375 public void addExcludePackage(final String excludePackage) { 376 synchronized (m_excludePackages) { 377 m_excludePackages.add(excludePackage + '.'); 378 } 379 } 380 381 /*** 382 * Adds a new prepare package. 383 * 384 * @param preparePackage the new prepare package 385 */ 386 public void addPreparePackage(final String preparePackage) { 387 synchronized (m_preparePackages) { 388 m_preparePackages.add(preparePackage + '.'); 389 } 390 } 391 392 /*** 393 * Returns the prepare packages. 394 * 395 * @return 396 */ 397 public Set getPreparePackages() { 398 return m_preparePackages; 399 } 400 401 /*** 402 * Checks if there exists an advice with the name specified. 403 * 404 * @param name the name of the advice 405 * @return boolean 406 */ 407 public boolean hasAdvice(final String name) { 408 Collection adviceDefs = getAdviceDefinitions(); 409 for (Iterator it = adviceDefs.iterator(); it.hasNext();) { 410 AdviceDefinition adviceDef = (AdviceDefinition) it.next(); 411 if (adviceDef.getName().equals(name)) { 412 return true; 413 } 414 } 415 return false; 416 } 417 418 /*** 419 * Checks if there exists an introduction with the name specified. 420 * 421 * @param name the name of the introduction 422 * @return boolean 423 */ 424 public boolean hasIntroduction(final String name) { 425 return m_introductionMap.containsKey(name); 426 } 427 428 /*** 429 * Checks if a class should be included. 430 * 431 * @param className the name or the class 432 * @return boolean 433 */ 434 public boolean inIncludePackage(final String className) { 435 if (className == null) { 436 throw new IllegalArgumentException("class name can not be null"); 437 } 438 if (m_includePackages.isEmpty()) { 439 return true; 440 } 441 for (Iterator it = m_includePackages.iterator(); it.hasNext();) { 442 String packageName = (String) it.next(); 443 >if (className.startsWith(packageName)) { 444 return true; 445 } 446 } 447 return false; 448 } 449 450 /*** 451 * Checks if a class should be excluded. 452 * 453 * @param className the name or the class 454 * @return boolean 455 */ 456 public boolean inExcludePackage(final String className) { 457 if (className == null) { 458 throw new IllegalArgumentException("class name can not be null"); 459 } 460 for (Iterator it = m_excludePackages.iterator(); it.hasNext();) { 461 String packageName = (String) it.next(); 462 >if (className.startsWith(packageName)) { 463 return true; 464 } 465 } 466 return false; 467 } 468 469 /*** 470 * Checks if a class is in prepare declaration 471 * 472 * @param className the name or the class 473 * @return boolean 474 */ 475 public boolean inPreparePackage(String className) { 476 if (className == null) { 477 throw new IllegalArgumentException("class name can not be null"); 478 } 479 for (Iterator it = m_preparePackages.iterator(); it.hasNext();) { 480 String packageName = (String) it.next(); 481 >if (className.startsWith(packageName)) { 482 return true; 483 } 484 } 485 return false; 486 } 487 488 /*** 489 * Checks if a class has an introduction. 490 * 491 * @param ctx the expression context 492 * @return boolean 493 */ 494 public boolean hasIntroduction(final ExpressionContext ctx) { 495 if (ctx == null) { 496 throw new IllegalArgumentException("context can not be null"); 497 } 498 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) { 499 IntroductionDefinition introDef = (IntroductionDefinition) it.next(); 500 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos(); 501 for (int i = 0; i < expressionInfos.length; i++) { 502 ExpressionInfo expressionInfo = expressionInfos[i]; 503 ExpressionVisitor expression = expressionInfo.getExpression(); 504 if (expression.match(ctx)) { 505 return true; 506 } 507 } 508 } 509 return false; 510 } 511 512 /*** 513 * Checks if a method has an pointcut. 514 * 515 * @param ctx the expression context 516 * @return boolean 517 */ 518 public boolean hasPointcut(final ExpressionContext ctx) { 519 if (ctx == null) { 520 throw new IllegalArgumentException("context can not be null"); 521 } 522 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) { 523 AspectDefinition aspectDef = (AspectDefinition) it.next(); 524 for (Iterator it2 = aspectDef.getAllAdvices().iterator(); it2.hasNext();) { 525 AdviceDefinition adviceDef = (AdviceDefinition) it2.next(); 526 ExpressionVisitor expression = adviceDef.getExpressionInfo().getExpression(); 527 528 if (expression.match(ctx)) { 529 if (AspectWerkzPreProcessor.DETAILS) { 530 System.out.println( 531 "match: " + expression.toString() + " @ " + aspectDef.getName() + "/" + 532 adviceDef.getName() 533 ); 534 System.out.println("\tfor " + ctx.getReflectionInfo()); 535 System.out.println("\twithin " + ctx.getWithinReflectionInfo()); 536 System.out.println("\ttype " + ctx.getPointcutType().toString()); 537 } 538 return true; 539 } 540 } 541 } 542 return false; 543 } 544 545 /*** 546 * Checks if a method has an cflow pointcut. 547 * 548 * @param ctx the expression context 549 * @return boolean 550 */ 551 public boolean hasCflowPointcut(final ExpressionContext ctx) { 552 if (ctx == null) { 553 throw new IllegalArgumentException("context can not be null"); 554 } 555 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) { 556 AspectDefinition aspectDef = (AspectDefinition) it.next(); 557 for (Iterator it2 = aspectDef.getAllAdvices().iterator(); it2.hasNext();) { 558 AdviceDefinition adviceDef = (AdviceDefinition) it2.next(); 559 ExpressionInfo expressionInfo = adviceDef.getExpressionInfo(); 560 if (expressionInfo.hasCflowPointcut() && expressionInfo.getCflowExpression().match(ctx)) { 561 return true; 562 } 563 } 564 } 565 return false; 566 } 567 568 /*** 569 * Checks if a class is advised. 570 * 571 * @param ctxs an array with the expression contexts 572 * @return boolean 573 */ 574 public boolean isAdvised(final ExpressionContext[] ctxs) { 575 if (ctxs == null) { 576 throw new IllegalArgumentException("context array can not be null"); 577 } 578 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) { 579 AspectDefinition aspectDef = (AspectDefinition) it.next(); 580 List advices = aspectDef.getAllAdvices(); 581 for (Iterator it2 = advices.iterator(); it2.hasNext();) { 582 AdviceDefinition adviceDef = (AdviceDefinition) it2.next(); 583 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo(); 584 for (int i = 0; i < ctxs.length; i++) { 585 ExpressionContext ctx = ctxs[i]; 586 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx) 587 || expressionInfo.getAdvisedCflowClassFilterExpression().match(ctx)) { 588 if (AspectWerkzPreProcessor.DETAILS) { 589 System.out.println( 590 "early match: " + expressionInfo.toString() + " @ " + 591 aspectDef.getName() + 592 "/" + 593 adviceDef.getName() 594 ); 595 System.out.println("\tfor " + ctx.getReflectionInfo()); 596 System.out.println("\twithin " + ctx.getWithinReflectionInfo()); 597 System.out.println("\ttype " + ctx.getPointcutType().toString()); 598 } 599 return true; 600 } 601 } 602 } 603 } 604 return false; 605 } 606 607 /*** 608 * Checks if a class is advised. 609 * 610 * @param ctx the expression context 611 * @return boolean 612 */ 613 public boolean isAdvised(final ExpressionContext ctx) { 614 if (ctx == null) { 615 throw new IllegalArgumentException("context can not be null"); 616 } 617 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) { 618 AspectDefinition aspectDef = (AspectDefinition) it.next(); 619 List advices = aspectDef.getAllAdvices(); 620 for (Iterator it2 = advices.iterator(); it2.hasNext();) { 621 AdviceDefinition adviceDef = (AdviceDefinition) it2.next(); 622 if (adviceDef.getExpressionInfo().getAdvisedClassFilterExpression().match(ctx) 623 || adviceDef.getExpressionInfo().getAdvisedCflowClassFilterExpression().match(ctx)) { 624 return true; 625 } 626 } 627 } 628 return false; 629 } 630 631 /*** 632 * Checks if a class has an introduction. 633 * 634 * @param ctxs an array with the expression contexts 635 * @return boolean 636 */ 637 public boolean isIntroduced(final ExpressionContext[] ctxs) { 638 if (ctxs == null) { 639 throw new IllegalArgumentException("context array can not be null"); 640 } 641 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) { 642 IntroductionDefinition introDef = (IntroductionDefinition) it.next(); 643 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos(); 644 for (int i = 0; i < expressionInfos.length; i++) { 645 ExpressionInfo expressionInfo = expressionInfos[i]; 646 for (int j = 0; j < ctxs.length; j++) { 647 if (expressionInfo.getAdvisedClassFilterExpression().match(ctxs[j])) { 648 return true; 649 } 650 } 651 } 652 } 653 return false; 654 } 655 656 /*** 657 * Checks if a class has an introduction. 658 * 659 * @param ctx the expression context 660 * @return boolean 661 */ 662 public boolean isIntroduced(final ExpressionContext ctx) { 663 if (ctx == null) { 664 throw new IllegalArgumentException("context can not be null"); 665 } 666 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) { 667 IntroductionDefinition introDef = (IntroductionDefinition) it.next(); 668 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos(); 669 for (int i = 0; i < expressionInfos.length; i++) { 670 ExpressionInfo expressionInfo = expressionInfos[i]; 671 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) { 672 return true; 673 } 674 } 675 } 676 return false; 677 } 678 679 /*** 680 * Checks if a class is advised with an interface introduction. 681 * 682 * @param ctx the expression context 683 * @return boolean 684 */ 685 public boolean isInterfaceIntroduced(final ExpressionContext ctx) { 686 if (ctx == null) { 687 throw new IllegalArgumentException("context can not be null"); 688 } 689 for (Iterator it = m_interfaceIntroductionMap.values().iterator(); it.hasNext();) { 690 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next(); 691 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos(); 692 for (int i = 0; i < expressionInfos.length; i++) { 693 ExpressionInfo expressionInfo = expressionInfos[i]; 694 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) { 695 return true; 696 } 697 } 698 } 699 return false; 700 } 701 702 /*** 703 * Adds a new parameter for the aspect. 704 * 705 * @param aspectName the name of the aspect 706 * @param key the key 707 * @param value the value 708 * @TODO: should perhaps move to the aspect def instead of being separated from the aspect def concept? 709 */ 710 public void addParameter(final String aspectName, final String key, final String value) { 711 Map parameters; 712 if (m_parametersToAspects.containsKey(aspectName)) { 713 parameters = (Map) m_parametersToAspects.get(aspectName); 714 parameters.put(key, value); 715 } else { 716 parameters = new HashMap(); 717 parameters.put(key, value); 718 m_parametersToAspects.put(aspectName, parameters); 719 } 720 } 721 722 /*** 723 * Returns parameters for the aspect. 724 * 725 * @param aspectName the name of the aspect 726 * @return parameters 727 */ 728 public Map getParameters(final String aspectName) { 729 if (m_parametersToAspects.containsKey(aspectName)) { 730 return (Map) m_parametersToAspects.get(aspectName); 731 } else { 732 return EMPTY_HASH_MAP; 733 } 734 } 735 }