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.