My favorites | Sign in
Project Logo
                
Feeds:
Groups:
People details
Project owners:
  alexmilowski

Xeerkat is a P2P computing framework that utilizes XMPP as a communication protocol. The basic model is that of a agent computing where an agent runs a number of services that available to peers. Each service is available over both HTTP and XMPP.

A service is a rest-style service that can communicate using any web-oriented protocol and implemented as a Restlet Application. This allows reuse of any Restlet implementation as a agent service.

An agent-oriented set of services are provided throught Restlet context to facilitate building services.

News

Status

The current code base is very stable an usable for applications. The only major missing feature is better discovery mechanisms. The current code relies upon presence management and works well in situations where every "worker" agent knows a common "broker" agent". Those workers who have the same broker agent will know about each other by periodically checking with the broker agent.

The current version is 2.1.0 and now uses an installer. Just double-click on the jar file or type:

   java -jar xeerkat-2.1.0.jar

XMPP Server

While the Xeerkat agent code will work with any XMPP server I've been frustrated by the lack of features for my needs in may other XMPP servers. As such, I built an experimental server that utilizes the same infoset processing technology and restlet engine as the client. The current version supports basic presence handling and rosters and also supports dynamic configuration of "virtual hosts" via Atom feeds.

The server requires a basic auth webservice for authentication. I use my authservice from the atomojo project.

The server does not support server-to-server connections and so does not federate with other services. It does support inter-communication between different hosts on the same server.

These documents should help the brave try out this server

History

The Xeerkat project grew out of a P2P grid computing project that originally used JXTA. Eventually issues with JXTA forced a re-examination of what technologies were needed. As each peer is actually on the edge, XMPP was a good choice for a communication technology.

The project was originally hosted as xeerkat at java.net and was moved to this project. The original code is available there with the JXTA support and that is considered the "1.0" version.

This code has been completely refactored and removes many of the restrictions to XML and opens up the ability to create P2P REST-oriented services. Underneath, XML is used as appropriate in the XMPP transport.

XMPP URI References

Xeerkat uses URIs to tunnel requests over XMPP. The general format of the URI is:

xeerkat://{sender-id}/{sender-resource}/{receiver-id}/{receiver-resource}/{path}

The final {path} part of the URI is the same path as the service over HTTP. For example, if the 'status' service is available from:

http://localhost:8080/status

then for sender 'test@talk.example.com' and agent host 'peer@talk.example.com', that same service is available (depending on resource names) at:

xeerkat://test@talk.example.com/shell/peer@talk.example.com/agent/status

You can also omit the resource of the receiver:

xeerkat://test@talk.example.com/shell/peer@talk.example.com//status

As the local client makes a connection to the XMPP server as themselves, the "host" information is the local connection and that's why the URI starts with your XMPP id.

Since XMPP messaging is asynchronous, a client can use the sxeerkat scheme to simulate a synchronous request.

Documentation

A Distributed Ping Example

The simplest test infrastructure is distributed ping where an single agent is going to "ping" other agents for a certain number of times. All Xeerkat services are just Restlet Application instances and so we need to do is write a simple Restlet Application and then package it for the agent.

The main part of a Application in Restlet is the createRoot method. Here we'll create a Router to route requests:

   public Restlet createRoot() {
      
      // Create a router for the root of the application
      Router router = new Router(getContext());

and then we'll create a main restlet for processing ping requests:

      Restlet main = new Restlet(getContext()) {
         
         public void handle(Request request, Response response) {

         }
      }

and then we attach it to the router:

   router.attach("",main);
   router.attach("/",main);

The two attach statements above let the service respond to ping and ping/.

The service receives an XML message with the number of requests. We won't bore you with the details of parsing the XML. Once the number is known, all that is necessary is to loop until a maximum time has been reached or the number of pings have been received.

That code looks like:

                  // loop till we've timed out or received all the responses
                  while(stat.received<stat.requested && (System.currentTimeMillis()-stat.start)<maxWait) {
                     
                     // If there are no other agents, we can't ping anyone and so we'll pause
                     if (agentContext.getAgentMonitor().getAgents().size()==0) {
                        long amount = amount = 3*tick;
                        getLogger().info("No agents, waiting "+amount);
                        try {
                           Thread.currentThread().sleep(amount);
                        } catch (InterruptedException ex) {
                           ex.printStackTrace();
                        }
                     }
                     
                     // Send pings to all agents
                     int sent = 0;
                     for (String id : agentContext.getAgentMonitor().getAgents()) {
                        
                        // We're using the async protocol
                        String uri = "xeerkat://"+self+"/"+id+"/ping/echo";
                        getLogger().info("Pinging "+uri);
                        
                        // Make a GET request on the rest service for echo
                        Response pingResponse = client.get(uri);
                        
                        // Success means we could send the request and not much more
                        if (pingResponse.getStatus().isSuccess()) {
                           sent++;
                           
                           // Get the id and add a listener for the response
                           Form headers = (Form)pingResponse.getAttributes().get("org.restlet.http.headers");
                           String responseId = headers.getValues("x-response-id");
                           agentContext.registerResponseListener(responseId,new ResponseListener() {
                              public void onResponse(String id,Response response) {
                                 stat.received++;
                                 
                                 // mark the moment we've satisfied the request
                                 if (stat.received==stat.requested) {
                                    stat.finished = System.currentTimeMillis();
                                 }
                              }
                           });
                           
                           
                        } else {
                           getLogger().info("Cannot ping "+id+", status="+pingResponse.getStatus().getCode()+", "+(pingResponse.getEntity()==null ? "(no text)" : pingResponse.getEntity().getText()));
                        }
                     }
                     
                     // If we couldn't send anything, pause
                     if (sent==0 && agentContext.getAgentMonitor().getAgents().size()>0) {
                        try {
                           Thread.currentThread().sleep(tick);
                        } catch (InterruptedException ex) {
                           ex.printStackTrace();
                        }
                        getContext().getLogger().info("Tick: "+(System.currentTimeMillis()-stat.start));
                     }
                  }

The response is then generated by:

                  // We're done, send the response
                  response.setStatus(Status.SUCCESS_OK);
                  response.setEntity(new StringRepresentation("<stats requested='"+stat.requested+"' received='"+stat.received+"' time='"+(stat.finished-stat.start)+"' elapsed='"+(System.currentTimeMillis()-stat.start)+"'/>",MediaType.APPLICATION_XML));

Now, pings are received at "/ping/echo" and so we just define a simple restlet to handle them:

      router.attach("/echo",new Restlet(getContext()) {
         public void handle(Request request, Response response) {
            response.setEntity(request.getEntity());
            response.setStatus(Status.SUCCESS_OK);
         }
      });

The neat bit here is that all these resources are available over both HTTP and XMPP.

The last step is to package the service for the agent. The agent currently requires the service to packaged as a separate jar file. At the root of the jar file there must be an 'application.xml' resource that looks like:

<application xmlns='http://www.xeerkat.org/Vocabulary/Services/2007/1/0' class='org.xeerkat.services.ping.PingApplication'/>

That's it. The full source is available from subversion as PingApplication.java.









Hosted by Google Code