One of the middleware frameworks being heavily promoted ay present is that of Web Services. These are built upon the invocation protocol SOAP, the specification language WSDL and the discovery system UDDI. In this chapter we look how clients and services from different frameworks can interoperate, with particular reference to Web Services and Jini
While this book has been about Jini, there are many other middleware systems in use, such as CORBA, Web Services, UPnP, Salutation and many others. While it would be very convenient to a software developer if all but their favourite middleware were to disappear, this is unlikely to happen. There are technical and political reasons for many of these frameworks to survive, and so the software developer will just have to live in a world of multiple middleware systems.
Users on the other hand just want their different pieces of software to work together no matter what framework is used. It is upto the software developer to figure out how to get a web Service client to work with a mixture of Jini and UPnP services, say.
The most common way of getting such mixtures to work is by a bridge. That is, to get a Web Service client to talk to a Jini service, typically a bridge will be an application that will sit between these and act as a Web Service service and a Jini client. In the middle, the bridge translates from one framework to the other, in both directions. This is shown in the figure.
There are actually two aspects to a bridge: one is concerned with discovery and the other with invocation.
Web services and Jini have special features that make this a simpler task than in general
Just looking at the case of a Jini client talking to a Web Service, this can lead to several models, as pictured in the figures. In the first, the proxy can be an ordinary (e.g. Jeri) proxy talking back to its service. This service also acts as a Web Service client.
We illustrate this with a simple web service, for file classification again.
To avoid the complexities of Web Service types and with deployment of such
services, we simplify
the service to one that takes a string as filename and returns the MIME type
as a string major/minor
. The class is not in a package,
allowing simple deployment under Apache Axis.
The implementation of this service
is then straightforward
/**
* FileClassifierService.java
*/
public class FileClassifierService {
public String getMIMEType(String fileName) {
if (fileName.endsWith(".gif")) {
return "image/gif";
} else if (fileName.endsWith(".jpeg")) {
return "image/jpeg";
} else if (fileName.endsWith(".mpg")) {
return "video/mpeg";
} else if (fileName.endsWith(".txt")) {
return "text/plain";
} else if (fileName.endsWith(".html")) {
return "text/html";
} else
// fill in lots of other types,
// but eventually give up and
return "";
}
public FileClassifierService() {
// empty
}
} // FileClassifierService
The Apache Axis server run under Apache Tomcat is a popular means of delivering
Web Services written in Java. It includes libraries for both client- and service-side.
The simplest wasy of deploying the service under Axis is to copy the
implementation source code to the axis/webapps
directory,
renaming the extension as .jws
instead of .java
The service can of course be written in many different languages. This is usually done by a horrible practice which has become common with Web Services: reverse engineer the implementation given above to a WSDL specification and then forward engineer this to your favourite language. We shall ignore all such issues here.
On the client-side, a consumer of this service can then be written most simply as
package ws;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
import ws.MIMEType;
public class TestWSClient {
public static void main(String [] args) {
try {
String endpoint =
"http://localhost:8080/axis/FileClassifierService.jws";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName(new QName("http://soapinterop.org/", "getMIMEType"));
String ret = (String) call.invoke( new Object[] { "file.txt" } );
System.out.println("Type of file 'file.txt' is " + ret);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
There are other ways, but this is good enough for the rest of this chapter
which is intended to show how Jini and Web Services can interoperate rather
than delving into the arcanities of Web Services.
We can write a bridge that acts as a Jini service for the
common.FileClassifier
specification used throughout this
book. It will also act as a Web Service client by essentially taking
all of the client code above and using this in the Jini service
implementation.
The bridge is a normal Jini server advertising the following Jini service
implementation:
package ws;
import common.MIMEType;
import common.FileClassifier;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
/**
* FileClassifierImpl.java
*/
public class FileClassifierImpl implements RemoteFileClassifier {
public MIMEType getMIMEType(String fileName)
throws java.rmi.RemoteException {
try {
String endpoint =
"http://localhost:8080/axis/FileClassifierService.jws";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName(new QName("http://soapinterop.org/", "getMIMEType"));
String ret = (String) call.invoke( new Object[] { fileName } );
return new MIMEType(ret);
} catch (Exception e) {
throw new RemoteException("SOAP failure", e);
}
}
public FileClassifierImpl() throws java.rmi.RemoteException {
// empty constructor required by RMI
}
} // FileClassifierImpl
This service can export a Jeri or RMI proxy to a Jini client as we have seen before. Client calls on the proxy are sent to this service which acts as a Web Service client using the model of figure 2. When this implementation is built and run, it will need the Axis libraries on the Jini service side.
A service can be written that follows the second pattern in figure 3, simply
by changing the inheritance from RemoteFileClassifier
to FileClassifier
and Serializable
.
A client then gets a copy of this service and all calls are made locally
in the client.
package ws;
import common.MIMEType;
import common.FileClassifier;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
/**
* FileClassifierImpl.java
*/
public class FileClassifierSerializableImpl implements FileClassifier, java.io.Serializable {
public MIMEType getMIMEType(String fileName)
throws java.rmi.RemoteException {
try {
String endpoint =
"http://localhost:8080/axis/FileClassifierService.jws";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName(new QName("http://soapinterop.org/", "getMIMEType"));
String ret = (String) call.invoke( new Object[] { fileName } );
return new MIMEType(ret);
} catch (Exception e) {
throw new RemoteException(e);
}
}
public FileClassifierImpl() throws java.rmi.RemoteException {
// empty constructor required by RMI
}
} // FileClassifierImpl
This has major implications for the classes downloaded to the client!
In order to run this service on the client side, it need access to the class files
for the Axis classes Call
, Service
and QName
.
The client cannot be expected to have these, since it doesn't need to know any details
of the implementation. So the Axis libraries have to be placed on an HTTP server
and listed in the Jini server's codebase.
The libraries are over one megabyte in size, and so this can result in a substantial
download to the Jini client.
Forming bridge between Web Service clients and Jini services follows the same general structure, as
There are a couple of wrinkles in getting this to work properly. With Apache Axis and the Tomcat server, these are
The Jini class files are not in the classpath for Tomcat. However, Tomcat
and Apache allow any extra jar files required by a Web Service to be placed
in special lib
directories under the service's
WEB-INF
directory. Copying the Jini files
jsk-lib.jar
and jsk-platform.jar
to this
directory will make them available to the Web Service. This will be part of the
deployment mechanisms for the Web Service
The issues of a security policy is potentially more difficult. A server such
as Tomcat can be started either with or without a security manager. If it uses a
security manager, then the default security policy does not allow a new
security manager to be put in place. This blocks a Jini client from
installing an RMISecurityManager
and so it cannot download
a Jini registrar and find Jini services. Negotiation would then be required
with the Tomcat administrator to add in sufficient permissions to the
security policy to allow a Jini client to run.
If Tomcat is run without a security manager then it is possible for the Web
Service to install one. But it will then need to use a security policy.
Up to now we have specified such a policy as a command line argument,
but the command line is not accessible to an Axis Web Service. The workaround
is to use System.setProperty()
to set the security policy file
before installing a security manager.
All I/O to the console has to be cleaned up and put into remote exceptions. With these factors, the Web Service to bridge to a Jini service looks like
/**
* FileClassifierJiniService.java
*/
import common.FileClassifier;
import common.MIMEType;
import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.core.lookup.ServiceItem;
import net.jini.lease.LeaseRenewalManager;
import java.rmi.RemoteException;
public class FileClassifierJiniService {
private final static long WAITFOR = 10000;
public String getMIMEType(String fileName) throws RemoteException {
ServiceDiscoveryManager clientMgr = null;
// set a security policy file here since we don't have command line access
System.setProperty("java.security.policy",
"/home/httpd/html/java/jini/tutorial/policy.all");
System.setSecurityManager(new RMISecurityManager());
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
clientMgr = new ServiceDiscoveryManager(mgr,
new LeaseRenewalManager());
} catch(Exception e) {
throw new RemoteException("Lookup failed", e);
}
Class [] classes = new Class[] {FileClassifier.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
ServiceItem item = null;
// Try to find the service, blocking till timeout if necessary
try {
item = clientMgr.lookup(template,
null, // no filter
WAITFOR); // timeout
} catch(Exception e) {
throw new RemoteException("Discovery failed", e);
}
if (item == null) {
// couldn't find a service in time
return "";
}
// Get the service
FileClassifier classifier = (FileClassifier) item.service;
if (classifier == null) {
throw new RemoteException("Classifier null");
}
// Now we have a suitable service, use it
MIMEType type;
try {
type = classifier.getMIMEType(fileName);
return type.toString();
} catch(java.rmi.RemoteException e) {
throw e;
}
}
public FileClassifierJiniService() {
// empty
}
} // FileClassifierJiniService
The steps to get all this running are
FileClassifierJiniService.java
to point to
a valid security policy file on your system
FileClassifierJiniService.java
to the Tomcat
webapps/axis
directory as FileClassifierJiniService.jws
,
changing the file extension
jsk-lib.jar
and jsk-platform.jar
to the Tomcat
webapps/axis/WEB-INF/lib
directory
FileClassifier
interface that has been given in this
tutorial
ws.TestWS2JiniClient
Bridging between different types of services is not an easy matter as there are many complex issues to be considered. Nevertheless, it is possible to build upon work performed to build Java Web Services and their Java clients. This allows us to use Jini services and clients in a relatively simple manner.
If you found this chapter of value, the full book "Foundations of Jini 2 Programming" is available from APress or Amazon .
This work is licensed under a
Creative Commons License, the replacement for the earlier Open Content License.