The goal of this section is to introduce advanced features (especially clusturing features and how to program new aspects) of JAC and show how applications can be programmed without UMLAF. Indeed, even if UMLAF is useful, JAC applications (written in pure Java) can be programmed with other IDEs. Moreover, UML is still beta and unstable. If you really use JAC, you'd rather use a stable IDE.
We have chosen very simple examples. More interesting ones can
be found in the JAC tarball (src/org/objectweb/jac/samples
). You
can also see the UMLAF source (src/org/objectweb/jac/ide
) for a
complex example.
For more general overview on the JAC's programming philosophy, see the JAC Programmer's Guide.
The base application is the functionnal or core-business
application. In other words, it is the simpliest expression of
your functionnal needs. Here, we would like to have a class that
performs some simple calculi. Wherever you want (in a directory
APP_DIR
), create a file called
Calcul.java
. The code is the following:
// Calcul.java file // A simple component definition that can perform // add and sub operations... public class Calcul { protected float value = 0; public void add(float toadd) { value+=toadd; } public void sub(float tosub) { value-=tosub; } // IMPORTANT: in JAC, each instance-field better have // a getter of the form getFieldName. public float getValue() { return value; } // IMPORTANT: in JAC, each instance-field better have // a setter of the form setFieldName. public void setValue(float value) { this.value=value; } // Program entry-point public static void main( String[] args ) { // Actually launch the calcul program by creating an // instance of the Calcul class that waits to be used... Calcul myCalcul = new Calcul(); } }
Note that the need of the getters and setters comes from the aspects that will use them later-on.
In JAC, any application must have a program descriptor that we put in a *.jac file. This file declares the new application and the supported aspects to the JAC system.
// calcul.jac file applicationName: calcul launchingClass: Calcul
The launching class is a regular class that contains a static
main
method. For simplicity, we put the main()
in the Calcul
class.
Compile these files like you are used to doing in Java
(*.jac
and *.acc
files do not need to
be compiled).
javac Calcul.java
When compiled, run the JAC server and indicate to launch the calcul sample.
cd <jac_dir> ## LINUX ## java -jar org.objectweb.jac.jar -R . -C <APP_DIR> APP_DIR/calcul.jac ## WINDOWS ## java -jar org.objectweb.jac.jar -R . -C <APP_DIR> APP_DIR\calcul.jac
Since no aspects are configured for the application, All you will see is this:
--- Launching Application calcul --- JAC system shutdown: notifying all ACs... Bye bye.
This is not very usefull, but is it prooves that JAC is working on your system.
org.objectweb.jac.core.rtti.RttiConf is a core aspect that allows the programmer to define extra type information that will be used by other aspects at runtime.
For instance, it can be very useful for other aspects to be
aware that a given method modify the object's state. Since JAC
can detect that automatically, we do not need to add specific
configurations for our sample application. All you need to do is
create an empty file rtti.acc
.
The GUI aspect (extends ClassAppearenceGuiConf, FieldAppearenceGuiConf, MethodAppearenceGuiConf, and BehaviorGuiConf) allows the programmer to define some presentation information, and to parameterize the interactions between the application objects and the user. For instance, by configuring a personal GUI aspect, a programmer can define the names of the method parameters as they will be displayed by a GUI.
For our example, create a gui.acc
file:
// configuration for the Calcul class class Calcul { // Show a button for each of these methods setMethodsOrder {add,sub}; // Set the names of the parameters of add and sub methods setParameterNames add { "Value to add" }; setParameterNames sub { "Value to sub" }; // Set a default value for add setDefaultValues add { 1 }; } // Says that all the methods of class Calcul can // be called interactively askForParameters "Calcul"; // The GUI main window configuration window default { registerCustomized; setTitle "Calculator"; // A real simple GUI setSubPanesGeometry 1 VERTICAL { false }; // Display the object named "calcul0" in the // panel "0" of the window. setPaneContent 0 Object { "calcul0" }; }
Once you have created your ACC files, you must declare them to the
application by modifying the calcul.jac
file as
following:
// calcul.jac file applicationName: calcul launchingClass: Calcul aspects: \ rtti rtti.acc true \ gui gui.acc true
You can then launch the application with the following command:
cd <jac_dir> ## LINUX ## java -jar org.objectweb.jac.jar -R . -C <APP_DIR> -G default APP_DIR/calcul.jac ## WINDOWS ## java -jar org.objectweb.jac.jar -R . -C <APP_DIR> -G default APP_DIR\calcul.jac
And you should see a window like this:
As you can see, the calcul instance is being
introspected by the GUI aspect of JAC that offers a default view
on it. It shows the fields of the calcul0
object
(here value
, and the methods that can be
called on the object (the two buttons add
and sub
)). The pencil-like
button on the right of
the field means that you can edit the field value by calling the
field's setter. Click on it to change the value of the
value
field. The following box pops-up. Type
a new value:
When you click on OK, you can notice that the view is
automatically refreshed. Indeed, thanks to bytecode analysis,
JAC knows that setValue
is a setter for the
value
field. Thus the MVC (Model-View-Controller)
underlying framework of the GUI aspect refreshes the view.
Let us now try the add
button:
You will first notice that the default value to be added is
"1". This is because of the setDefaultValues
instruction in the gui.acc
. When you click on OK, once again the
view of the main window is automatically refreshed because JAC
detected that the add
method modifies
the field value
.
All this configuration works also with the WEB. If you launch JAC with the WEB-GUI server:
cd <jac_dir> ## LINUX ## java -jar org.objectweb.jac.jar -R . -C <APP_DIR> -W default APP_DIR/calcul.jac ## WINDOWS ## java -jar org.objectweb.jac.jar -R . -C <APP_DIR> -W default APP_DIR\calcul.jac
You will see the following output on the console:
--- Launching Application calcul --- WARNING: Resource rtti.acc not found 13:34:52.863 EVENT Starting Jetty/4.1 13:34:53.832 EVENT Started HttpContext[/jac] 13:34:53.841 EVENT Started HttpContext[/org/objectweb/jac/resources] 13:34:54.295 EVENT Started SocketListener on 0.0.0.0:8088 13:34:54.306 EVENT Started org.mortbay.http.HttpServer@100bac2 WARNING: Web server already started
Then, with you favorite web browser, go to http://localhost:8088/org/objectweb/jac/default
:
Other aspects are available and can be configured using the same process that the one depicted for the RTTI and GUI aspects.
Usable aspects aliases are declared in the org.objectweb.jac.prop
file. These aliases are used in *.jac
application
descriptors. By default, available aliases are (click on the
link to see the corresponding aspect configuration):
These aspects implementations can be found in the
org.objectweb.jac.aspects
package (see the JAC API documentation for further details).
JAC provides full support for distributed AOP (then, it is easy to implement clusturing features by configuring aspects). Basically, the deployment aspect provides a set of deployment rules that the programmer can use to deploy its application over a set of containers.
If we take again the calcul example, you may intend to launch JAC
in a distributed mode where a set of clients will access to one
unique instance of calcul (here, calcul0
) located
on a server host.
To allow this, first modify the application descriptor to tell that the application knows a set of two other containers, and activate the deployment aspect.
// calcul.jac file applicationName: calcul launchingClass: Run aspects: \ rtti rtti.acc true \ gui gui.acc true \ deployment deployment.acc true \ consistency consistency.acc true topology: //localhost/s1 //localhost/s2
This means that, including the master host (called
//localhost/s0
), your JAC system will contain three
local sites s0, s1, s2
. If you want to replicate
calcul0
on s1
and s2
,
just write an ACC file, deployment.acc
:
// replicate the calcul0 object from s0 to s1 replicate ".*s0" "calcul0" ".*s[12]";
The deployment aspect only instanciates objects on remote
servers. In order to introduce some consistency between those
replicas we must configure the consistency aspect in the
consistency.acc
file:
addStrongPushConsistency "calcul0" "MODIFIERS" ".*[0-2]";
Thus, any call to a modifier method on a site will also be called on the other ones.
Then, start 2 JAC slave servers in distributed mode.
cd <jac_dir> java -Djava.security.policy=<jac_dir>/org.objectweb.jac.policy -jar org.objectweb.jac.jar -R . -D s1 java -Djava.security.policy=<jac_dir>/org.objectweb.jac.policy -jar org.objectweb.jac.jar -R . -D s2
Then start the application in a JAC master server in a distributed mode.
cd <jac_dir> ## LINUX ## java -jar org.objectweb.jac.jar -R . -Djava.security.policy=<jac_dir>/org.objectweb.jac.policy -C <APP_DIR> -G default -D APP_DIR/calcul.jac ## WINDOWS ## java -jar org.objectweb.jac.jar -R . -Djava.security.policy=<jac_dir>/org.objectweb.jac.policy -C <APP_DIR> -G default -D APP_DIR\calcul.jac
This will show a GUI for the master server. You can also launch a GUI for the slave servers.
cd <jac_dir> java -jar org.objectweb.jac.jar -R . -G calcul@s1:default java -jar org.objectweb.jac.jar -R . -G calcul@s2:default
Now you can check that if you call any modifier method (add
,sub
or setValue
) on any of the servers, the change of
the value
attribute will spread to the other
servers.
Note that you can change a configuration file and reload it at runtime for any server. For instance, to reload the consistency aspect (in order to change its configuration at runtime).
cd <jac_dir> java -jar org.objectweb.jac.jar -R . -a calcul consistency s0
Once you have fully understood how it works, you are ready to
play with more advanced features. For instance, you can use the
load-balancing aspect (see org.objectweb.jac.distribution.aspects.LoadBalancingConf)
to increase the load capability of your applications. For
instance you can program a load-balanced calculator only by
slightly changing the distribution aspects, and by adding a new
ACC (load-balancing.acc
):
// deployment.acc file // replicate calcul0 on s1 and s2 replicate ".*s0" "calcul0" ".*s[12]";
// consistency.acc addStrongPushConsistency "calcul0" "MODIFIERS" ".*[1-2]";
// load-balancing.acc // object methods frontend backends addRoundTripLoadBalancer "calcul0" "ALL" ".*s0" ".*[1-2]";
With these three configurations, the calcul instance of
s0
is a load-balancer that performs a rountrip
load-balancing algorithm to dispatch on s1
and
s2
.
With the JAC software, we furnish a bunch of useful aspects that can be advantagely used to program distributed applications. However, since JAC is a young project and that we cannot think in advance of all the possible uses you can make out of such a software, most of the aspects we provide may lack useful configuration method or may not work fine for specific usages.
Next, we show the code for an aspect that checks that the
add
or sub
method invocations on the
calcul0
instance so that it raises an error if the
added value is greater than 100 or if the substracted value is
greater than 50.
// CheckingAC.java import org.objectweb.jac.core.*; public class CheckingAC extends AspectComponent { // at instantiation-time, you should define the pointcuts public CheckingAC() { // this pointcut will make the add method of calcul0 // wrapped by an instance of checkingWrapper (an // inner wrapper of this aspect), and more specifically // by the wrapping-method "checkAdd" pointcut("calcul0","Calcul","add(float):void", CheckingWrapper.class.getName(),"checkAdd", null,false); // same principles for sub... pointcut("calcul0","Calcul","sub(float):void", CheckingWrapper.class.getName(),"checkSub", null,false); } // then define the wrappers (if you think that these // wrappers can be used by other aspect, you can make // them public within a standalone class-file) public class CheckingWrapper extends Wrapper { public CheckingWrapper(AspectComponent ac) { super(ac); } // see the programmer's guide for details on the // wrapping methods semantics... public Object checkAdd(Interaction interaction) throws Error { if (((Integer)interaction.args[0]).intValue()>100) { throw new Error("bound excedeed when calling add!"); } return proceed(interaction); } public Object checkSub(Interaction interaction) throws Error { if (((Integer)interaction.args[0]).intValue()>50) { throw new Error("bound excedeed when calling sub!"); } return proceed(interaction); } } }
Note that this aspect is very specific since it can only be applied
to the calcul0
instance. To program generic
aspects, you may refer to the JAC programmer's
Guide. Configuration methods are a first step towards
genericity since they allow the system to dynamically create the
pointcuts with the interpretation of the Aspect-Component
Configuration (ACC) files (*.acc
).
In this case, you can parametrize the creation of the pointcuts
instead of hardcoding them in the aspect-component
constructor. For instance, the following two files have exactly
the same effect than the previous hardcoded aspect-component
except that a simple change of the object's name in the
configuration file allows the user of this aspect component to
make it work on other instances of the Calcul
class.
// CheckingAC.java: public class CheckingAC extends AspectComponent { // at configuration-time (just after the instantiation), // this configuration method can be called to define the // pointcuts public void checkCalcul(String name) { // this pointcut will make the add method of the // instance named "name" wrapped pointcut(name,"Calcul","add(float):void", CheckingWrapper.class.getName(),"checkAdd", null, false); // same principles for sub... pointcut(name,"Calcul","sub(float):void", CheckingWrapper.class.getName(),"checkSub", null, false); } // then, same as the hardcoded aspect... Class CheckingWrapper extends Wrapper { (...) } }
// my-checking.acc: checkCalcul "calcul0";
NOTE: to be able to use and declare your new aspect
component in *.jac
file, we recommand that you
first declare them in the global org.objectweb.jac.prop
file located in
$JAC_ROOT
. Add the following line in the
org.objectweb.jac.acs
property of org.objectweb.jac.prop
(at the end of the file), and do not
forget to add a \
at the end the preceding line!!!
If you do not declare aspects in org.objectweb.jac.prop
, you can still use the fully
qualified name of the aspect component's class
(<myPackage>.<MyAspectComponent>
)
checking CheckingAC
You should also specify that the CheckingWrapper
must be called after the InputWrapper
of the GUI aspect because
otherwise, when the method is called from the GUI, the check will
occur before the user has entered the value. Just add the
following line in org.objectweb.jac.prop
, in the
org.objectweb.jac.comp.wrappingOrder
property, just after the org.objectweb.jac.aspects.gui.InputWrapper
line.
CheckingAC$CheckingWrapper \