Course: SE737F - Adaptive Object-Oriented Software Development: The Demeter Method Name: Mike Miller Host: n/a Account: n/a Directory: n/a Credits: Josh Marshall - Aspect programming and the weaver Project: Demeter/Java For Java Beans Project Summary This project studies the JavaBeans specification and provides recommendations for changes to be made to the Demeter/Java system so that it can build JavaBean components. The project plan follows: Take a program generated by Demeter/Java and rewrite it into a JavaBean. Take detailed notes on the changes required. Design a class dictionary for an aspect language for JavaBeans. It should allow us to talk about events which a class exports. Translate the aspect language into weaving instructions for the generic aspect weaver written by Johsh Marshall. Run the weaver to generate the JavaBean. JavaBeans Overview JavaBeans is a portable, platform-independent software component model implemented in Java. As a result of being written in Java, JavaBeans have the advantage of "write-once and run-anywhere", which is Java's biggest advantage. Like other software component models, JavaBeans is intended to provide "software reuse". Once a component is built, it can be included in a builder-tool. Some beans have GUI elements such as buttons or sliders but beans are not required to contain GUI elements. JavaBeans are defined in terms of support for the following five (5) features: Introspection - Introspection is the process by which one program or component inspects another component to discover what methods and events are available from the second component. Introspection can be completed in one of two ways. One method is reflection, where the builder environment determines the properties, events and methods by by searching for the defined design patterns. The other method is explicit bean specification. Explicit bean specification includes the creation of a BeanInfo object to provide detailed information about properties, methods and events for a JavaBean. Customization - This process allows the user or application builder to customize the apppearance and behavior of a bean. Events - The Java 1.1 event handling process is used to communicate events between different components. Properties - JavaBeans provide support for customizing and programmatic use of a components data members. Persistence - This feature means storing away appropriate parts of a bean's internal state so that the bean can be reconstructed at a later time. Initial Demeter/Java class dictionary for Container container.cd ------------------------------------------------------------------------------------------ Container = "(" List(Item) Capacity [ Integer] ")". Item : Container | Simple. List(S) ~ {S}. Simple = Ident Weight. Capacity = Integer. Weight = Integer. SummingVisitor = Integer. CheckingVisitor = SummingVisitor Integer. Container.java source code modified to create a JavaBean Changes made to the original source in order to create a JavaBean are marked in bold. import java.beans.*; import java.io.*; import java.util.*; import EDU.neu.ccs.demeter.*; public class Container extends Item implements Cloneable, Serializable { // include this so it shows up in the BeanBox PropertySheet // and make it a Bound Property protected int testnumber=7; public int getTestNumber() { return testnumber; } public void setTestNumber(int newNum) { int old = testnumber; testnumber = newNum; changes.firePropertyChange("testnumber", new Integer(old), new Integer(testnumber) ); } protected Item_List contents; public Item_List get_contents() { return contents; } public void set_contents(Item_List new_contents) { contents = new_contents; } protected Capacity capacity; public Capacity get_capacity() { return capacity; } // force an event just to show how to implement Events public void set_capacity(Capacity new_capacity) { capacity = new_capacity; this.broadcastContainerEvent(); } // make this a Contrained Property protected Integer initial; public Integer get_initial() { return initial; } public void set_initial(Integer new_initial) { try { Integer oldInitial = initial; vetos.fireVetoableChange("initial", oldInitial, new_initial); initial = new_initial; changes.firePropertyChange("initial", oldInitial, initial); } catch (PropertyVetoException e) { System.out.println("set_initial received a veto!"); } } public Container() { super(); } public Container(Item_List contents, Capacity capacity, Integer initial) { super(); set_contents(contents); set_capacity(capacity); set_initial(initial); } public static Container parse(java.io.InputStream in) throws ParseError { return new Parser(in)._Container(); } public static Container parse(String s) { try { return parse(new java.io.ByteArrayInputStream(s.getBytes())); } catch (ParseError e) { throw new RuntimeException(e.toString()); } } // traverse while counting and summing //void checkCapacity() throws Exception { public void checkCapacity() { try{ SummingVisitor sV = new SummingVisitor(); CheckingVisitor cV = new CheckingVisitor(); cV.set_sV(sV); this.allWeights(cV, sV); } catch(Exception e) {} } void universal_trv0_bef(UniversalVisitor _v_) { super.universal_trv0_bef(_v_); _v_.before(this); } void universal_trv0_aft(UniversalVisitor _v_) { _v_.after(this); super.universal_trv0_aft(_v_); } void universal_trv0(UniversalVisitor _v_) { universal_trv0_bef(_v_); _v_.before_contents(this, contents); contents.universal_trv0(_v_); _v_.after_contents(this, contents); _v_.before_capacity(this, capacity); capacity.universal_trv0(_v_); _v_.after_capacity(this, capacity); if (initial != null) { _v_.before_initial(this, initial); _v_.after_initial(this, initial); } super.universal_trv0(_v_); universal_trv0_aft(_v_); } public void allWeights(CheckingVisitor cV, SummingVisitor sV) { allWeights_trv1(cV, sV); } void allWeights_trv1_bef(CheckingVisitor cV, SummingVisitor sV) { super.allWeights_trv1_bef(cV, sV); cV.before(this); } void allWeights_trv1_aft(CheckingVisitor cV, SummingVisitor sV) { cV.after(this); super.allWeights_trv1_aft(cV, sV); } void allWeights_trv1(CheckingVisitor cV, SummingVisitor sV) { allWeights_trv1_bef(cV, sV); contents.allWeights_trv1(cV, sV); allWeights_trv1_aft(cV, sV); } /** internal fields for PropertyChange processing */ private PropertyChangeSupport changes = new PropertyChangeSupport(this); private VetoableChangeSupport vetos = new VetoableChangeSupport(this); /** regular PropertyChangeListners methods */ public void addPropertyChangeListener(PropertyChangeListener l) { changes.addPropertyChangeListener(l); } public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l); } /** Vetoable PropertyChangeListners methods */ public void addVetoableChangeListener(VetoableChangeListener l) { vetos.addVetoableChangeListener(l); } public void removeVetoableChangeListener(VetoableChangeListener l) { vetos.removeVetoableChangeListener(l); } // Events code private Vector containerListeners = new Vector(); public synchronized void addContainerListener(ContainerListener l) { containerListeners.addElement(l); } public synchronized void removeContainerListener(ContainerListener l) { containerListeners.removeElement(l); } public void broadcastContainerEvent() { Vector l; ContainerEventObject ceo = new ContainerEventObject(this); synchronized(this) { l = (Vector)containerListeners.clone(); } for (int i = 0; i < l.size(); i++) { ContainerListener cl = (ContainerListener)l.elementAt(i); cl.handleContainerEvent(ceo); } } } The above code was tested using the JavaSoft BeanBox utility. The BeanBox provides a JavaBean test container in which new beans can be inserted and configured to work with other JavaBeans. Iteration #1 - Java's Default Reflection The first iteration of this project uses the JavaBean's default behavior for reflection. This means that no BeanInfo class will be created. By using the design pattern described in the JavaBean specification, reflection handles this introspection processing for you. The desired result is to have the Demeter/Java system create the JavaBean as part of its processing rather then requiring the programmer to manually edit the source code. In order to achieve this result, the sequence of steps should be: 1) The class dictionary (.cd) and behavior (.beh) files are created. 2) The new aspect file for JavaBeans (.bean) is created 3) Demeter/Java is invoked with the -aspect compile parameter in order to create aspect (.asp) files 4) The JavaBeans 'engine' is invoked to translate the .bean file into an aspect (.asp) file 5) The Weaver is invoked to create the java source code. JavaBean Aspect Language (javabean.cd) The JavaBean Aspect Language borrows heavily from the weaver's syntax. The intention is to consolidate in one place all of the changes required in order to create a JavaBean using the Demeter/Java system. Most of the files required to create a JavaBean will already exist either as java source or as an aspect file. The .bean file will provide the guidance for creating a JavaBean manifest file, indicating Bound/Constrained properties, creating events and event listeners. Shown below is the class dictionary (JavaBean.cd) for the JavaBeans aspect language. JavaBean.cd /* * JavaBean Aspect class dictionary */ import java.io.*; import java.util.*; JavaBean = CodeFrags *EOF* . CodeFrags ~ {CodeFrag} . CodeFrag = AdjustItem . AdjustItem : BeanList | ImplementInterface | *lookahead* (@ "create:" ("public"|"final"|"abstract")* "class" @) AddClass | *lookahead* (@ "create:" ("public"|"final"|"abstract")* "interface" @) AddInterface | ListenerSupport | EventListenerSupport | AugmentProperty | BroadcastEvent | FireEvent | RenameMethod . /* * BeanList */ BeanList = "beanlist:" ClassNames ";" . ClassNames ~ ClassName { "," ClassName } . /* * ImplementInterface */ ImplementInterface = "implement:" DottedName "in:" ClassName ";" . /* * Property Listener Support */ ListenerSupport = "add:" TypeOfListener "in:" ClassName ";" . TypeOfListener : BoundListener | ConstrainedListener . BoundListener = "RegisterBoundListener" . ConstrainedListener = "RegisterConstrainedListener" . /* * Event Listener Support */ EventListenerSupport = "event_var: " AddEventVar AddEventType "in:" ClassName ";" . AddEventVar = Ident . AddEventType = Ident . /* * AugmentProperty */ AugmentProperty = "update:" DataMemberName PropertyType DataMemberType "in:" ClassName ";" . DataMemberName = Ident . PropertyType : BoundProperty | ConstrainedProperty . BoundProperty = "Bound" . ConstrainedProperty = "Constrained" . DataMemberType = Ident . //DottedName = DotList(Ident) . DottedName = Ident . ClassName = Ident . ComListOfDotList ~ DottedName {"," *s DottedName} . DotList(S) ~ S {"." S} . /* * Keywords */ Options ~ Option {Option} *s . Option : PubOpt | PrivOpt | ProtOpt | StaticOpt | FinalOpt | SynchOpt | TransOpt | VolOpt | AbsOpt. PubOpt = "public" . PrivOpt = "private" . ProtOpt = "protected" . StaticOpt = "static" . FinalOpt = "final" . SynchOpt = "synchronized" . TransOpt = "transient" . VolOpt = "volatile" . AbsOpt = "abstract" . /* * create class */ AddClass = "create:" [Options] "class" AddClassName ClassParentFeature "{" ClassParts "}" . AddClassName = Ident . ClassParentFeature = ["extends" ParentClass] ["implements" Interfaces] . ParentClass = DottedName . Interfaces = ComListOfDotList . ClassParts ~ {ClassPart} . ClassPart : TextClassPart | ParsableClassPart . TextClassPart = JavaCode . JavaCode = Text . ParsableClassPart = [Options] MemberType MemberName ClassFieldVsMethod . MemberType : ConstructorType | DataType . ConstructorType = "constructor" . DataType = DottedName [SquareBracketses] . SquareBracketses ~ SquareBrackets {SquareBrackets} *s . SquareBrackets = "[" "]" . MemberName = Ident [SquareBracketses] . ClassFieldVsMethod : IncompleteClassField | IncompleteClassMethod . IncompleteClassField : IncompleteInitField | IncompleteUninitField . IncompleteInitField = "=" JavaCode . IncompleteUninitField = ";" . IncompleteClassMethod = "(" [Arguments] ")" [MethodThrows] MethodBody . Arguments ~ Argument {"," *s Argument} . Argument = DataType VarName . VarName = Ident [SquareBracketses] . MethodThrows = "throws" SomeExceptions . SomeExceptions = ComListOfDotList . MethodBody = JavaCode . /* * AddInterface */ AddInterface = "create:" [Options] "interface" InterfaceName InterfaceParentFeature "{" InterfaceParts "}" . InterfaceName = DottedName . InterfaceParentFeature = ["extends" Interfaces] . InterfaceParts ~ {InterfacePart} . InterfacePart = [Options] DataType MemberName InterfaceFieldVsMethod . InterfaceFieldVsMethod : IncompleteInterField | IncompleteMethodSig . IncompleteInterField = IncompleteInitField . IncompleteMethodSig = "(" [Arguments] ")" [MethodThrows] ";" . /* * RenameMethod */ RenameMethod = "rename:" ClassName OrigRenameMethodSig "to:" NewRenameMethodSig ";" . OrigRenameMethodSig = [Options] [*lookahead* (@_MemberType() _MemberName() _OrigRenameMethodArgs()@) MemberType] MemberName [OrigRenameMethodArgs] . OrigRenameMethodArgs = "(" [ArgumentTypes] ")" . ArgumentTypes ~ ArgumentType {"," *s ArgumentType} . ArgumentType = DataType . NewRenameMethodSig = [Options] [*lookahead* (@_MemberType() _MemberName() _NewRenameMethodArgs()@) MemberType] MemberName [NewRenameMethodArgs] . NewRenameMethodArgs = "(" [Arguments] ")" . /* * Broadcast Event */ BroadcastEvent = "broadcast: " EventInformation "in:" ClassName ";" . EventInformation = EventMethodName EventObjectType EventListenerInfo . EventListenerInfo = EventListenerVar EventListenerType ListenerEventMethod . EventMethodName = Ident . EventObjectType = Ident . EventListenerVar = Ident . EventListenerType = Ident . ListenerEventMethod = Ident . /* * Fire Event */ FireEvent = "fire: " TargetClass TargetMethod FireMethod";" . TargetMethod = Ident . TargetClass = Ident . FireMethod = Ident . Main = . OutputVisitor = . This sample input file represents the JavaBeans aspect changes needed to generate a bean from the original container source code. Container.bean - sample of new aspect langauge for JavaBeans // Container.bean beanlist: Container ; implement: Serializable in: Container ; add: RegisterBoundListener in: Container ; add: RegisterConstrainedListener in: Container ; update: TestNumber Bound Integer in: Container ; update: _initial Constrained Integer in: Container ; create: public class ContainerEventObject extends EventObject { private long timeOfEvent = (@ 0; @) public constructor ContainerEventObject(Object o) (@ super(o); timeOfEvent = System.currentTimeMillis(); @) } create: public interface ContainerListener extends EventListener { void handleContainerEvent(ContainerEventObject ceo) ; } event_var: contListeners ContainerListener in: ClassName ; fire: Container set_capacity broacastContainerEvent ; broadcast: ContainerEvent ContainerEventObject containerListeners ContainerListener handleContainerEvent in: Container ; rename: Container constructor Container ( ) to: public constructor Container ( ) ; Changes to Demeter/Java to create a JavaBean Introspection - This iteration of the project recommends using reflection, the default method of Introspection. In order to create code in accordance with the JavaBeans specification, the only change required would be for Demeter/Java to change its accessor method naming pattern to follow the JavaBean design pattern as described below. By implementing this design pattern, Reflection is handled automatically by Java. There are four types of properties defined in the JavaBeans specification (simple, indexed, bound and constrained). Properties are accessed by getter/setter methods within the owning component. The accessor method prototypes as defined by the JavaBean specification are: public PropertyType get(); // simple getter method public void set(PropertyType var); // simple setter method public PropertyType get(int index); // indexed getter method public void set(int index, PropertyType var); // setter method public PropertyType[] get(); // indexed getter method public void set(PropertyType[] var); // indexed setter method Demeter/Java accessor method prototypes use the following pattern: public PropertyType get_(); // simple getter method public void set_(PropertyType var); // simple setter method Using the current naming convention with JavaBeans, reflection returns _PropertyName, with the leading underscore as the property name rather than the correct value - PropertyName. Properties Simple Properties Simple properties require no changes for JavaBeans. These properties provide the getter and setter methods to provide access to the property values. Indexed Properties Indexed properties are supply support for arrays of properties. No changes are required for JavaBeans. Bound Properties Bound Properties trigger an event whenever their value changes. Each time its value changes, the property fires a PropertyChange event, which contains the property name, the old value and the new value. See the example below. public void setTestNumber(int newNum) { int old = testnumber; testnumber = newNum; changes.firePropertyChange("testnumber", new Integer(old), new Integer(testnumber) ); } JavaBean aspect syntax: update: "Bound" in: ; Constrained Properties Constrained Properties are similiar to Bound properties but more restrictive. Constrained Properties allow other objects to veto a constrained property change. Before changing a value, the contrained property fires a vetoableChange event that informs all registered listeners of the property being changed, the old value and the new value. A listener can veto the change simply by throwing a PropertyVetoException in response to receiving the vetoableChange event. See the example below for the processing of a constrained property. public void set_initial(Integer new_initial) { try { Integer oldInitial = initial; vetos.fireVetoableChange("initial", oldInitial, new_initial); initial = new_initial; changes.firePropertyChange("initial", oldInitial, initial); } catch (PropertyVetoException e) { System.out.println("set_initial received a veto!"); } } JavaBean aspect syntax: update: "Constrained" in: ; Bound and Constrained Properties also require the addition of a small block of code for the purposes of registering and unregistering 'property change' listeners. These property change listeners are other components that interested in being notified when properties in the bean are modified. The code below was added to the Container bean to register and unregister listeners for Bound and Constrained Properties. /** internal fields for PropertyChange processing */ private PropertyChangeSupport changes = new PropertyChangeSupport(this); private VetoableChangeSupport vetos = new VetoableChangeSupport(this); /** regular PropertyChangeListners methods */ public void addPropertyChangeListener(PropertyChangeListener l) { changes.addPropertyChangeListener(l); } public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l); } /** Vetoable PropertyChangeListners methods */ public void addVetoableChangeListener(VetoableChangeListener l) { vetos.addVetoableChangeListener(l); } public void removeVetoableChangeListener(VetoableChangeListener l) { vetos.removeVetoableChangeListener(l); } JavaBean aspect syntax: add: in: ; Events - in order to implement events in JavaBeans, a new event type that inherits from java.util.EventObject must be created. Objects interested in this new EventObject must implement a new listener interface that extends the java.util.EventListener. Shown below is an example event and event listener that have been created for this project bean. public class ContainerEventObject extends EventObject { long timeOfEvent; public ContainerEventObject(Object o) { super(o);s timeOfEvent = System.currentTimeMillis(); } } public interface ContainerListener extends EventListener { void handleContainerEvent(ContainerEventObject ceo); } JavaBean aspect syntax: Event syntax: create: class extends ... (additional code here to complete the object) EventListener create: interface extends ... (additional code here to complete the object) The create event and create event listener will reuse the AddClass and AddInterface class dictionary entries respectively from the weaver. Processing of these two entries is not complete due to time contraints. The behavior files for the weaver will provide more software reuse for processing these entries. Since these two statements match the weaver syntax exactly, the JavaBean 'engine' should simply pass these statements through unchanged for processing by the weaver. In addition to the new event object and event listener interface, processing of events also requires some additional changes. Events also provide a mechanism for registering and unregistering listeners. The code added to the Container sample is shown below. // Events code private Vector containerListeners = new Vector(); public synchronized void addContainerListener(ContainerListener l) { containerListeners.addElement(l); } public synchronized void removeContainerListener(ContainerListener l) { containerListeners.removeElement(l); } JavaBean aspect syntax: event_var: in: ; Once listeners have been registered, the bean needs a way to communicate to the listeners the occurance of the new event. This processing requires a new method that creates an object of the new event type and triggers a method which has been defined for the new event listener interface. Below is the sample code added for the Container example. public void broadcastContainerEvent() { Vector l; ContainerEventObject ceo = new ContainerEventObject(this); synchronized(this) { l = (Vector)containerListeners.clone(); } for (int i = 0; i < l.size(); i++) { ContainerListener cl = (ContainerListener)l.elementAt(i); cl.handleContainerEvent(ceo); } } JavaBean aspect syntax: broadcast: in: ; The last piece of event processing is a call to the new method discussed above whenever the event occurs. In the case of the Container example, the code below the new method being triggered whenever the capacity property is modfied. // force an event just to show how to implement Events public void set_capacity(Capacity new_capacity) { capacity = new_capacity; this.broadcastContainerEvent(); } JavaBean aspect syntax: fire: ; Persistance - this requires that the Bean class implement the Serializable interface. public class Container extends Item implements Cloneable, Serializable { ... JavaBean aspect syntax: implement: in: ; Additional class changes - Two additional changes were also required in order to create a JavaBean from the Container example. 1) The class has to be made public. Due to time constrains, I was unable to implement this change in my aspect language. 2) The default constructor also had to be made public. Here again, I recommend software reuse from the weaver by using the RenameMethod class dictionary entries and behavior processing. Since the syntax is exactly the same, the JavaBean 'engine' should pass this entry through to the weaver. Due to time constrains, I only included the class dictionary entries and no code in my behavior file. Manifest file - Create a manifest file like the example below Name: Container.class Java-Bean: True I recommend that the JavaBean 'engine' provide the ability to create the manifest file for the JavaBean using the syntax shown below. The manifest file will be named "manifest". Syntax: beanlist: "," "," ";" for example: beanlist: Container ; beanlist: Container , Weight , Capacity ; JavaBean.beh - Behavior file JavaBean { traversal toAll(OutputVisitor v) { to *; } } OutputVisitor { (@ PrintWriter out; OutputVisitor(PrintWriter pw) { out=pw; } @) after BeanList (@ System.out.println("processing BeanList..."); ClassNames beanClasses; PrintWriter manifest; // set output file; try { manifest = new PrintWriter(new FileOutputStream("manifest")); } catch(IOException e) { throw new RuntimeException(e.toString()); } beanClasses = host.get_classnames(); Enumeration beanclass_enum = beanClasses.elements(); while(beanclass_enum.hasMoreElements()) { Ident name = ((ClassName)beanclass_enum.nextElement()).get_ident(); manifest.print("Name: " + name.toString() + "\n"); manifest.print("JavaBean: True\n\n"); } manifest.flush(); manifest.close(); @) after ImplementInterface (@ System.out.println("processing ImplementInterface..."); out.print("implement: " + host.get_interfacename().get_name().toString()+ " in: " + host.get_classname().get_ident().toString() + ";\n\n"); @) after ListenerSupport (@ out.print("add: to: "+host.get_classname().get_ident().toString()+" {\n"); out.print(" ("+"@\n"); // will need to see which type - bound or contrained if (host.get_typeoflistener() instanceof BoundListener) { System.out.println("processing BoundListener..."); out.print(" private PropertyChangeSupport changes = new PropertyChangeSupport(this)\n\n"); out.print(" /** Bound Property PropertyChangeListners methods \n"); out.print(" */\n"); out.print(" public void addPropertyChangeListener(PropertyChangeListener l) {\n"); out.print(" changes.addPropertyChangeListener(l);\n"); out.print(" }\n"); out.print(" public void removePropertyChangeListener(PropertyChangeListener l) {\n"); out.print(" changes.removePropertyChangeListener(l)\n"); out.print(" }\n"); } else if (host.get_typeoflistener() instanceof ConstrainedListener){ System.out.println("processing ConstrainedListener..."); out.print(" private VetoableChangeSupport vetos = new VetoableChangeSupport(this)\n\n"); out.print(" /** Vetoable PropertyChangeListners methods \n"); out.print(" */\n"); out.print(" public void addVetoableChangeListener(VetoableChangeListener l) {\n"); out.print(" vetos.addVetoableChangeListener(l);\n"); out.print(" }\n"); out.print(" public void removeVetoableChangeListener(VetoableChangeListener l) {\n"); out.print(" vetos.removeVetoableChangeListener(l);\n"); out.print(" }\n"); } //out.print(" @"+") in: "+host.get_classname().get_ident().toString()+" ;\n\n"); out.print(" @"+")\n}\n\n"); @) after AugmentProperty (@ System.out.println("processing AugmentProperty..."); out.print("add:\n"); out.print(" ("+"@\n"); out.print(" "+host.get_datamembertype().get_ident().toString()+ " old"+host.get_datamembername().get_ident().toString()+ " = " +host.get_datamembername().get_ident().toString()+" ;\n"); if (host.get_propertytype() instanceof ConstrainedProperty) { out.print(" veto.fireVetoableChange("+host.get_datamembername().get_ident().toString()+",\n"); out.print(" ("+host.get_datamembertype().get_ident().toString()+")"+ "old"+host.get_datamembername().get_ident().toString()+",\n"); out.print(" ("+host.get_datamembertype().get_ident().toString()+")"+ host.get_datamembername().get_ident().toString()+");\n\n"); } out.print(" changes.firePropertyChange("+host.get_datamembername().get_ident().toString()+",\n"); out.print(" ("+host.get_datamembertype().get_ident().toString()+")"+ "old"+host.get_datamembername().get_ident().toString()+",\n"); out.print(" ("+host.get_datamembertype().get_ident().toString()+")"+ host.get_datamembername().get_ident().toString()+");"); out.print("\n @"+")"); out.print(" in: " + host.get_classname().get_ident().toString() + ";\n\n"); @) after EventListenerSupport (@ System.out.println("processing EventListenerSupport..."); out.print("add: ("+"@\n"); out.print(" // Event Listener code\n"); out.print(" private Vector "+host.get_addeventvar().get_ident().toString()+ " = new Vector();\n"); out.print(" public synchronized void add"+host.get_addeventtype().get_ident().toString()+ "("+host.get_addeventtype().get_ident().toString()+" l) {\n"); out.print(" "+host.get_addeventvar().get_ident().toString()+".addElement(l);\n"); out.print(" }\n"); out.print(" public synchronized void remove"+host.get_addeventtype().get_ident().toString()+ "("+host.get_addeventtype().get_ident().toString()+" l) {\n"); out.print(" "+host.get_addeventvar().get_ident().toString()+".removeElement(l);\n"); out.print(" }\n\n"); @) after AddClass (@ System.out.println("processing AddClass..."); out.print("\n add: newclass\n try software reuse (borrow this processing from the weaver)\n\n"); @) after AddInterface (@ System.out.println("processing AddInterface..."); out.print("\n add: newinterface\n try software reuse (borrow this processing from the weaver)\n\n"); @) around FireEvent (@ System.out.println("processing Fire..."); out.print("after: "); subtraversal.apply(); @) around TargetClass (@ out.print(host.get_ident().toString()+"."); @) around TargetMethod (@ out.print(host.get_ident().toString()+"\n"); @) around FireMethod (@ out.print(" ("+"@ this."+host.get_ident().toString()+"(); @"+")\n\n"); @) around BroadcastEvent (@ System.out.println("processing Broadcast..."); subtraversal.apply(); @) around EventMethodName (@ out.print("add: ("+"@\n"); out.print(" public void broadcast"+host.get_ident().toString()+"() {\n"); out.print(" Vector l;\n"); @) around EventObjectType (@ out.print(" "+host.get_ident().toString()+" eo = new "+ host.get_ident().toString()+"(this);\n"); @) around EventListenerVar (@ out.print(" synchronized(this) {\n"); out.print(" l = (Vector)"+host.get_ident().toString()+".clone();\n }\n"); @) around EventListenerType (@ out.print(" for (int i=0; i "); @) around RenameMethod (@ System.out.println("processing RenameMethod..."); out.print("\nrename: "); out.print(" (reuse some logic from weaver)\n\n"); subtraversal.apply(); @) } Main { (@ static JavaBean parsed_input; static PrintWriter out; public static void main(String[] args) throws ParseError { // parse input file; if(args.length != 1) throw new IllegalArgumentException("Bad arguments to JavaBean engine"); try { FileInputStream infile = new FileInputStream(args[0]); parsed_input = JavaBean.parse(infile); } catch(FileNotFoundException e) { throw new RuntimeException(e.toString()); } // set output file; try { Main.out = new PrintWriter(new FileOutputStream("gen/javabean.asp")); } catch(IOException e) { throw new RuntimeException(e.toString()); } // generate weaver code; out.print("javabean:\n\n"); OutputVisitor v_out = new OutputVisitor(Main.out); parsed_input.toAll(v_out); // cleanup; out.flush(); out.close(); } @) } Sample Output - gen\javabean.asp javabean: implement: Serializable in: Container; add: to: Container { (@ private PropertyChangeSupport changes = new PropertyChangeSupport(this) /** Bound Property PropertyChangeListners methods */ public void addPropertyChangeListener(PropertyChangeListener l) { changes.addPropertyChangeListener(l); } public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l) } @) } add: to: Container { (@ private VetoableChangeSupport vetos = new VetoableChangeSupport(this) /** Vetoable PropertyChangeListners methods */ public void addVetoableChangeListener(VetoableChangeListener l) { vetos.addVetoableChangeListener(l); } public void removeVetoableChangeListener(VetoableChangeListener l) { vetos.removeVetoableChangeListener(l); } @) } add: (@ Integer oldTestNumber = TestNumber ; changes.firePropertyChange(TestNumber, (Integer)oldTestNumber, (Integer)TestNumber); @) in: Container; add: (@ Integer old_initial = _initial ; veto.fireVetoableChange(_initial, (Integer)old_initial, (Integer)_initial); changes.firePropertyChange(_initial, (Integer)old_initial, (Integer)_initial); @) in: Container; add: newclass try software reuse (borrow this processing from the weaver) add: newinterface try software reuse (borrow this processing from the weaver) add: (@ // Event Listener code private Vector contListeners = new Vector(); public synchronized void addContainerListener(ContainerListener l) { contListeners.addElement(l); } public synchronized void removeContainerListener(ContainerListener l) { contListeners.removeElement(l); } after: Container.set_capacity (@ this.broacastContainerEvent(); @) add: (@ public void broadcastContainerEvent() { Vector l; ContainerEventObject eo = new ContainerEventObject(this); synchronized(this) { l = (Vector)containerListeners.clone(); } for (int i=0; i rename: (reuse some logic from weaver) In some cases, the output from the JavaBean 'engine' does not currently match input the weaver. I recommend that the weaver be modified to allow the syntax of add: (@ ... @) in: classname. This syntax allows you to include blocks of code, variables, etc with minimal processing required. Known bugs and future implementation ideas Bugs - Incomplete processing of some of the aspect language. - ImplementInterface processing does not currently handle dotted names. - I was not able to find a way to position new code within an existing method. In some cases, to create a bean, new code must be added to existing methods. It appears that the before: and after: sytnax from the weaver attempts to do this but I was until to determine exactly how that works. Future implementation ideas - Provide support for explicit specification of a BeanInfo object. Questions: 1. Did you change the generated Java code ? The only changes I made were during the inital phase of the project where I was supposed to create a bean from the generated code. 2. If you had the priviledge to have one of the graduate students as a host or had interactions with the teaching assistance, feel free to provide an evaluation of their performance. I had a couple of interactions with Josh Marshall while trying to get grasp of aspect programming and how the weaver works. Josh was very helpful. E-mail was responded to very promptly and we even had a phone conversation to go over the processing of the weaver. This was very helpful because some times it is hard to ask and answer technical questions through e-mail. Invariably, an answer spawns another question and following that path in e-mail can take forever. References: JavaBeans for Dummies by Emily VanderVeer JavaBeans Spec - found at http://java.sun.com/beans/docs/spec.html JavaBeans Tutorial - found at http://java.sun.com/beans/docs/Tutorial-Sep97.pdf