Last updated January 29, 2011 21:14, by John Catherino
Feedicon  

Overview of the cajo SDK



The cajo project SDK provides a small yet detailed example of two important, yet independent concepts. One is to easily develop Rich Internet Applications; which can also be launched as full-window Applets, and via WebStart. The cajo library takes care of presenting them in the browser, and as remote desktop applications. The optional remote Graphical User Interfaces are just plain-old JComponents; your Swing learning investment is preserved. The other is to provide a simple and powerful approach to dynamic distributed computing.

The simple architecture of the SDK is the foundation; no detailed knowledge of the inner workings of the cajo library is necessary, as it makes no significant impact on the source code. Everything is plain-old Java, no special tools are needed, nor IDE plug-ins, nor other 3rd party libraries; not even a servlet container.

The SDK is intended as a quick, but comprehensive introduction into the application and functionality of cajo. The cajo framework is an inherently peer to peer, meaning no central servers are used, and thereby no central points of failure.


Architectural Overview

There are three fundamental components:

1. Services A Service is simply an ordinary Java object, all of whose public methods are callable, from other remote objects. No special annotations are needed, nor XML files, it is fully transparent. A service object furnishes interesting functionality that other objects can use. A service may even use other remote service objects to fulfill its functionality. A service is used to distribute functional modules across multiple JVMs for improved performance, scalability, and reliability.

2. Controllers A Controller is an object requested by a remote object, to operate within its JVM. The controller arrives with a remote reference to its sending service, and has no reference to its receiving object, as it is strictly a servant. A controller is used to minimise network network traffic, and improve performance; particularly when a lot of invocations are needed on the sending service, or a lot of processing unique to the receiver is necessary. When the receiving object nulls its reference to a controller, or lets the reference go out of scope; the controller is garbage collected automatically. A service can furnish as many controllers as it wishes, including none, but typically only one.

3. Agents An agent is an object sent to a remote object, to operate within its JVM. In essence it is the logical inverse of a controller. An agent arrives with a remote reference to its sending service, and a local reference to its receiving object. The sending service, in turn, receives a remote reference to its agent. An agent is used to minimise network traffic, and improve performance; particularly when a lot of invocations are needed on the receiving object, or the data sets being manipulated at the receiver are large. When the sending object nulls its reference to an agent, or lets the reference go out of scope; the agent is garbage collected automatically. A service can provide as many types of agents as it wishes, including none.

In both cases, the receiving object views controllers and agents as local "intelligent stand-ins" for the sending services. An object can, if necessary, interact with a remote service neither using controllers, nor accepting agents; or also in addition to using them. Controllers and agents can also in turn, connect with other services, request controllers, and send agents, as needed to fulfill their functionality.


Sending Service <---------- Controller <--- Receiving Object (requested by)
Sending Service <--------------> Agent ---> Receiving Object (sent to)

Ancillary components:

A cajo object is provided to all components, this allows them to look up services needed for assistance: dynamically by interface, as well as statically by server address.


Making Connexions

An object obtains a reference to a remote object typically in one of four ways:


Class Diagramme

The following is a schematic of the cajo SDK architecture, fully implemented:

cajo SDK class diagramme]


Source Examples

(excerpted from the SDK)

Example Service:

package service;

public class Service extends cajo.sdk.AbstractService implements IService {
   public Service(String name) throws Exception {
      super("controller.Controller", name); // service specific controller
      // use the cajo member object to connect to other services as needed
   }
   // this method can be called by remote JVMs
   public String foo(String bar) {
      if (bar != null) return "foo invocation completed for " + bar;
      throw new NullPointerException("null arg"); // for illustration
   }
}

Example Controller:

package controller;

import view.View;
import java.util.concurrent.Future;

public class Controller extends cajo.sdk.AbstractController implements IController {
   // This is the controller's interface to its sending service.
   private interface homeInterface { // example interface
      String foo(String baz); // synchronous call
   // Future<String> foo(String baz); // or call it asynchronously
   }
   // The reference to the sending service, to make callback invocations.
   private final HomeService homeService;
   // The reference to the the view object to listen to, and modify at runtime.
   private transient View view;
   public Controller(Object homeService) {
      this.homeService = proxy(homeService, HomeService.class);
   }
   // this method is called when the controller arrives at the remote JVM
   public void init(gnu.cajo.Cajo cajo) {
      super.init(cajo);
      System.out.println("Controller arrived at service!");
      // use the cajo object to connect to other services as needed
   }
   // this method is called by the receiving JVM to get the GUI view
   public cajo.sdk.AbstractView getView() throws java.io.IOException {
      // the view is hard-coded it could be read from a file, or a property
      view = new View();
      view.center.display("service call result:\n > ");
      try { view.center.display(baz("hello")); } // for illustration
      catch(Exception x) { x.printStackTrace(); }
      return view;
   }
   // this example method can be called by the sending and receiving JVM
   public String baz(String bar) {
      // return homeService.foo(null); // test error handling
      return homeService.foo(bar).toString();
   }
}

Example Agent:

package agent;

import java.util.concurrent.Future;

public class Agent extends cajo.sdk.AbstractAgent implements IAgent {
   // This defines the agent's interface to its sending service.
   private interface HomeService {
   // Object foo(String bar); // synchronous call
      Future<Object> foo(String bar); // or call it asynchronously
   }
   // This defines the agent's interface to its receiving service.
   private interface LocalService {
      String baz(String foo); // synchronous call
   // Future<String> baz(String foo); // or call it asynchronously
   }
   // This is the reference on which the agent will communicate with
   // its sending service.
   private final HomeService homeService;
   // This is the reference on which the agent will communicate with
   // its receiving service.
   private LocalService localService;
   public Agent(Object homeService) {
      this.homeService = proxy(homeService, HomeService.class);
   }
   // this method is called when the agent arrives at the receiver
   public void init(Object localService, gnu.cajo.Cajo cajo) {
      super.init(cajo, localService);
      this.localService = proxy(localService, LocalService.class);
      System.out.println("Agent arrived at service!");
      // use the cajo object to connect to other services as needed
   }
   public String bar(String baz) { // this method can be called by the sender
      try { homeService.foo("hello from agent"); } // can talk to sender
      catch(java.rmi.RemoteException x) { /* call home can fail */ }
      return localService.baz(baz).toString(); //can talk to receiver
   }
}


Conclusion

Security: Accepting mobile code for controllers and agents can be risky. A rogue object could hobble or even crash the receiving JVM simply by allocating lots of memory for instance. For this reason, acceptance of mobile controllers and agents is disabled by default. It should go without saying; an internet facing server should not accept mobile code. However inside the firewall, other servers can enable it by calling a single line of code.* (An example of this is included in the SDK)

[*If the controllers and agents code are in the receiving server's classpath, then they are not mobile by definition, rather quite static, and can be used without having to enable acceptance of mobile code.]

Note: As you may have surmised; a server can send its controller, and agents, without having to accept mobile code itself.

The cajo framework is just as intuitive and easy to use to as it looks. All it takes to get started is to download the very small SDK(125kB). The example source is openly licensed under the Apache Licence 2.0, and compiles and runs right out of the box. The autogenerated javadoc provides extremely detailed documentation. Try the SDK as a starting template; modify the example classes as you wish, and you can have your own distributed system up and running very quickly.

 

cajo uses GNU FDL]
© 1999 - 2011 John Catherino. Some rights reserved.
cajo: free dynamic transparent distributed computing]