My favorites | Sign in
Project Home Downloads Wiki Issues Source
Project Information
Members
Featured
Downloads
Links

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

  • 2010-10-05: Code has just been committed to support the "xmpp" uri scheme. You can now POST/GET to uri's like xmpp:someone@somewhere.com?message;body=testing and XMPP stanzas will be sent. See XMPP URI Handling for more information.
  • 2010-04-15: A new release version 3.0M1 (Milestone 1) is available that has been updated to Restlet 2.0. This not an official release but one for testing with Restlet 2.0 based services.
  • 2010-04-15: A new release of the server 0.7.0 is available that has been updated to Restlet 2.0
  • 2010-04-12: New code has been committed that supports Restlet 2.0.
  • 2008-07-07: The 2.1.0 release is now available. See the release notes for a list of changes.
  • 2008-07-07: The 0.6.0 release of the XMPP server is now available. It now implements a heartbeat presence message to detect dead clients and also contains several fixes for stability related to network connections.

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

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.

Powered by Google Project Hosting