Java Beans for Demeter/Java
Brandon W. Busacker
Inception
The ability to transform any application into a self-contained component adds tremendous flexibility and power. Not only does it allow the application to interact with other components, it also makes it more convenient for the programmer to take advantage of and use Demeter/Java applications. With this motivation, the task was undertaken to provide this functionality to Java applications generated using the Demeter/Java tools. It was also suggested that an aspect language be designed that would simplify the use of event receivers and senders in the Demeter/Java framework. It is difficult to determine a fair scope for this project so for now a preliminary goal is to:
Elaboration
Requirements Risks
From the email and web page information about the Java Beans project I derived the following Use Case:
A Demeter/Java user/developer would like some way of preparing his program so that it can interact with other applications in a Java Bean environment. The programmer will be able to specify which events are listened to and sent. By using the public and private modifiers the programmer will be able to control which properties and methods will be exported. It will be vital to the success of this project to make certain that the resulting Java code adheres to the Java Bean Specification and Design Patterns so that any Java Beanbox-like application will be able to determine all exported methods, events, and properties.
Technological Risks
All the software tools including Java, Demeter/Java, JavaCC, etc. are extremely new products. In addition, with Java and the Java Specification continuing to evolve there is potential for design decisions made now to be deprecated in the future. Technological risks are very high for this project.
Skills Risks
My first exposure to Java and Demeter/Java was 2 months ago at the beginning of this class. I have little to no experience with Java Beans but have an interest in expanding my knowledge. I am mainly a C programmer with some intermediate knowledge of C++ and object-oriented programming. With such limited knowledge of Java and Java Beans, skills risks are also very high.
Political Risks
The only potential political risks would be the result of economic pressures on my company. These pressures would cause my company to discontinue all educational programs for its employees such as NTU. Although the DRAM market is bleak, the chances of this happening are slight.
Baseline Architecture and Plan
With the above-mentioned risks, it seems clear that Skill and Technology pose the greatest threat to the success of this project. I expect to take 1 man-week (40 hours) to complete the first iteration including:
This will lead us (if time permits) into the second iteration of 1 man-week (40 hours) which will include:
Construction
Iteration 1
After glancing for a relatively brief time at the Java Beans specification and other supporting material and building a few simple Java Beans, I was ready to begin the design of a Demeter/Java Bean. During this process it became clear that with current Java Bean Technology only one class could be declared as a bean for a given application. If multiple collaborating classes are present (as in the instance of most Demeter/Java programs), then they would all have to communicate via one individual class (the Java Bean).
With this in mind, I took the approach to create such a class by hand. This class would be the interface for all underlying Demeter/Java classes. The Demeter/Java program that I chose to work with was the capacity-check program. Since a Java Bean does not use a "main" program, the "main" program section of the "statistics.beh" file was removed. In its place, I created a CapacityBean class as shown in figure 1 below:
CapacityBean
{
(@ private Container a; private String filename;
private Boolean overCapacity;
private PropertyChangeSupport changes = new PropertyChangeSupport(this);
@)
init
(@ setFilename("c:\\statistics.input");
@)
(@
public void checkCapacity() throws Exception
{
setOverCapacity(a.checkCapacity());
}
public void setFilename(String newFilename)
{
filename = newFilename;
try
{
java.io.FileInputStream fin = new java.io.FileInputStream(filename);
a = Container.parse(fin);
}
catch (Exception e)
{
return;
}
}
public String getFilename()
{
return filename;
}
public void setOverCapacity(Boolean newOverCapacity)
{
Boolean oldOverCapacity = overCapacity;
overCapacity = newOverCapacity;
changes.firePropertyChange("overCapacity", oldOverCapacity, newOverCapacity);
}
public void addPropertyChangeListener(PropertyChangeListener listener)
{
changes.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener)
{
changes.removePropertyChangeListener(listener);
}
@)
}
The things that changed between the "main" program and the CapacityBean were:
package capacitycheck;
Additional enhancements could have been added, but as a test of a typical Demeter/Java program as a Java Bean, this seemed to contain most of the features of the capacity-check standalone Demeter/Java program.
Once the java code was generated, I attempted to compile it. A couple of slight modifications had to be made which could not be specified in the ".beh" file. Due to the length of explicit java.beans.* names and because it was unnecessary to include the import java.beans.* directive in all files, I added the following line to the Java code myself.
import java.bean.*;
In addition, even when the code compiled it still would not run as a bean until the CapacityBean class’s visibility as well as the default constructor's visibility was made public. Once this was all accomplished, the Java Bean was ready to be encapsulated into a jar file. The jar file consists of the Java Bean class and all supporting classes as well as a manifest file. The format of the manifest file is rather simple. In our case, it included the following lines:
Name: capacitycheck/CapacityBean.class
Java-Bean: True
The Java Bean was now placed in the jars directory where the BeanBox application could introspect and display it in its list of Java Beans when the Beanbox application was run. The plan was then to graphically establish a connection between a push button and the CapacityBean. When the push button was pressed, the checkCapacity method of the CapacityBean would be executed. This was tested and verified to work. Another connection was established between the juggling bean and the propertyChange event of the overCapacity property of the CapacityBean. When the overCapacityProperty changed it would stop the juggling bean from juggling. Below is a screen dump of this organization inside the Beanbox:
At this point, a serious defect in the Java Bean System was encountered. When the CapacityBean was selected and dragged onto the center design area, its default constructor method was executed. In our case, this called the setFilename method that sets the default filename and then begins parsing the default input file to create the dynamic Container. For some reason a certain section of code inside one of the JavaCC libraries hung. Below is a snippet of code from the section where this anomaly occurs:
final public Ident _Ident() throws ParseError {
Token t;
t = jj_consume_token(IDENTIFIER);
pw.println(t.image); pw.flush();
return new Ident(t.image);
}
This code printed out "apple" correctly (the first container item) before returning but never returned to the calling routine. To remedy this problem, different versions of the JavaCC product were triedd as well as different Java Development Kits. Even with these attempts, the problem still existed. Either the code gets hung allocating the new Ident or there is a bug in the generation of the return address for this routine. Since I was unable to resolve this issue, I was unable to verify that this approach would work correctly. However, I am confident, that this approach is viable and an excellent approach to exploiting the Demeter/Java tools and the dynamic instantiation approach that Demeter/Java takes.
Note: The statistics.beh and statistics.cd files will be included in the appendix of this document.
Iteration 2
In iteration two we begin the formulation of an aspect language. Below is the text from the input file we will use with the Java Bean Builder to generate aspect files that will later be weaved together using the weaver.
BeanBuilder CapacityBean
{
AddBoundProperty(overCapcacity);
}
I kept the language fairly simple due to time constraints. Currently the language only supports adding bound properties. In future versions one could add constrained properties as well as vetoable properties, etc. In my language you would just add another function such as AddConstrainedProperty(theProperty, constraints, …);
To use this tool you run the Demeter/Java tool with the –aspect flag and then run the BeanBuilder tool. You then run the weaver which I didn’t have access to. You then fix all the little quirks with Demeter/Java like the visibility of the main bean class and default constructor (from package to public) as well as any names of properties that you want the Beanbox to be able to see. You then compile the code using both the Java compiler as well as JavaCC. Finally you place all of the class files and the generated manifest file into a jar file using the jar utility. Copy the resulting jar file to the jars directory where the Beans Development Kit (BDK) is installed and run Beanbox to play with the resulting bean.
There is a compile.bat file in the Iteration 2 directory to build the BeanBuilder and a run.bat to run the BeanBuilder program using the example Capacity.bean as the input file. This will generate a CapacityBean.mft manifest file and a CapacityBean.asp file.
In the future it would be better to put any package statements in the aspect file so that the BeanBuilder can generate a correct manifest file. Otherwise, the manifest file will need to be generated by hand which is another option.
I would like to see Demeter/Java use the design patterns of Java so that introspection of Java Beans will work correctly without being forced to create a BeanInfo interface. Demeter/Java would use setProperty() and getProperty() instead of set_property() and get_property() making all Demeter/Java Applications more Java-friendly.
Transition
This phase of the development was never reached. All developed software and this document were finalized for submission in this phase.