001 /* 002 Copyright (C) 2001-2004 Renaud Pawlak <renaud@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 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.io.FileNotFoundException; 022 import java.io.Serializable; 023 import java.lang.reflect.InvocationTargetException; 024 import java.lang.reflect.Method; 025 import java.util.Arrays; 026 import java.util.Collection; 027 import java.util.HashSet; 028 import java.util.Iterator; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.Set; 032 import java.util.Vector; 033 import org.apache.log4j.Logger; 034 import org.objectweb.jac.core.rtti.*; 035 import org.objectweb.jac.util.WeakHashSet; 036 037 /** 038 * This class is the root class for the aspect components defined by a 039 * Jac application programmers. 040 * 041 * <p>When programming a new aspect, the programmer must extends this 042 * class and define new pointcuts to link a set of base method 043 * invocation to a wrapping method. If necessary, the programmer can 044 * also redefine the methods of the interface 045 * <code>BaseProgramListener</code> to be able to react on several 046 * events happenning in the base programm such as object 047 * (de)serialization, object instantiation, object naming, object 048 * cloning, or wrappers execution. 049 * 050 * @author <a href="mailto:pawlak@cnam.fr">Renaud Pawlak</a> 051 * 052 * @see Pointcut */ 053 054 public class AspectComponent 055 implements Serializable, BaseProgramListener 056 { 057 static Logger logger = Logger.getLogger("aspects"); 058 static Logger loggerConf = Logger.getLogger("aspects.config"); 059 static Logger loggerWrap = Logger.getLogger("wrappers"); 060 static Logger loggerWuni = Logger.getLogger("wuni"); 061 static Logger loggerPerf = Logger.getLogger("perf"); 062 063 protected static final boolean SHARED = false; 064 protected static final boolean NOT_SHARED = true; 065 066 protected static final ClassRepository cr = ClassRepository.get(); 067 068 /** 069 * Returns true if the method is defined in the aspect component 070 * class. 071 * 072 * @param methodName the method to test 073 * @return true if aspect component method */ 074 075 public static boolean defines(String methodName) { 076 if (ClassRepository.getDirectMethodAccess(AspectComponent.class, 077 methodName )[0] != null) 078 return true; 079 return false; 080 } 081 082 /** 083 * A common configuration method that defines a timer and its 084 * callback. 085 * 086 * <p>The method <code>mi</code> is invoked every 087 * <code>period</code> ms. The method is defined in the aspect 088 * component. 089 * 090 * @param period the timer's period (in ms) 091 * @param callback the method that is called every period 092 * @param args the arguments the callback is called with */ 093 094 public void defineTimer(final long period, final MethodItem callback, 095 final Object[] args) { 096 final AspectComponent ac=this; 097 new Thread() { 098 public void run() { 099 while(true) { 100 try { 101 sleep(period); 102 } catch(Exception e) { 103 e.printStackTrace(); 104 } 105 callback.invoke(ac,args); 106 } 107 } 108 }.start(); 109 } 110 111 /** 112 * Tell if the aspect component is already woven to the base 113 * program.<br> 114 * 115 * @see #weave() */ 116 117 // public boolean woven = false; 118 119 /** 120 * Memorize how many calls have been performed on 121 * <code>start_weaving_type.start_weaving_method</code>. The 122 * weaving will start when this number reaches 123 * <code>start_weaving_count</code>.<br> 124 * 125 * @see #weave() */ 126 127 public int firstCall = -1; 128 129 /** 130 * The type where the weaving has to start. 131 * 132 * @see #weave() */ 133 134 public Class startWeavingType; 135 136 /** 137 * The method where the weaving has to start.<br> 138 * 139 * @see #weave() 140 */ 141 public String startWeavingMethod; 142 143 /** 144 * The number of calls of 145 * <code>startWeavingType.startWeavingMethod</code> where the 146 * weaving has to start.<br> 147 * 148 * @see #weave() 149 */ 150 public int startWeavingCount; 151 152 /** 153 * The number of instance of <code>startWeavingType</code> where 154 * the weaving has to start.<br> 155 * 156 * @see #weave() 157 */ 158 public int startWeavingCCount; 159 160 /** 161 * The wrappers handled by this aspect component.<br> 162 * 163 * @see #getWrappers() 164 * @see #addWrapper(Wrapper) 165 */ 166 protected transient WeakHashSet wrappers; 167 168 /** 169 * Tells if this aspect component is a system listener one so that 170 * it can receive events from the org.objectweb.jac.core objects. 171 */ 172 protected boolean systemListener = false; 173 174 /** 175 * Sets this aspect component to be a system or non-system 176 * listener. 177 * 178 * @param systemListener if true, the component can receive events 179 * from the JAC core objects (e.g. whenUsingNewInstance) ; if 180 * false, these events are filtered 181 */ 182 public void setSystemListener(boolean systemListener) { 183 this.systemListener = systemListener; 184 } 185 186 /** 187 * Tells if this aspect component is a system listener. 188 * 189 * @return true if system listener 190 * @see #setSystemListener(boolean) 191 */ 192 public boolean isSystemListener() { 193 return systemListener; 194 } 195 196 /** 197 * The default constructor of the aspect component. By default, 198 * there is no start weaving type or method so that the 199 * <code>weave()</code> is called as the aspect component is 200 * registering to the aspect component manager<br>. 201 * 202 * @see #weave() 203 * @see ACManager#register(String,Object) 204 */ 205 public AspectComponent() { 206 logger.debug("New AC: "+this); 207 wrappers = new WeakHashSet(); 208 firstCall = -1; 209 startWeavingType = null; 210 startWeavingMethod = null; 211 startWeavingCount = 0; 212 startWeavingCCount = 0; 213 init(); 214 // Collaboration.get().setCurAC(ACManager.get().getName(this)); 215 } 216 217 /** 218 * Initializes the aspect component so that its state is resetted 219 * to the default state.<br> 220 */ 221 public void init() { 222 //woven = false; 223 loggerWrap.debug("clearing wrappers (was "+wrappers+")"); 224 wrappers.clear(); 225 } 226 227 protected String application=null; 228 229 /** 230 * Gets the application that declares this aspect component. 231 * 232 * @return the application's name 233 * @see Application 234 * @see ApplicationRepository 235 */ 236 public String getApplication() { 237 return application; 238 } 239 240 /** 241 * Gets the application that declares this aspect component. 242 * 243 * <p>This method is invoked by the system and should not be 244 * directly used. 245 * 246 * @param application the application's name 247 * @see Application 248 * @see ApplicationRepository */ 249 250 public void setApplication(String application) { 251 this.application = application; 252 } 253 254 /** 255 * Returns the wrappers handled by this aspect component. When you 256 * have a reference on a wrapper, you can also know which aspect 257 * component is currently handling it by using 258 * <code>Wrapper.getAspectComponent()</code>.<br> 259 * 260 * @return a vector containing the handdled wrappers 261 * @see #addWrapper(Wrapper) 262 * @see Wrapper#getAspectComponent() */ 263 264 public Collection getWrappers() { 265 return wrappers; 266 } 267 268 /** 269 * Add a wrapper to the wrappers handled by this aspect 270 * component. 271 * 272 * <p>The programmer should not call this method explicitly unless 273 * s/he really hnows what s/he is doing. In fact, this method is 274 * automatically upcalled when the aspect component wraps a base 275 * program object (see <code>Wrappee.wrap()</code>).<br> 276 * 277 * @param wrapper the wrapper to add to the handdled wrappers 278 * @see #getWrappers() 279 * @see Wrapping#wrap(Wrappee,Wrapper,AbstractMethodItem) 280 */ 281 282 public void addWrapper(Wrapper wrapper) { 283 wrappers.add(wrapper); 284 } 285 286 /** 287 * The programmer should define this method to wrap all the 288 * currently instantiated Jac objects so that it plugs a given 289 * aspect. Since the objects might allready be wrapped by other 290 * weavers, the programmer should be careful that it does not wrap 291 * the same object with the same wrapper several times. This can 292 * be done in the weave function by using the 293 * <code>Wrappee.isExtendedBy()</code> method. When a new AC is 294 * registered the weave method is automatically called. In the 295 * jac.prop file, the time when it is called can be parametrized 296 * with the org.objectweb.jac.startWeavingPlaces property. 297 * 298 * <p>This method is only called once. For the objects that are 299 * created after the weaving, the programmer must define the 300 * <code>whenUsingNewInstance</code> method to wrap them. 301 * 302 * <p>The default implementation of the weave method is the 303 * following. In most of the case, it will work if the 304 * <code>whenUsingNewInstance()</code> is correctly defined (thus, 305 * the programer do not have to redefine the weave method). 306 * 307 * <ul><pre> 308 * for (int i=0; i < JacObject.objectCount(); i++) { 309 * simulateUsingNewInstance ( (Wrappee)JacObject.getObject(i) ); 310 * } 311 * </pre></ul> 312 * 313 * <b>IMPORTANT NOTE</b>: this method is not deprecated but the 314 * programmer should not overload it since it is much cleaner to 315 * define the <code>whenUsingNewInstance()</code> to implement the 316 * weaving.<br> 317 * 318 * @see #whenUsingNewInstance(Interaction) 319 * @see #simulateUsingNewInstance(Wrappee) 320 * @see Wrapping#isExtendedBy(Wrappee,ClassItem,Class) 321 */ 322 323 public void weave() { 324 Iterator it = ObjectRepository.getMemoryObjects().iterator(); 325 while (it.hasNext()) { 326 simulateUsingNewInstance((Wrappee)it.next()); 327 } 328 } 329 330 /** 331 * This method is called when a new instance (that has not been 332 * wrapped by the aspect component yet) is used by a peer 333 * object. 334 * 335 * <p>By default, this method check out all the pointcuts that have 336 * been defined within this aspect component and accordingly wraps 337 * the wrappee. 338 * 339 * <p>However, for performance or flexibility reasons, the 340 * programmer can also define it from scratch so that the aspects 341 * can dynamically apply while the base program creates new base 342 * objects. The aspect component is notified only once for each new 343 * instance. 344 * 345 * <p>Here is a typical implementation of this method for a 346 * many-to-one wrappees-wrapper relationship: 347 * 348 * <ul><pre> 349 * public class MyAC extends AspectComponent { 350 * public MyWrapper myWrapper = null; 351 * public void whenUsingNewInstance() { 352 * // creates the sole instance of MyWrapper 353 * if ( myWrapper == null ) myWrapper = new MyWrapper(); 354 * // make sure we do not wrap an object several times 355 * // (this is usually not useful!!) 356 * if ( wrappee().isExtendedBy( MyWrapper.class ) ) return; 357 * // Do not wrap system or aspect objects 358 * if ( wrappee().getClass().getName().startsWith( "org.objectweb.jac.core." ) || 359 * wrappee().getClass().getName().startsWith( "org.objectweb.jac.aspects." ) ) 360 * return; 361 * // wrap it... 362 * wrappee().wrapAll( myWrapper, "myWrappingMethod" ); 363 * } 364 * } 365 * </pre></ul> 366 * 367 * <p>The same but with one-to-one wrappees-wrappers 368 * relationship: 369 * 370 * <ul><pre> 371 * public class MyAC extends AspectComponent { 372 * public void whenUsingNewInstance() { 373 * // make sure we do not wrap an object several times 374 * // (this is usually not useful!!) 375 * if ( wrappee().isExtendedBy( MyWrapper.class ) ) return; 376 * // Do not wrap system or aspect objects 377 * if ( wrappee().getClass().getName().startsWith( "org.objectweb.jac.core." ) || 378 * wrappee().getClass().getName().startsWith( "org.objectweb.jac.aspects." ) ) 379 * return; 380 * // one wrapper for each new instance 381 * MyWrapper myWrapper = new MyWrapper(); 382 * // wrap it... 383 * wrappee().wrapAll( myWrapper, "myWrappingMethod" ); 384 * } 385 * } 386 * </pre></ul> 387 * 388 * <b>NOTE</b>: this method is not upcalled until the aspect component 389 * is woven.<br> 390 * 391 * @see #weave() 392 */ 393 public void whenUsingNewInstance(Interaction interaction) { 394 //if(treatedInstances.contains(wrappee())) return; 395 //treatedInstances.add(wrappee()); 396 ClassItem cli = interaction.getClassItem(); 397 //Log.trace("wuni",this+": "+method()); 398 AbstractMethodItem method = interaction.method; 399 400 loggerWuni.debug("whenUsingNewInstance("+interaction+"); pointcuts: "+pointcuts.size()); 401 int i = 0; 402 Iterator it = pointcuts.iterator(); 403 while (it.hasNext()) { 404 loggerWuni.debug(" pointcut "+i++); 405 ((Pointcut)it.next()).applyTo(interaction.wrappee, cli); 406 } 407 } 408 409 public void whenUsingNewClass(ClassItem cli) { 410 Iterator it = pointcuts.iterator(); 411 int i = 0; 412 loggerWuni.debug(this+".whenUsingNewClass"+cli); 413 while (it.hasNext()) { 414 ((Pointcut)it.next()).applyTo(null, cli); 415 } 416 } 417 418 /** 419 * This method simulates an instance first use so that the system 420 * upcalls <code>whenUsingNewInstance()</code> with a fake 421 * collaboration point (<code>method()</code> is null and 422 * <code>args</code> is null too). 423 * 424 * @param wrappee the wrappe on which the call is simulated. 425 * @see #whenUsingNewInstance(Interaction) */ 426 427 protected void simulateUsingNewInstance(Wrappee wrappee) { 428 /** simulates a new interaction */ 429 // Log.trace("core","simulateUsingNewInstance "+this); 430 Collaboration collab = Collaboration.get(); 431 //collab.newInteraction(); 432 whenUsingNewInstance(new Interaction(null,wrappee,null,null)); 433 /** end of simulated interaction */ 434 //collab.endOfInteraction(); 435 } 436 437 /** 438 * This method is called when a new instance is created. 439 * 440 * <p>Since the instance is not initialized yet, the aspect 441 * component must not wrap it at this stage. To wrap a new 442 * instance, the aspect component must use the 443 * <code>whenUsingNewInstance()</code> method. This method is not 444 * upcalled when the instantiation is remotly performed (from a 445 * deployer site), see the <code>whenRemoteInstantiation()</code> 446 * method.<br> 447 * 448 * @param newInstance the newly created instance 449 * @see #whenUsingNewInstance(Interaction) 450 * @see #whenRemoteInstantiation(Wrappee,String) */ 451 452 //public void whenNewInstance( Wrappee newInstance ) {} 453 454 /** 455 * This method is upcalled when a new object is instanciated from a 456 * remote site. 457 * 458 * <p>The name that is passed is the name of the remote 459 * reference that has been used to create the object. Typically, it 460 * is the name that will be used by the naming aspect to name the 461 * new object. 462 * 463 * @param newInstance the instance that have been created by a 464 * remote host 465 * @param name the name of the new instance 466 * @see org.objectweb.jac.aspects.naming.NamingAC#whenRemoteInstantiation(Wrappee,String) 467 */ 468 public void whenRemoteInstantiation(Wrappee newInstance, 469 String name) {} 470 471 /** 472 * This method unwraps all the instances that have been wrapped by 473 * the aspect component. 474 * 475 * <p>This is done in a generic way that is not very efficient. To 476 * optimize this process, the user may overload the 477 * <code>unweave()</code> method to change its default 478 * behavior.<br> 479 * 480 * @see #unweave() 481 */ 482 protected final void unwrapAll() { 483 loggerWrap.debug("unwrap all"); 484 loggerWrap.debug(wrappers.size()+" wrappers: " + wrappers); 485 Iterator it = ObjectRepository.getMemoryObjects().iterator(); 486 while (it.hasNext()) { 487 Object o = it.next(); 488 if (o instanceof Wrappee) { 489 Wrappee wrappee = (Wrappee)o; 490 ClassItem wrappeeClass = cr.getClass(wrappee); 491 loggerWrap.debug("testing wrappee " + wrappee); 492 Wrapping.unwrap(wrappee,wrappeeClass,wrappers); 493 Wrapping.unwrap(null,wrappeeClass,wrappers); 494 } 495 } 496 wrappers.clear(); 497 } 498 499 /** 500 * The programmer should overload this method to unwrap all the 501 * currently wrapped Jac objects so that it unplugs a given aspect. 502 * 503 * <p>By default, calls the <code>unwrapAll</code> method but the 504 * programmer can implement the unveaving process in a more 505 * efficient way.<br> 506 * 507 * @see #unwrapAll() */ 508 509 public void unweave() { 510 logger.info("--- unweaving "+this+" ---"); 511 long start = System.currentTimeMillis(); 512 unwrapAll(); 513 pointcuts.clear(); 514 loggerPerf.info("unweaved "+this+" in "+(System.currentTimeMillis()-start)+"ms"); 515 } 516 517 /** 518 * This method is automatically called when a Jac Object is 519 * cloned. */ 520 521 public void whenClone(Wrappee cloned, Wrappee clone) {} 522 523 /** 524 * This method is called when a JAC object is serialized and can 525 * parametrize the serialization by filling the <code>finalObject</code> 526 * parameter. 527 * 528 * <p><b>IMPORTANT</b>: this method is upcalled only if the 529 * serialization is done with a 530 * <code>JacObjectOutputStream</code>. To ensure the correct use of 531 * this class, only use <code>JacObject.serialize()</code> to 532 * serialize an object.</p> 533 * 534 * @param orgObject the object being serialized 535 * @param finalObject the corresponding serialized structure 536 * @return the object being serialized (usually orgObject, but not 537 * necessarily). 538 * @see SerializedJacObject 539 * @see JacObjectOutputStream */ 540 541 public Wrappee whenSerialized(Wrappee orgObject, 542 SerializedJacObject finalObject) { 543 return orgObject; 544 } 545 546 /** 547 * This method is called when a base object is deserialized and can 548 * parametrize the deserialization by reading the 549 * SerializedJacObject instance to get some extra infos on the 550 * aspects. 551 * 552 * <p><b>IMPORTANT</b>: this method is upcalled only if the 553 * deserialization is done with a 554 * <code>JacObjectInputStream</code>. To ensure the correct use of 555 * this class, only use <code>JacObject.deserialize()</code> to 556 * deserialize an object. 557 * 558 * @param orgObject the corresponding serialized structure 559 * @param finalObject the object being deserialized 560 * @return the object being deserialized (usually finalObject but 561 * not necessarily) 562 * @see SerializedJacObject 563 * @see JacObjectInputStream */ 564 565 public Wrappee whenDeserialized(SerializedJacObject orgObject, 566 Wrappee finalObject) { 567 return finalObject; 568 } 569 570 571 public void onExit() {} 572 573 /** 574 * This method is called when a wrapper is going to be applied to a 575 * wrappee. 576 * 577 * <p>By overloading this method, the aspect component can skip the 578 * wrapper, for instance if the current collaboration shows that 579 * the wrapper has allready been applied.<br> 580 * 581 * <p>If the method returns true, then the wrapper is run, else it 582 * is skipped. Default: return true.<br> 583 * 584 * @param wrapper the wrapper that is going to be runned 585 * @param wrappingMethod the name of the may-be runned wrapping 586 * method 587 * @return a boolean that tells if the wrapper has to be runned 588 * (true) or not (false) 589 * @see Wrappee 590 * @see Wrapping#wrap(Wrappee,Wrapper,AbstractMethodItem) 591 * @see Wrapper 592 * @see Wrapper#proceed(Invocation) 593 */ 594 public boolean beforeRunningWrapper(Wrapper wrapper, 595 String wrappingMethod) { 596 return true; 597 } 598 599 /** 600 * This method is called after the application of the 601 * wrapper. 602 * 603 * <p>Typically, the aspect component can set an attribute to 604 * the collaboration that indicates that the wrapper has been 605 * applied (and that can be used by the beforeRunningWrapper 606 * method). 607 * 608 * @param wrapper the wrapper that has just been runned 609 * @param wrappingMethod the name of the runned wrapping method 610 * @see Wrappee 611 * @see Wrapping#wrap(Wrappee,Wrapper,AbstractMethodItem) 612 * @see Wrapper 613 * @see Wrapper#proceed(Invocation) 614 */ 615 public void afterRunningWrapper(Wrapper wrapper, 616 String wrappingMethod) {} 617 618 /** 619 * This method is called after a new wrapper wraps a wrappee. 620 */ 621 622 public void afterWrap(Wrappee wrappee, Wrapper wrapper, 623 String[] wrapping_methods, 624 String[][] wrapped_methods) {} 625 626 /** 627 * This method is called when a program gets a set of object from 628 * the object repository. 629 * 630 * <p>At this step, the aspect can change the returned list so that 631 * the program will see the right set of objects (for instance a 632 * security aspect may filter this set so that the user will not 633 * see the objects s/he is not allowed to see or a persistance 634 * aspect may force the load of a set of objects from the storage 635 * and add them to the list). 636 * 637 * @param objects the returned list (can be modified) 638 * @param cl the class that was used by the program to filter its 639 * instances (can be null for all the objects) 640 * @see ObjectRepository */ 641 642 public void whenGetObjects(Collection objects, ClassItem cl) {} 643 644 public String whenNameObject(Object object,String name) { 645 return name; 646 } 647 648 public void getNameCounters(Map counters) { 649 } 650 651 public void updateNameCounters(Map counters) { 652 } 653 654 /** 655 * This method is upcalled by JAC when an object was seeked into 656 * the name repository and was not found. 657 * 658 * <p>The reason of this miss can be multiple. For instance, the 659 * persistence aspect may have not already load the object from the 660 * storage, or the distribution aspect may need to bind to a remote 661 * object. Thus, this method allows the aspects to resolve the 662 * object. 663 * 664 * <p>By default, this method does nothing. 665 * 666 * <p>The final choosen name is a contextual attribute called 667 * FOUND_OBJECT. 668 * 669 * @param name the name of the object 670 * @see BaseProgramListener#FOUND_OBJECT 671 */ 672 673 public void whenObjectMiss(String name) {} 674 675 public void whenDeleted(Wrappee object) {} 676 677 public void whenFree(Wrappee object) {} 678 679 public void afterApplicationStarted() {} 680 681 public void whenCloseDisplay(Display display) {} 682 683 public void whenTopologyChanged() {} 684 685 /** 686 * Called when the aspect's configuration is reloaded 687 */ 688 public void whenReload() {} 689 public void beforeReload() {} 690 691 /** 692 * This method should be defined by the programmer when specific 693 * actions are needed once the aspect component has be 694 * configured. */ 695 696 public void whenConfigured() {} 697 698 /** 699 * To overload in order to perform some treatements before the 700 * configuration. */ 701 public void beforeConfiguration() throws Exception { 702 } 703 704 public String getName() { 705 return ACManager.getACM().getName(this); 706 } 707 708 /** 709 * This method is upcalled by the system when the aspect is 710 * actually registered in the AC manager. 711 * 712 * <p>Here it does nothing. */ 713 public void doRegister() { 714 } 715 716 /** 717 * This method is upcalled by the system when the aspect is 718 * actually registered in the AC manager. 719 * 720 * <p>Here it does nothing. */ 721 public void doUnregister() { 722 } 723 724 /** Store the pointcuts. */ 725 Vector pointcuts = new Vector(); 726 727 /** 728 * Defines and adds a new method pointcut. 729 * 730 * <p>For more details on how to use pointcuts, see the 731 * <code>MethodPointcut</code> class. 732 * 733 * @param wrappeeExpr the wrappee definition that matches the names 734 * as defined by the naming aspect 735 * @param wrappeeClassExpr the wrappee class expression (matches 736 * the fully qualified class name) 737 * @param wrappeeMethodExpr the wrappee method expression (matches 738 * the full method name as defined by the rtti) 739 * @param wrappingClassName the name of the wrapper class 740 * @param one2one true if each new wrapped instance corresponds to 741 * one different wrapper 742 * @see MethodPointcut 743 */ 744 public Pointcut pointcut(String wrappeeExpr, 745 String wrappeeClassExpr, 746 String wrappeeMethodExpr, 747 String wrappingClassName, 748 String exceptionHandler, 749 boolean one2one) { 750 751 MethodPointcut pc = new MethodPointcut( this, 752 wrappeeExpr, 753 wrappeeClassExpr, 754 wrappeeMethodExpr, 755 null, 756 wrappingClassName, 757 null, 758 null, 759 "ALL", 760 exceptionHandler, 761 one2one ); 762 pointcuts.add(pc); 763 return pc; 764 } 765 766 /** 767 * Defines and adds a new method pointcut. 768 * 769 * <p>For more details on how to use pointcuts, see the 770 * <code>MethodPointcut</code> class. 771 * 772 * @param wrappeeExpr the wrappee definition that matches the names 773 * as defined by the naming aspect 774 * @param wrappeeClassExpr the wrappee class expression (matches 775 * the fully qualified class name) 776 * @param wrappeeMethodExpr the wrappee method expression (matches 777 * the full method name as defined by the rtti) 778 * @param wrappingClassName the name of the wrapper class 779 * @param initParameters the initialization parameters of the 780 * wrapper (passed to the wrappers constructor that matches the 781 * parameters types) 782 * @param one2One true if each new wrapped instance corresponds to 783 * one different wrapper 784 * @see MethodPointcut 785 */ 786 public Pointcut pointcut(String wrappeeExpr, 787 String wrappeeClassExpr, 788 String wrappeeMethodExpr, 789 String wrappingClassName, 790 Object[] initParameters, 791 String exceptionHandler, 792 boolean one2One) { 793 794 MethodPointcut pc = new MethodPointcut( this, 795 wrappeeExpr, 796 wrappeeClassExpr, 797 wrappeeMethodExpr, 798 null, 799 wrappingClassName, 800 null, 801 initParameters, 802 "ALL", 803 exceptionHandler, 804 one2One ); 805 pointcuts.add(pc); 806 return pc; 807 } 808 809 /** 810 * Defines and adds a new localized method pointcut. 811 * 812 * <p>For more details on how to use pointcuts, see the 813 * <code>MethodPointcut</code> class. 814 * 815 * @param wrappeeExpr the wrappee definition that matches the names 816 * as defined by the naming aspect 817 * @param wrappeeClassExpr the wrappee class expression (matches 818 * the fully qualified class name) 819 * @param wrappeeMethodExpr the wrappee method expression (matches 820 * the full method name as defined by the rtti) 821 * @param wrappingClassName the name of the wrapper class 822 * @param hostExpr a regular expression that macthes the hosts 823 * where the pointcut has to be applied (if null or empty string, 824 * default is ".*" which means that the pointcut will be applied on 825 * all the hosts) 826 * @param one2One true if each new wrapped instance corresponds to 827 * one different wrapper 828 * @see MethodPointcut */ 829 830 public Pointcut pointcut(String wrappeeExpr, 831 String wrappeeClassExpr, 832 String wrappeeMethodExpr, 833 String wrappingClassName, 834 String hostExpr, 835 String exceptionHandler, 836 boolean one2One) { 837 838 MethodPointcut pc = new MethodPointcut(this, 839 wrappeeExpr, 840 wrappeeClassExpr, 841 wrappeeMethodExpr, 842 null, 843 wrappingClassName, 844 null, 845 null, 846 hostExpr, 847 exceptionHandler, 848 one2One); 849 pointcuts.add(pc); 850 return pc; 851 } 852 853 /** 854 * Defines and adds a new localized method pointcut. 855 * 856 * <p>For more details on how to use pointcuts, see the 857 * <code>MethodPointcut</code> class. 858 * 859 * @param wrappeeExpr the wrappee definition that matches the names 860 * as defined by the naming aspect 861 * @param wrappeeClassExpr the wrappee class expression (matches 862 * the fully qualified class name) 863 * @param wrappeeMethodExpr the wrappee method expression (matches 864 * the full method name as defined by the rtti) 865 * @param wrappingClassName the name of the wrapper class 866 * @param initParameters the initialization parameters of the 867 * wrapper (passed to the wrappers constructor that matches the 868 * parameters types) 869 * @param hostExpr a regular expression that macthes the hosts 870 * where the pointcut has to be applied (if null or empty string, 871 * default is ".*" which means that the pointcut will be applied on 872 * all the hosts) 873 * @param one2One true if each new wrapped instance corresponds to 874 * one different wrapper 875 * @see MethodPointcut 876 */ 877 public Pointcut pointcut(String wrappeeExpr, 878 String wrappeeClassExpr, 879 String wrappeeMethodExpr, 880 String wrappingClassName, 881 Object[] initParameters, 882 String hostExpr, 883 String exceptionHandler, 884 boolean one2One) { 885 886 MethodPointcut pc = new MethodPointcut(this, 887 wrappeeExpr, 888 wrappeeClassExpr, 889 wrappeeMethodExpr, 890 null, 891 wrappingClassName, 892 null, 893 initParameters, 894 hostExpr, 895 exceptionHandler, 896 one2One); 897 pointcuts.add(pc); 898 return pc; 899 } 900 901 /** 902 * Defines and adds a new method pointcut. 903 * 904 * <p>For more details on how to use pointcuts, see the 905 * <code>MethodPointcut</code> class. 906 * 907 * @param wrappeeExpr the wrappee definition that matches the names 908 * as defined by the naming aspect 909 * @param wrappeeClassExpr the wrappee class expression (matches 910 * the fully qualified class name) 911 * @param wrappeeMethodExpr the wrappee method expression (matches 912 * the full method name as defined by the rtti) 913 * @param wrapper the wrapper that contains the wrapping method, 914 * cannot be null 915 * 916 * @see MethodPointcut 917 */ 918 public Pointcut pointcut(String wrappeeExpr, 919 String wrappeeClassExpr, 920 String wrappeeMethodExpr, 921 Wrapper wrapper, 922 String exceptionHandler) { 923 924 MethodPointcut pc = new MethodPointcut(this, 925 wrappeeExpr, 926 wrappeeClassExpr, 927 wrappeeMethodExpr, 928 wrapper, 929 wrapper.getClass().getName(), 930 null, 931 null, 932 "ALL", 933 exceptionHandler, 934 false); 935 pointcuts.add(pc); 936 return pc; 937 } 938 939 /** 940 * Defines and adds a new localized method pointcut. 941 * 942 * <p>For more details on how to use pointcuts, see the 943 * <code>MethodPointcut</code> class. 944 * 945 * @param wrappeeExpr the wrappee definition that matches the names 946 * as defined by the naming aspect 947 * @param wrappeeClassExpr the wrappee class expression (matches 948 * the fully qualified class name) 949 * @param wrappeeMethodExpr the wrappee method expression (matches 950 * the full method name as defined by the rtti) 951 * @param wrapper the wrapper that contains the wrapping method, 952 * cannot be null 953 * @param hostExpr a regular expression that macthes the hosts 954 * where the pointcut has to be applied (if null or empty string, 955 * default is ".*" which means that the pointcut will be applied on 956 * all the hosts) 957 * 958 * @see MethodPointcut 959 */ 960 public Pointcut pointcut(String wrappeeExpr, 961 String wrappeeClassExpr, 962 String wrappeeMethodExpr, 963 Wrapper wrapper, 964 String hostExpr, 965 String exceptionHandler) { 966 967 MethodPointcut pc = new MethodPointcut(this, 968 wrappeeExpr, 969 wrappeeClassExpr, 970 wrappeeMethodExpr, 971 wrapper, 972 wrapper.getClass().getName(), 973 null, 974 null, 975 hostExpr, 976 exceptionHandler, 977 false); 978 pointcuts.add(pc); 979 return pc; 980 } 981 982 /** 983 * Defines and adds a new pointcut that upcalls an aspect method 984 * when the matching object is created. 985 * 986 * <p>For more details on how to use pointcuts, see the 987 * <code>MethodPointcut</code> class. 988 * 989 * @param wrappeeExpr the wrappee definition that matches the names 990 * as defined by the naming aspect 991 * @param wrappeeClassExpr the wrappee class expression (matches 992 * the fully qualified class name) 993 * @param methodName a method of the aspect component to call 994 * @param methodArgs the arguments of this methods when called 995 * @see MethodPointcut */ 996 997 public Pointcut pointcut(String wrappeeExpr, 998 String wrappeeClassExpr, 999 String methodName, 1000 Object[] methodArgs, 1001 String exceptionHandler) { 1002 1003 MethodPointcut pc = new MethodPointcut(this, 1004 wrappeeExpr, 1005 wrappeeClassExpr, 1006 null, 1007 null, 1008 null, 1009 methodName, 1010 methodArgs, 1011 "ALL", 1012 exceptionHandler, 1013 false); 1014 pointcuts.add(pc); 1015 return pc; 1016 } 1017 1018 /** 1019 * Defines and adds a new localized pointcut that upcalls an aspect 1020 * method when the matching object is created. 1021 * 1022 * <p>For more details on how to use pointcuts, see the 1023 * <code>MethodPointcut</code> class.</p> 1024 * 1025 * @param wrappeeExpr the wrappee definition that matches the names 1026 * as defined by the naming aspect 1027 * @param wrappeeClassExpr the wrappee class expression (matches 1028 * the fully qualified class name) 1029 * @param methodName a method of the aspect component to call 1030 * @param methodArgs the arguments of this methods when called 1031 * @see MethodPointcut */ 1032 1033 public Pointcut pointcut(String wrappeeExpr, 1034 String wrappeeClassExpr, 1035 String methodName, 1036 Object[] methodArgs, 1037 String hostExpr, 1038 String exceptionHandler) { 1039 1040 MethodPointcut pc = new MethodPointcut(this, 1041 wrappeeExpr, 1042 wrappeeClassExpr, 1043 null, 1044 null, 1045 null, 1046 methodName, 1047 methodArgs, 1048 hostExpr, 1049 exceptionHandler, 1050 false); 1051 pointcuts.add(pc); 1052 return pc; 1053 } 1054 1055 /** 1056 * Generic config method to set an attribute on a class 1057 * @param cli the class to set an attribute on 1058 * @param name name of the attribute 1059 * @param value string value of the attribute 1060 */ 1061 public void setAttribute(ClassItem cli, String name, String value) { 1062 cli.setAttribute(name,value); 1063 } 1064 1065 /** 1066 * Generic config method to set an attribute on a class 1067 * @param field the field to set an attribute on 1068 * @param name name of the attribute 1069 * @param value string value of the attribute 1070 */ 1071 public void setAttribute(FieldItem field, String name, String value) { 1072 field.setAttribute(name,value); 1073 } 1074 1075 1076 /** 1077 * Generic config method to set an attribute on a method 1078 * @param method the method to set an attribute on 1079 * @param name name of the attribute 1080 * @param value string value of the attribute 1081 */ 1082 public void setAttribute(AbstractMethodItem method, 1083 String name, String value) { 1084 method.setAttribute(name,value); 1085 } 1086 1087 // following methods implement the CollaborationParticipant interface 1088 1089 public final void attrdef(String name, Object value) { 1090 Collaboration.get().addAttribute( name, value ); 1091 } 1092 1093 public final Object attr( String name ) { 1094 return Collaboration.get().getAttribute( name ); 1095 } 1096 1097 /** Block keywords which can be used instead of "block" */ 1098 protected String[] blockKeywords = new String[0]; 1099 1100 public Set getBlockKeywords() { 1101 return new HashSet(Arrays.asList(blockKeywords)); 1102 } 1103 1104 /** Returns defaults configuration files that must be loaded before 1105 the user's configuration */ 1106 public String[] getDefaultConfigs() { 1107 return new String[0]; 1108 } 1109 1110 /** 1111 * Returns all the configuration methods of the aspect 1112 * @return Collection of MethodItem 1113 */ 1114 public Collection getConfigurationMethods() { 1115 Vector result = new Vector(); 1116 Iterator ms = cr.getClass(getClass()).getAllMethods().iterator(); 1117 1118 while(ms.hasNext()){ 1119 MethodItem methodItem=(MethodItem)ms.next(); 1120 if (isConfigurationMethod(methodItem.getActualMethod())){ 1121 result.add(methodItem); 1122 } 1123 } 1124 return result; 1125 } 1126 1127 /** 1128 * Returns all the name of the configuration methods of the aspect 1129 * @return Collection of String 1130 */ 1131 public Collection getConfigurationMethodsName() { 1132 Vector result = new Vector(); 1133 Method[] ms = getClass().getMethods(); 1134 for(int i=0;i<ms.length;i++) { 1135 if (isConfigurationMethod(ms[i])) { 1136 result.add(ms[i].getName()); 1137 } 1138 } 1139 return result; 1140 } 1141 1142 1143 /** 1144 * Returns all the configuration methods of the aspect whose first 1145 * parameter is compatible with a given type. 1146 * @param firstParamType the type the first parameter must be compatible with 1147 * @return a collection of MethodItem with at least one parameter, 1148 * and whose first parameter can be of type firstParamType or a 1149 * subclass of firstParamType */ 1150 public List getConfigurationMethodsName(Class firstParamType) { 1151 Vector result = new Vector(); 1152 Iterator ms = cr.getClass(getClass()) 1153 .getAllMethods().iterator(); 1154 // We use hash set to remove duplicate entries quickly 1155 HashSet names = new HashSet(); 1156 1157 while (ms.hasNext()) { 1158 MethodItem methodItem = (MethodItem)ms.next(); 1159 if (isConfigurationMethod(methodItem.getActualMethod())) { 1160 Class[] paramTypes = methodItem.getParameterTypes(); 1161 if (paramTypes.length>0 && 1162 (paramTypes[0].isAssignableFrom(firstParamType) || 1163 firstParamType.isAssignableFrom(paramTypes[0])) && 1164 !names.contains(methodItem.getName())) { 1165 result.add(methodItem.getName()); 1166 names.add(methodItem.getName()); 1167 } 1168 } 1169 } 1170 return result; 1171 } 1172 1173 1174 /** 1175 * Tells wether a method is configuration method of this aspect 1176 * @param method 1177 * @return true if the method was a configuration method. 1178 */ 1179 public boolean isConfigurationMethod(Method method) { 1180 Class cl = method.getDeclaringClass(); 1181 if (AspectComponent.class.isAssignableFrom(cl)) { 1182 Class[] interfs=cl.getInterfaces(); 1183 for(int i=0;i<interfs.length;i++) { 1184 if (!interfs[i].getName().endsWith("Conf")) 1185 continue; 1186 Method[] ms = interfs[i].getMethods(); 1187 for(int j=0; j<ms.length; j++) { 1188 if (ms[j].getName().equals(method.getName())) { 1189 return true; 1190 } 1191 } 1192 } 1193 } 1194 return false; 1195 } 1196 1197 protected ConfigMethod currentConfigMethod; 1198 protected Imports currentImports; 1199 1200 /** 1201 * Configures this aspect component with a given configuration file. 1202 * 1203 * @param name name of the aspect component (informative) 1204 * @param filePath path to a resource of file containing the configuration 1205 */ 1206 public void configure(String name, String filePath) { 1207 long start = System.currentTimeMillis(); 1208 if (filePath == null) { 1209 return; 1210 } 1211 loggerConf.info("configuring aspect " + name + " with file "+filePath); 1212 List configMethods = null; 1213 try { 1214 // Naming.PARSER_NAME ("parserimpl#0") is the only 1215 // instance of ACParser, if we are on the master site, it 1216 // is automatically instantiated by the binding aspect, if 1217 // not, the binding aspect creates a stub for 1218 // parserimpl#0@mastersite 1219 Parser acp = (Parser)NameRepository.get().getObject(Naming.PARSER_NAME); 1220 configMethods = acp.parse(filePath,this.getClass().getName(), 1221 this.getBlockKeywords()); 1222 } catch (FileNotFoundException e) { 1223 logger.warn("cannot find config file "+filePath+" for "+name+ 1224 "("+e+"), using serialized infos."); 1225 } catch (Exception e) { 1226 logger.error("configure "+name+","+filePath,e); 1227 return; 1228 } 1229 if (configMethods==null) 1230 return; 1231 currentImports = new Imports(); 1232 for (int i=0; i<configMethods.size(); i++) { 1233 MethodItem method = null; 1234 Object statement = configMethods.get(i); 1235 if (statement instanceof ImportStatement) { 1236 ImportStatement imp = (ImportStatement)statement; 1237 currentImports.add(imp.getExpr()); 1238 continue; 1239 } 1240 ConfigMethod cm = (ConfigMethod)statement; 1241 currentConfigMethod = cm; 1242 if (cm==null) { 1243 loggerConf.warn("skipping error"); 1244 continue; 1245 } 1246 try { 1247 loggerConf.debug( 1248 "invoking configuration method '"+cm.getMethod()+ 1249 "' on "+this.getClass()+" with "+Arrays.asList(cm.getArgs())); 1250 if (cm.getMethod().equals("")) 1251 continue; 1252 MethodItem[] methodItems = 1253 cr.getClass(this.getClass()). 1254 getMethods(cm.getMethod()); 1255 if (methodItems==null || methodItems.length==0) { 1256 loggerConf.warn("No such configuration method "+cm.getMethod()+ 1257 " in AC "+this.getClass()); 1258 } 1259 loggerConf.debug( 1260 "matching methods : "+Arrays.asList(methodItems)); 1261 1262 Object[] paramValues = null; 1263 //determine which method to call based on number of parameters 1264 // Warning, ClassItem+String can become a MemberItem 1265 Vector exceptions = new Vector(); 1266 for(int j=0; j<methodItems.length && method==null; j++) { 1267 paramValues = cm.getArgs(); 1268 //If the ConfigMethod has the same number of param as MethodItem 1269 if (methodItems[j].getParameterTypes().length==paramValues.length) { 1270 method = methodItems[j]; 1271 loggerConf.debug("selecting method "+method); 1272 1273 // Try to convert the parameters 1274 // if it fails, try the next method 1275 paramValues = cm.getArgs(); 1276 try { 1277 Class[] paramTypes = method.getParameterTypes(); 1278 for(int k=0; k<paramTypes.length; k++) { 1279 paramValues[k] = 1280 ACConfiguration.convertValue( 1281 paramValues[k], 1282 paramTypes[k], 1283 currentImports); 1284 } 1285 break; 1286 } catch(Exception e) { 1287 exceptions.add(e); 1288 method = null; 1289 } 1290 } 1291 //If method==null try to translate MemberItem parameters 1292 //into ClassItem+String 1293 if (method==null && 1294 (methodItems[j].getParameterCount()>0)) { 1295 for (int p=0; p<methodItems[j].getParameterCount(); p++) { 1296 if ((MemberItem.class.isAssignableFrom( 1297 methodItems[j].getParameterTypes()[p])) 1298 && (methodItems[j].getParameterTypes().length 1299 == paramValues.length-1)) 1300 { 1301 method = methodItems[j]; 1302 loggerConf.debug( 1303 "selecting method "+method+ 1304 " with MemberItem=ClassItem+String for param #"+p); 1305 paramValues = cm.getArgs(); 1306 Object paramValuesTranslated[] = new Object[paramValues.length-1]; 1307 try { 1308 Class[] paramTypes = method.getParameterTypes(); 1309 ClassItem classItem = 1310 (ClassItem)ACConfiguration.convertValue( 1311 paramValues[p], 1312 ClassItem.class, 1313 currentImports); 1314 paramValuesTranslated[p] = 1315 classItem.getMember( 1316 (String)ACConfiguration.convertValue( 1317 paramValues[p+1], 1318 String.class, 1319 currentImports)); 1320 if (!paramTypes[p].isAssignableFrom( 1321 paramValuesTranslated[p].getClass())) 1322 { 1323 throw new Exception( 1324 "Error translating parameter: "+ 1325 paramValuesTranslated[0].getClass().getName()+ 1326 " can't be assigned to "+paramTypes[p].getName()); 1327 } 1328 for(int k=p+1; k<paramTypes.length; k++){ 1329 paramValuesTranslated[k] = 1330 ACConfiguration.convertValue( 1331 paramValues[k+1], 1332 paramTypes[k], 1333 currentImports); 1334 } 1335 1336 for(int k=0; k<p; k++){ 1337 paramValuesTranslated[k] = 1338 ACConfiguration.convertValue( 1339 paramValues[k], 1340 paramTypes[k], 1341 currentImports); 1342 } 1343 paramValues = paramValuesTranslated; 1344 loggerConf.debug("Parameter transformation successful"); 1345 break; 1346 } catch (Exception e) { 1347 loggerConf.debug("Exception in parameter transformation: "+e); 1348 exceptions.add(e); 1349 method = null; 1350 } 1351 } 1352 } 1353 } 1354 1355 } 1356 if (method == null) { 1357 if (exceptions.isEmpty()) { 1358 throw new Exception("Wrong number of parameters for method "+ 1359 cm.getMethod()); 1360 } else { 1361 throw (Exception)exceptions.get(0); 1362 //new Exception(exceptions.toString()); 1363 } 1364 } 1365 1366 loggerConf.debug( 1367 filePath+": line "+cm.getLineNumber() + 1368 ", invoke configuration method '" + 1369 (method!=null?method.toString():cm.getMethod()) + 1370 "' on " + this.getClass() + 1371 " with " + Arrays.asList(cm.getArgs())); 1372 method.invoke(this,paramValues); 1373 1374 } catch (InvocationTargetException e) { 1375 Throwable target = e.getTargetException(); 1376 loggerConf.warn(cm.getLineNumber() + 1377 ", failed to invoke configuration method '"+ 1378 (method!=null?method.toString():cm.getMethod())+ 1379 "' on "+this.getClass() + " with "+ 1380 Arrays.asList(cm.getArgs()),target); 1381 loggerConf.info("Stack trace follows",e); 1382 } catch (Throwable e) { 1383 loggerConf.warn(cm.getLineNumber() + 1384 ", failed to invoke configuration method '"+ 1385 (method!=null?method.toString():cm.getMethod())+ 1386 "' on "+this.getClass()+ 1387 " with "+Arrays.asList(cm.getArgs())+" : "+e); 1388 loggerConf.info("Stack trace follows",e); 1389 } finally { 1390 currentConfigMethod = null; 1391 } 1392 } 1393 loggerPerf.debug( 1394 "aspect configuration file "+filePath+" read in "+ 1395 (System.currentTimeMillis()-start)+"ms"); 1396 } 1397 1398 public void beforeWrappeeInit(Wrappee wrappee) {}; 1399 1400 public void afterWrappeeInit(Wrappee wrappee) {}; 1401 1402 /** 1403 * Issue a warning containing the file, line number and 1404 * configuration method name (if available) 1405 * 1406 * @param message the warning message to print 1407 * @see #error(String) 1408 */ 1409 protected void warning(String message) { 1410 if (currentConfigMethod!=null) 1411 loggerConf.warn( 1412 currentConfigMethod.getLineNumber()+":"+ 1413 currentConfigMethod.getMethod()+": "+message); 1414 else 1415 loggerConf.warn(message); 1416 } 1417 1418 /** 1419 * Issue an error containing the file, line number and 1420 * configuration method name (if available) 1421 * 1422 * @param message the warning message to print 1423 * @see #warning(String) 1424 */ 1425 protected void error(String message) { 1426 if (currentConfigMethod!=null) 1427 loggerConf.error( 1428 currentConfigMethod.getLineNumber()+":"+ 1429 currentConfigMethod.getMethod()+": "+message); 1430 else 1431 logger.error(message); 1432 } 1433 1434 /** 1435 * Returns a named aspect component in the same application 1436 */ 1437 protected AspectComponent getAC(String name) { 1438 return ACManager.getACM().getACFromFullName(application+"."+name); 1439 } 1440 }