Hi Josh: Below is a description of a dynamic COOL version for Smalltalk. Any implications for Demeter/Java COOL? -- Karl ----------------- From czarn@rtna.daimlerbenz.com Tue Dec 2 15:47:14 1997 here is an excerpt from a mail I send to the AspectJ team: Some desired features for Cool ---------------------------------------------------------- Given that some system requires an elaborated synchronization and dynamic load balancing, there are some features which, in my view, Cool would have to provide in order to adequately support such a system. These features would include 1) splitting coordination state and the ability to reference one coordinator from another: this is needed for dynamic composition - dynamically adding new server objects would require interaction not only between objects but coordinators too; also, if an instance of class A interacts with an instance of class B and an instance of class C, it might be reasonable to have two coordinators, one for A and B and one for A and C (esp. if they do not share coordination state). So it should be possible to associate an object with more than one coordinator. Splitting also seems to be desirable for dividing large coordination state machines. 2) dynamically assigning coordinators to object instances: changes in the environment could require changes to the synchronization strategy; 3) per-instance coordination of instances of multiple classes: Cool references functionality using class names. This turns out to be awkward when coordinating instances of multiple classes. Even in the simple candy assembly line example, it is not possible to simply instantiate multiple assembly lines without changing the coordination code. The need for instance-based coordination is even greater if you would like to support dynamic reconfiguration and load balancing. Implementing "dynamic Cool" in Smalltalk -------------------------------------------------------- In order to see how the dynamic features work, I implemented Cool in Smalltalk using the Smalltalk MOP. This implementation is based on the ability to attach listener objects to an arbitrary object at a certain "selector". All messages of the form "selector" sent to the object are redirected to the listener attached at "selector". This allows us to transparently implement before and after methods. Now, there are a number of coordinator objects. Each coordinator object has one or more ports. An object which has to be synchronized can be connected to one or more ports of one or more coordinators. A port of a coordinator maintains a number of message coordinators, one per message that has to be coordinated on that port. A message coordinator keeps the selfex, mutex, and requires conditions and onEntry and onExit blocks for that message. By connecting an object to a port, the message coordinators of that port get connected to the corresponding listeners of the object. Since one listener can be connected to many message coordinators belonging to different object coordinators, one object can be connected to more than one port of more than one object coordinator. Connecting or disconnecting an object instance to/from a port of a coordinator instance can be done at any time during run-time. Also the selfex, mutex, and reqires/onEntry/onExit specs of a coordinator instance can be modified at run-time. The selfex, mutex, and reqires/onEntry/onExit specs look much the same as Cool, e.g. here is the candy assembly line coordination spec in Smalltalk: defineCoordination packFull := false. gotPack := false. gotLabel := false. self addPorts: #( packer finalizer ); selfex: #( 'packer.newCandy:' ); at: 'packer.newCandy:' requires: [ :packer | packFull not ] onExit: [ :packer | packer candyPack candyCount = packer maxCandy ifTrue: [ packFull := true ]]; at: 'packer.processPack' requires: [ :packer | packFull ]; at: 'finalizer.newPack:' requires: [ :finalizer | gotPack not ] onEntry: [ :finalizer | gotPack := true ] onExit: [ :finalizer | packFull := false ]; at: 'finalizer.newLabel:' requires: [ :finalizer | gotLabel not ] onEntry: [ :finalizer | gotLabel := true ]; at: 'finalizer.glueLabelToPack' requires: [ :finalizer | gotPack & gotLabel ]; at: 'finalizer.shipNewCandyPack' onExit: [ :finalizer | gotPack := false. gotLabel := false ]. This method is called automatically in order to initialize a coordinator instance. Any of the selfex:, mutex:, at:requires:onEntry:onExit: messages can also be sent to the coordinator instance in order to modify its synchronization strategy thereafter. The following code snippet creates a finalizer, a packer, and a coordinator (which is initialized using the method presented above), and connects the packer and the finalizer to the coordinator: finalizer := Finalizer new. packer := Packer newWith: finalizer. coordinator := CandyAssemblyLineCoordinator new. coordinator register: packer at: #packer; register: finalizer at: #finalizer. Unlike in the AspectJ implementation, we can have multiple instances of finalizer and packer at a time. The full Smalltalk (VisualWorks 2.0 or 2.5) code for Cool, the assembly line example, and another example are in the attachments. The implementation of Cool in Smalltalk was simple thanks to the reflective facilities in Smalltalk. First, a simple MOP was implemented, and the layer for attaching object was implemented on top of it. The coordinator code sits on the latter. The MOP consists of two methods in Object, one for adding methods to single instances (i.e. instance-specific methods) and one for adding instance variables to instances: specialize: stringRepresentingCodeOfAMethod "This method inserts a method represented by stringRepresentingCodeOfAMethod into the receiver. This is implemented using a lightweight, private class, which is inserted between the receiver and its original class." addInstanceVariable: stringRepresentingNameForAccessors "This method adds a new instance variable to the receiver and creates accessor methods based on stringRepresentingNameForAccessors. This is implemented by modifying the format of the object, creating an object of the new format, and mutating the old object into the new one using become:." ------------------------------------ Examples: TestObject (class) >> examples this example demonstrates the layer for attaching objects TwoMethodsTest (class) >> example1 TwoMethodsTest (class) >> example2 TwoMethodsTest (class) >> example3 these examples demonstrate the flexibility of dynamic configurators. The code is not secured against any potential problems when dynamically modifying the coordination (one would need to introduce some extra critical blocks). But with the included examples, it works fine. AssemblyLine (class) >> example this is the assembly line example