ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Developing Highly Distributed Applications with Jtrix
Pages: 1, 2

Possibilities For a "Hello, World" Service

Now we know what a service consists of, here are some ideas of how we might construct a "Hello, World" service:

  • A single-server version. An access point proxies the getMessage() call back to a central server, which provides the message and is returned to the client. Of course, the server is a single point of failure, so we would do better with...
  • A multi-server version. As above, but with many servers. More robust, but manual intervention is required when one of the servers dies. An improvement would be...
  • A self-distributing multi-server version. As before, but this time, when a server dies, the other servers notice and restart a replacement. Since the location of the servers change over time, each access point needs to be updated with the latest list. And if all of this is too complicated we could opt for...
  • A standalone access-point. The access point itself provides the message. There are no servers and no proxying. Very simple, but of course it doesn't make for a very managable system.

For the sake of simplicity, we'll implement the last option only.

Writing a Service

To implement our access point netlet, we first need to have the facet that provides the message. That's IHelloFacet, shown above.



Then the code of our access point netlet implements the INetlet interface, just like the client. Here's the code, which we'll discuss later:


package org.jtrix.project.helloworld;

import org.jtrix.base.*;

/** Netlet which provides a Hello World service in a very simple way.
 */
public class HelloServer implements INetlet
{
    private static final String _facet_name = IHelloFacet.class.getName();
    private INode _node;

    public byte[] initialise(INode node, Object param, byte[] unsigned)
        throws InitialiseException
    {
        _node = node;
        System.out.println("Server started");
        return null;
    }

    public void terminate(long date, INetlet.IShutdownProgress progress)
    {
        // Nothing to clean up when we terminate
    }

    /** Provide the service requested by the client.
     * @param warrant   The warrant which the client used for binding
     * @param consumer  The client's reciprocating service interface.
     * @return  A service connection.
     */
    public IService bindService(Warrant warrant, IService consumer)
        throws ServiceBindException
    {
        return new HelloService();
    }

    public String[] getFacets()
    {
        // We offer only the IHelloFacet
        return new String[]{ _facet_name };
    }

    public IRemote bindFacet(String facet) throws FacetBindException
    {
        // The node shouldn't try to bind facets from here
        throw new FacetBindException();
    }

    /** A simple implementation of IHelloFacet
     */
    private class HelloFacet implements IHelloFacet
    {
        public String getMessage() 
        {
            return "Hello, world";
        }
    }

    /** This is the real implementation of the hello world service.  It's
     * handed out only through the bindService() method of the public
     * (netlet) class.
     */
    private class HelloService implements IService
    {
        /** Respond to the service connection failing. If it does fail
         * this netlet is useless, so we terminate ourselves.
         */
        public void terminate()
        {
            System.out.println("Service terminated, terminate ourselves");
            _node.requestTermination();
        }

        /** See what facets this service offers the binding (consumer) netlet.
         */
        public String[] getFacets()
        {
            return new String[] { _facet_name };
        }

        /** Allows the consumer netlet to bind a specific facet.
         * @param facet  The name of the facet to bind.
         * @return  An interface to the requested facet.
         */
        public IRemote bindFacet(String facet) throws FacetBindException
        {
            System.out.println("Instantiating "+facet);

            if(!facet.equals(_facet_name))
            {
                throw new FacetBindException();
            }

            System.out.println("Facet instantiated");
            return new FacetHandle(new HelloFacet(), facet);
        }

    } // HelloService

} // HelloServer

The first few methods are how this netlet communicates with the node. It doesn't do much, but initialization does save a reference to the node for the future.

We can see that the service comes out of the bindService() method, which creates a new HelloService object, defined further on. Notice that it provides one facet to the client netlet, which is called by the bindFacet() method.

This code is discussed in much more detail in the document "How to Write Netlets." You should be see, however, that writing a service in Jtrix is not necessarily a difficult thing, at least when the service is simple. Also, the service binding is a two-way connection (each provides an IService to the other), which can be useful if the client needs to offer facilities to the service.

Preparing the Example

To compile the code, we need jtrix.jar on our classpath, which contains all of org.jtrix.base. Then we can bundle all of the classes into a single JAR, which we'll call helloserver.jar. Make sure the JAR containsthe following: HelloServer.class, HelloServer$HelloFacet.class, HelloServer$HelloService.class, and IHelloFacet.class.

Next, we need to create a warrant, which every service needs. A warrant tells the node how to access the service by telling it about the access point netlet. But first we need to create a netlet descriptor, which describes an access point netlet. Our current directory contains the hello1.jar file from our client code:


% ls
hello1.jar  helloserver.jar
% jtrixmaker -type netlet -outfile hello-server.xml \
    -jardirs . -jars helloserver.jar \
    -classname org.jtrix.project.helloworld.HelloServer
% ls
hello1.jar  helloserver.jar  hello-server.xml
%

This gives us the netlet descriptor for the access point netlet, hello-server.xml. And now we can put this into a warrant for the service:


% jtrixmaker -type warrant -descriptor-in hello-server.xml \
    -outfile hello-local-warrant.xml
% ls
hello1.jar  hello-local-warrant.xml  helloserver.jar  hello-server.xml
%

This warrant gives access to our service to any netlet that holds it. If you examine the warrant, you'll see it's very large because it actually contains all of the necessary JARs. In more realistic examples, you can upload the JARs to a Web server and supply jtrixmaker with their URLs. This makes warrants much easier to manage.

Now let's go back to our client netlet. That took a warrant as a parameter and used it to connect to the service. Therefore we need to create a descriptor for the client netlet and include this warrant as a parameter. Here's how we do it, putting it in the file hello1-client.xml:


% ls
hello1.jar  hello-local-warrant.xml  helloserver.jar  hello-server.xml
% jtrixmaker -type netlet -outfile hello1-client.xml \
    -jardirs /home/nik/jtrix/bin . -jars libjtrix.jar hello1.jar \
    -classname org.jtrix.project.helloworld.Hello1Client \
    -param {warrant:hello-local-warrant.xml}
% ls
hello1-client.xml  hello-local-warrant.xml  hello-server.xml
hello1.jar         helloserver.jar
%

Running the Demo

Finally we start up a new node. Here's the command we use and the output produced:


% jnode 211 -netlet-stdio hello1-client.xml
Jnode starting...
Initialising Nodality...
Bootstrap starting
starting hello1-client
netlet:211.0.2: Server started

Bootstrap complete 
netlet:211.0.2: Instantiating org.jtrix.project.helloworld.IHelloFacet
netlet:211.0.2: Facet instantiated
netlet:211.0.1: Hello, world
^C

%

We hit Control-C to stop the node, which would otherwise carry on waiting for more service requests. The jnode command starts a Jtrix node, and the integer that follows is just some arbitrary identifier. This helps when we run node clusters. The -netlet-stdioM option says that standard I/O from the netlets should be output, which is useful for debugging. Finally we name the netlet descriptor.

The output is from three sources, which don't appear in sync. We get the node's output ("Bootstrap starting," etc.), the service netlet's output (labelled 211.0.2) and the client netlet's output (labelled 211.0.1). This shows that the client netlet triggers the service and can then output the message. This is a complete Jtrix client and service!

Where to From Here?

We've seen just a little of what goes into a Jtrix client and service. There is much more we could talk about, such as proxying and self-redundancy. Jtrix doesn't impose these things on developers, nor does it even impose communication standards. It allows an environment for secure distributed systems, and complete freedom: we can use anything from SOAP to much lighter-weight protocols. It's up to the application.

A self-redundant multi-server version of the "Hello, World" service has been produced. It was written using a Jtrix framework called Beatrix, which has also allowed us to write the HTTP server, servlet engine, and DNS that we mentioned originally. The self-redundant multi-server "Hello, World" service required us to write only seven classes within Beatrix.

Conclusion

This introduction has shown how to write a Jtrix client and server, albeit briefly. For much more detail on Jtrix, see http://www.jtrix.org and in particular the document "How to Write Netlets," from which this information is taken. I hope this brief glimpse whets your appetite for developing high-availability, distributed systems with Jtrix.

Nik Silver is manages the open source Jtrix project.


Return to Onjava.com.