New to Java? We'll help you get started with our revised beginner's tutorial, or our free online textbook.


Get the latest Java books
h t t p : / /w w w . j a v a c o f f e e b r e a k . c o m /

Java Coffee Break

Chapter 8: A Simple Example

Contents

This chapter looks at a simple problem, implementing it in a number of different ways

8.1. Problem Description

Applications often need to work out the type of a file, to see if it is a text file, an HTML document, an executable, etc. This can be done in two ways:

  1. By examining the file's name
  2. By examing the file's contents
Utilities such as the Unix file command use the second method, and have a complex description file (such as /etc/magic or /usr/share/magic) to aid in this. Many other applications such as Web browsers, mail readers (and even some operating systems!) use the first method and work out a file's type based on its name.

A common file classification is into MIME types such as text/plain and image/gif. There are tables of ``official'' MIME types (unofficial ones can be added on an adhoc basis), and there are also tables of mappings from filename endings to corresponding MIME types. These tables have entries such as


application/postscript          ai eps ps
application/rtf                 rtf
application/zip                 zip
image/gif                       gif
image/jpeg                      jpeg jpg jpe
text/html                       html htm
text/plain                      txt
and are stored in files for applications to access.

This storage of tables separate from the applications that would use them is rated as bad from the O/O point of view, since each application would need to have code to interpret the tables. The multiplicity of these tables and the ability of users to modify them makes this a maintenance problem. It would be better to encapsulate at least the filename to MIME type mapping table in an object. We define a MIME class


package standalone;

/**
 * MIMEType.java
 */

public class MIMEType  {

    /**
     * A MIME type is made up of 2 parts
     * contentType/subtype
     */    
    protected String contentType;
    protected String subtype;

    public MIMEType(String type) {
	int slash = type.indexOf('/');
	contentType = type.substring(0, slash-1);
	subtype = type.substring(slash+1, type.length());
    }
    
    public MIMEType(String contentType, String subtype) {
	this.contentType = contentType;
	this.subtype = subtype;
    }

    public String toString() {
	return contentType + "/" + subtype;
    }
} // MIMEType

and a mapping class

package standalone;

/**
 * FileClassifier.java
 */

public class FileClassifier  {
    
    static MIMEType getMIMEType(String fileName) {
	if (fileName.endsWith(".gif")) {
            return new MIMEType("image", "gif");
        } else if (fileName.endsWith(".jpeg")) {
            return new MIMEType("image", "jpeg");
        } else if (fileName.endsWith(".mpg")) {
            return new MIMEType("video", "mpeg");
        } else if (fileName.endsWith(".txt")) {
            return new MIMEType("text", "plain");
        } else if (fileName.endsWith(".html")) {
            return new MIMEType("text", "html");
        } else
	    // fill in lots of other types,
	    // but eventually give up and
            return null;
    }
} // FileClassifier

This mapping class has no constructors, as it justs acts as a lookup table via its static method getMIMEType().

Applications may make use these classes as they stand, by simply compiling with them and having the class files available at runtime. This will still result in duplication throughout JVMs, possible multiple copies of the class files, and potentially severe maintenance problems if applications need to be re-compiled. It may be better to have the FileClassifier as a network service. What will be involved in this?

8.2. Service Specification

If we wish to make a version of FileClassifier available across the network, there are a number of possibilities. The client will be asking for an instance of a class, and generally will not care too much about the details of this instance. For example, it will want an instance of a DiskDrive or a Calendar. Usually it will not care which drive it gets, or which calendar. If it requires further specification it can either ask for a subclass instance (such as a SeagateDiskDrive, or use an Entry object for this additional information.

Services will have particular implementations, and will upload these to the service locators. The uploaded service will be of a quite specific class, and may have associated entries.

There are several options that the client could use in trying to locate a suitable service.

Option 1
The silly one: push the entire implementation upto the lookup service and make the client ask for it by its class. Then the client might just as well create the classifier as a local object as it has all the information needed! This doesn't lend itself to flexibility with new unknown services coming along if the client already has to know the details. So this option is not feasible.
Option 2
Let the client ask for a superclass of the service. This is better, as it allows new implementations of a service to just be implemented as new subclasses. It is not ideal, as classes have implementation code, and if ever this changes over time then there is a maintenance issue with the possibility of version ``skew'' over time. This can be used for Jini: it just isn't the best way.
Option 3
Separate the interface completely from the implementation. Make the interface available to the client, and upload the implementation to the lookup service. Then when the client asks for an instance object that implements the interface, it will get any object for this interface. This will reduce maintenance: if the client is coded just in terms of the interface then it will not need recompilation even if the implementation changes. Note that these words will translate straight into Java terms: the client knows about a Java interface, whereas the server deals in terms of a Java class that implement's the interface.

The ideal mechanism in the Jini world is to specify services by interfaces, and have all clients know this interface. Then each service can be an implementation of this interface. This is simple in Java terms, simple in specification terms, and simple for maintenance. This is not the complete set of choices for the service, but is enough to allow a service to be specified and get on with building the client. One possibility for service implementation is looked at later in this chapter, and the next chapter is devoted to the full range of possibilities.

8.3. Common Classes

The client and any implementations of a service must share some common classes. For a file classification service the common classes are the classifier itself (which can be implemented as many different services) and the return value, the MIME type. These have to change very slightly from their standalone form.

8.3.1 MIMEType

The class MIMEType is known to the client and to any file classifier service. The class files can be expected to be known to the JVMs of all clients and services. The method getMIMEType() will return an object from the service. There are implementation possibilities that can affect this object:

  1. If the service runs in the client JVM, then nothing special needs to be done
  2. If the service runs in a separate JVM, then the MIMEType object must be serialized for transport to the client JVM. For this to be possible, it must implement the Serializable interface
There is a difference in the object depending on possible implementations. If it implements Serializable then it can be used in both the remote and local cases, but if it doesn't then it can only be used in the local case.

Making decisions about interfaces based on future implementation concerns is traditionally rated as poor design. However, a document from Sun ``A Note on Distributed Computing'' by Jim Waldo et al argues that in the case of distributed objects it is important to determine if the objects may be running remotely and adjust interfaces and classes accordingly at the design stage. This is to take into account possible extra failure modes of methods, and in this case, an extra requirement on the object. The paper is reprinted in the Jini specification book from Sun and is also at http://www.sun.com/research/techrep/1994/abstract_29.html

This leads to an interface which adds the Serializable interface to the previous version.



package common;

import java.io.Serializable;

/**
 * MIMEType.java
 */

public class MIMEType implements Serializable {

    /**
     * A MIME type is made up of 2 parts
     * contentType/subtype
     */    
    protected String contentType;
    protected String subtype;

    public MIMEType(String type) {
        int slash = type.indexOf('/');
        contentType = type.substring(0, slash-1);
        subtype = type.substring(slash+1, type.length());
    }
    
    public MIMEType(String contentType, String subtype) {
        this.contentType = contentType;
        this.subtype = subtype;
    }

    public String toString() {
        return contentType + "/" + subtype;
    }
} // MIMEType




8.3.2 FileClassifier interface

Changes have to be made to the file classifier interface as well. Firstly, interfaces cannot have static methods, so we shall have to turn the method getMIMEType() into a public instance method. In addition, instances implementing this may need to be shipped around as proxy objects, so it should extend Serializable. This is the same situation as with MIMEType.

In addition, all methods are defined to throw a java.rmi.RemoteException. This type of exception is used by Java (not just the RMI component) to mean ``a network error has occurred''. This could be a lost connection, a missing server, a class not downloadable, etc. There is a little subtlety here, related to the java.rmi.Remote class: the methods of Remote must all throw a RemoteException, but the converse is not true. If all the methods throw RemoteException, it does not mean the class implements/extends Remote. It only means that an implementation may be implemented as a remote (distributed) object, and this implementation might also use the RMI Remote interface.

This gives the following interface



package common;

import java.io.Serializable;

/**
 * FileClassifier.java
 */

public interface FileClassifier extends Serializable {
    
    public MIMEType getMIMEType(String fileName) 
	throws java.rmi.RemoteException;
    
} // FileClasssifier

Why does this interface throw a java.rmi.RemoteException in the getMIMEType() method? Well, an interface is supposed to be above all possible implementations, and should never change. The implementation discussed later in this chapter does not throw such an exception. However, other implementations in other sections use a Remote implementation, and this will require that the method throws an java.rmi.RemoteException. Since it is not possible to just add a new exception in a subclass or interface implementation, the possibility must be added in the interface specification.

8.4. Client

The client is the same for all of the possible server implementations discussed in throughout this book. The client does not care how the server-side implementation is done, just as long as it gets a service that it wants, and it specifies this by asking for a FileClassifier interface.

8.4.1 Unicast Client

If there is a known service locator which will know about the service, then there is no need to search. This doesn't mean that the location of the service is known, only of the locator. For example, there may be a (fictitious) organisation ``All About Files'' at www.all_about_files.com that would know about various file services, keeping track of them as they come on line, move, disappear, etc. A client would ask the service locator running on this site for the service, wherever it is. This uses the unicast lookup techniques.



package client;

import common.FileClassifier;
import common.MIMEType;

import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistration;
import java.rmi.RMISecurityManager;
import net.jini.core.lookup.ServiceTemplate;

/**
 * TestUnicastFileClassifier.java
 */

public class TestUnicastFileClassifier {

    public static void main(String argv[]) {
	new TestUnicastFileClassifier();
    }

    public TestUnicastFileClassifier() {
	LookupLocator lookup = null;
	ServiceRegistrar registrar = null;
	FileClassifier classifier = null;

        try {
            lookup = new LookupLocator("jini://www.all_about_files.com");
        } catch(java.net.MalformedURLException e) {
            System.err.println("Lookup failed: " + e.toString());
	    System.exit(1);
        }

	System.setSecurityManager(new RMISecurityManager());

	try {
	    registrar = lookup.getRegistrar();
	} catch (java.io.IOException e) {
            System.err.println("Registrar search failed: " + e.toString());
	    System.exit(1);
	} catch (java.lang.ClassNotFoundException e) {
            System.err.println("Registrar search failed: " + e.toString());
	    System.exit(1);
	}

	Class[] classes = new Class[] {FileClassifier.class};
	ServiceTemplate template = new ServiceTemplate(null, classes, null);
	try {
	    classifier = (FileClassifier) registrar.lookup(template);
	} catch(java.rmi.RemoteException e) {
	    e.printStackTrace();
	    System.exit(1);
	}

	if (classifier == null) {
	    System.out.println("Classifier null");
	    System.exit(2);
	}
	MIMEType type;
	try {
	    type = classifier.getMIMEType("file1.txt");
	    System.out.println("Type is " + type.toString());
	} catch(java.rmi.RemoteException e) {
	    System.err.println(e.toString());
	}
	System.exit(0);
    }
} // TestUnicastFileClassifier







The client's JVM looks like

Figure 8.1: Objects in client JVM
It has objects that implement the interfaces ServiceRegistrar and FileClassifier, but it doesn't know - or need to know - what classes they are. When the service locator's JVM is added in, it looks like
Figure 8.2: Objects in client and service locator JVM's
This shows that the client gets its registrar from the JVM of the service locator. This object is not specified in detail. Sun supply a service locator known as reggie. This locator implements the ServiceRegistrar using an implementation that neither clients nor services are expected to know. The classes which implement this object are contained in the file reggie-dl.jar and are downloaded to the clients and services using (typically) an HTTP server.

8.4.2 Multicast Client

More likely, client will need to search through all of the service locators till it finds one holding a service it is looking for. It would need to use a multicast search for this. If it only needs one occurrence of the service, then it can do something like exit after using the service. More complex behaviour will be illustrated in later examples. The client does not need to have long-term persistence. But it does need a user thread to remain in existence for long enough to find service locators and find a suitable service. So in main() a user thread again sleeps for a short period (ten seconds).



package client;

import common.FileClassifier;
import common.MIMEType;

import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;

/**
 * TestFileClassifier.java
 */

public class TestFileClassifier implements DiscoveryListener {

    public static void main(String argv[]) {
	new TestFileClassifier();

        // stay around long enough to receive replies
        try {
            Thread.currentThread().sleep(100000L);
        } catch(java.lang.InterruptedException e) {
            // do nothing
        }
    }

    public TestFileClassifier() {
	System.setSecurityManager(new RMISecurityManager());

	LookupDiscovery discover = null;
        try {
            discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
        } catch(Exception e) {
            System.err.println(e.toString());
            System.exit(1);
        }

        discover.addDiscoveryListener(this);

    }
    
    public void discovered(DiscoveryEvent evt) {

        ServiceRegistrar[] registrars = evt.getRegistrars();
	Class [] classes = new Class[] {FileClassifier.class};
	FileClassifier classifier = null;
	ServiceTemplate template = new ServiceTemplate(null, classes, 
						       null);
 
        for (int n = 0; n < registrars.length; n++) {
	    System.out.println("Service found");
            ServiceRegistrar registrar = registrars[n];
	    try {
		classifier = (FileClassifier) registrar.lookup(template);
	    } catch(java.rmi.RemoteException e) {
		e.printStackTrace();
		continue;
	    }
	    if (classifier == null) {
		System.out.println("Classifier null");
		continue;
	    }
	    MIMEType type;
	    try {
		String fileName;

		fileName = "file1.txt";
		type = classifier.getMIMEType(fileName);
		printType(fileName, type);

		fileName = "file2.rtf";
		type = classifier.getMIMEType(fileName);
		printType(fileName, type);

		fileName = "file3.abc";
		type = classifier.getMIMEType(fileName);
		printType(fileName, type);
	    } catch(java.rmi.RemoteException e) {
		System.err.println(e.toString());
		continue;
	    }
	    // success
	    System.exit(0);
	}
    }

    private void printType(String fileName, MIMEType type) {
	System.out.print("Type of " + fileName + " is ");
	if (type == null) {
	    System.out.println("null");
	} else {
	    System.out.println(type.toString());
	}
    }

    public void discarded(DiscoveryEvent evt) {
	// empty
    }
} // TestFileClassifier

8.4.3 Exception Handling

A Jini program can generate a huge number of exceptions, often related to the network nature of Jini. Many programs adopt a somewhat cavalier attitude to exceptions: catch them, maybe put out an error message and continue - Java makes it easy to handle errors! More seriously, whenever an exception occurs the question has to be asked as to whether the program can continue, or has its state has been corrupted but not so badly that it cannot recover, or whether the program state has been damaged so much that the program must exit.

The multicast TestFileClassifier of the last section can throw exceptions at a number of places.

  1. The LookupDiscovery constructor can fail. This is indicative of some serious network error. The created discover object is needed to add a listener, and if this cannot be done, then the program really can't do anything. So it is appropriate to exit with an error value
  2. The ServiceRegistrar.lookup() can fail. This is indicative of some network error in the connection with a particular service locator. While this may have failed, it is possible that other network connections may succeed. The application can restore a consistent state by skipping the rest of the code in this iteration of the for() loop by a continue statement
  3. The FileClassifier.getMIMEType() can fail. This can be caused by a network error, or perhaps the service has simply gone away. Whatever, consistent state can again be restored by skipping the rest of this loop iteration

Finally, if one part of a program can exit with an abnormal (non-zero) error value, then a successful exit should signal its success with an exit value of zero. If this is not done, then the exit value becomes indeterminate, and of no value to other processes which may wish to establish if the program exited successfully or not.

8.5. Service Proxy

A service will be delivered from out of a server. That is, a server will be started, create one or more service objects, register these with service locators, and then wait for network requests to come in for the service. What the server will actually export is usually a proxy for the service. The proxy is an object that will eventually run in a client, and will make calls back across the network to the service object within the server.

The service and its proxy are tightly integrated: they must communicate using a protocol known to them both, and must exchange information in an agreed manner. However, the relative size of each is up to the designer of a service and its proxy. For example, the proxy may be ``fat'' (or ``smart''), which means it does a lot of processing on the client side. The service itself is then typically ``thin'', not doing much at all. Alternatively, the proxy may be thin, doing little more (or nothing more) than passing requests between the client and the fat service.

As well as this choice of size, there is also a choice of communication mechanisms between the two. Client-server systems often have the choice of message-based or remote procedure call. These choices are also available between a Jini proxy and its service. Since they are both in Java, there is a standard RPC-like mechanism called RMI (Remote Method Invocation), and this can be used if wanted. There is no necessity to use this, but many implementations of Jini proxies will do so since it is easy. It does force a particular choice of thin proxy to a fat client, though, and this may not be ideal for all situations.

This chapter will look at one possibility only, where the proxy is fat and is the whole of the service. The next chapter will look in more detail at the other possibilities.

8.6. Uploading a Complete Service

The file classifier service does not rely on any particular properties of its host - it is not hardware or operating system dependant, and does not make use of any files on the host side. In this case it is possible to upload the entire service to the client and let it run there. The proxy is the service, and no processing elements need to be left on the server.

8.6.1 FileClassifier implementation

The implementation of this is straightforward



package complete;

import common.MIMEType;
import common.FileClassifier;

/**
 * FileClassifierImpl.java
 */

public class FileClassifierImpl implements FileClassifier {

    public MIMEType getMIMEType(String fileName) {
        if (fileName.endsWith(".gif")) {
            return new MIMEType("image", "gif");
        } else if (fileName.endsWith(".jpeg")) {
            return new MIMEType("image", "jpeg");
        } else if (fileName.endsWith(".mpg")) {
            return new MIMEType("video", "mpeg");
        } else if (fileName.endsWith(".txt")) {
            return new MIMEType("text", "plain");
        } else if (fileName.endsWith(".html")) {
            return new MIMEType("text", "html");
        } else
            // fill in lots of other types,
            // but eventually give up and
            return null;
    }


    public FileClassifierImpl() {
	// empty
    }
    
} // FileClassifierImpl

8.6.2 Server

The server for this needs to create an instance of the exportable service, register this and keep the lease alive. In the discovered() method it not only registers the service but also adds it to a LeaseRenewalManager, to keep the lease alive ``forever''. This manager runs its own threads to keep re-registering the leases, but these are daemon threads. So in the main() method the user thread goes to sleep for as long as we want the server to stay around. Note that if the server does terminate, then the lease will fail to be renewed and the exported service will be discarded from lookup locators even though the server is not required for delivery of the service. The serviceID is initially set to null and then extracted from the first lookup service that replies and is re-used. Ideally it should also be stored (say on disk) and retrieved the next time the service starts. Note that we get the service id from the registration not the registrar.


package complete;

import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistration;
import net.jini.core.lease.Lease;
import net.jini.core.lookup.ServiceID ;
// import com.sun.jini.lease.LeaseRenewalManager; // Jini 1.0
// import com.sun.jini.lease.LeaseListener;       // Jini 1.0
// import com.sun.jini.lease.LeaseRenewalEvent;   // Jini 1.0
import net.jini.lease.LeaseListener;              // Jini 1.1
import net.jini.lease.LeaseRenewalEvent;          // Jini 1.1
import net.jini.lease.LeaseRenewalManager;        // Jini 1.1

/**
 * FileClassifierServer.java
 */

public class FileClassifierServer implements DiscoveryListener, 
                                             LeaseListener {
    
    protected LeaseRenewalManager leaseManager = new LeaseRenewalManager();
    protected ServiceID serviceID = null;

    public static void main(String argv[]) {
	new FileClassifierServer();
	
        // keep server running forever to 
	// - allow time for locator discovery and
	// - keep re-registering the lease
	Object keepAlive = new Object();
	synchronized(keepAlive) {
	    try {
		keepAlive.wait();
	    } catch(java.lang.InterruptedException e) {
		// do nothing
	    }
	}
    }

    public FileClassifierServer() {
        System.setSecurityManager(new RMISecurityManager());

	LookupDiscovery discover = null;
        try {
            discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
        } catch(Exception e) {
            System.err.println("Discovery failed " + e.toString());
            System.exit(1);
        }

        discover.addDiscoveryListener(this);
    }
    
    public void discovered(DiscoveryEvent evt) {

        ServiceRegistrar[] registrars = evt.getRegistrars();

        for (int n = 0; n < registrars.length; n++) {
            ServiceRegistrar registrar = registrars[n];

	    ServiceItem item = new ServiceItem(serviceID,
					       new FileClassifierImpl(), 
					       null);
	    ServiceRegistration reg = null;
	    try {
		reg = registrar.register(item, Lease.FOREVER);
	    } catch(java.rmi.RemoteException e) {
		System.err.println("Register exception: " + e.toString());
		continue;
	    }
	    System.out.println("service registered");

	    System.out.println("Class " + registrar.getClass().toString());
	    Object [] signers = registrar.getClass().getSigners();
	    if (signers == null) {
		System.out.println("No signers");
	    } else {
		System.out.println("Signers");
		for (int m = 0; m < signers.length; m++)
		    System.out.println(signers[m].toString());
	    }
	    java.security.ProtectionDomain domain = registrar.
		getClass().getProtectionDomain();
	    java.security.CodeSource codeSource = domain.getCodeSource();
	    System.out.println("CodeSource " + codeSource.toString());
	    RuntimePermission perm = new RuntimePermission("getProtectionDomain");
	    if (domain.implies(perm))
		System.out.println("Permission allowed");
	    else
		System.out.println("Permission not allowed");
	    // should be ok - we aren't in "domain", we have given permission
	    System.getSecurityManager().checkPermission(perm);

	    // set lease renewal in place
	    leaseManager.renewUntil(reg.getLease(), Lease.FOREVER, this);

	    // set the serviceID if necessary
	    if (serviceID == null) {
		serviceID = reg.getServiceID();
	    }
	}
    }

    public void discarded(DiscoveryEvent evt) {

    }

    public void notify(LeaseRenewalEvent evt) {
	System.out.println("Lease expired " + evt.toString());
    }   
    
} // FileClassifierServer

The server by itself running in its JVM looks like

Figure 8.3: Objects in server JVM
This receives an object implementing ServiceRegistrar from the service locator (such as reggie). If we add in the service locator and the client in their JVM's, it becomes
Figure 8.4: Objects in all the JVM's
The unknown FileClassifier object in the client is here supplied by the service object FileClassifierImpl (via the lookup service, where it is stored in passive form).

8.6.3 Client

The client for this service was discussed earlier. The client does not need any special information about this implementation of the service and so can remain quite generic.

8.6.4 What classes need to be where?

We have the classes

  1. common.MIMEType
  2. common.FileClassifier
  3. complete.FileClassifierImpl
  4. complete.FileClassifierServer
  5. client.TestFileClassifier
These could be running on upto four different machines
  1. The server machine for FileClassifier
  2. The machine for the lookup service
  3. The machine running the client TestFileClassifier
  4. An HTTP server will need to run somewhere to deliver the class file definition of FileClassifierImpl to clients
What classes need to be known to which machines?

The server running FileClassifierServer needs to know the following classes and interfaces

  1. The common.FileClassifier interface
  2. The class common.MIMEType
  3. The class complete.FileClassifierServer
  4. The class complete.FileClassifierImpl
These classes all need to be in the CLASSPATH of the server.

The class complete.FileClassifierImpl will need to be accessible to an HTTP server, as discussed in the next section.

The lookup service does not need to know any of these classes. It just deals with them in the form of a java.rmi.MarshalledObject

The client needs to know

  1. The common.FileClassifier interface
  2. The class common.MIMEType
  3. The class client.TestFileClassifier

8.6.5 Running the FileClassifier

We now have a service FileClassifierServer and a client TestFleClassifier to run. There should also be at least one lookup locator already running. The CLASSPATH should be set for each to include the classes discussed in the last section, in addition to the standard ones.

A serialized instance of complete.FileClassifierImpl will be passed from the server to the locator and then to the client. Once on the client, it will need to be able to run the class file for this object, so will need to load its class file from an HTTP server. The location of this class file relative to the server's DocumentRoot will need to be specified by the service invocation. For example, if it is stored in /DocumentRoot/classes/complete/FileClassifierImpl.class then the service will be started by:


java -Djava.rmi.codebase=http://hostname/classes \
     complete.FileClassifierServer
In this, hostname is the name of the host the server is running on. Note that this host name cannot be localhost, because the local host for the server will not be the local host for the client!

The client will be loading a class definition across the network. It will need to allow this in a security policy file by


java -Djava.security.policy=policy.all client.TestFileClassifier

8.7. Summary

The material of the previous chapters is put together in a simple example. The requirements of class structures for a Jini system are discussed. A discussion is also given of what classes need to be available to each component of a Jini system.


This file is Copyright (©) 1999, 2000 by Jan Newmarch (http://pandonia.canberra.edu.au) jan@ise.canberra.edu.au.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v0.4 or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.

Back to main


Copyright 1998, 1999, 2000 David Reilly

Privacy | Legal | Linking | Advertise!

Last updated: Monday, June 05, 2006