Some Simple Examples

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 correspoding 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



/**
 * MIMEType.java
 *
 *
 * Created: Mon Mar 15 22:09:13 1999
 *
 * @author Jan Newmarch
 * @version
 */

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

import MIMEType;

/**
 * FileClassifier.java
 *
 *
 * Created: Mon Mar 15 22:18:59 1999
 *
 * @author Jan Newmarch
 * @version
 */

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 compiled. it may be better to have the FileClassifier as a network service. What will be involved in this?

1. Design Options

If we wish to make a version of FileClassifier available across the network, there are a number of options

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!
Option 2
Separate the interface 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 the classifier object. 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.
Option 3
It is possible that a classifier running as a service might change over time, For example, it may have a means of listening for new MIME-type announcements made by other applications. If it uploads a complete copy of itself to the lookup service, then that copy might become ``stale'' with respect to the ``real'' service, since it is just a serialized ``snapshot'' at the instant of time it was uploaded. An alternative is to upload a proxy that will refer back to the ``live'' service when needed. This will be even more important for services which are in control of volatile pieces of hardware, such as the proverbial toaster.
Option 4

2. Option 2: Uploading a Complete Service

In the form given above, the FileClassifier is basically just a lookup table, with one static method. There is no need to create instances of this type, and this could have been eliminated as a possibility by declaring the constructor as private:


    private FileClassifier() {
    }

If we want to make this into a service that will be exported, we have to make a couple of changes to this. Firstly, we will need to separate out an interface class FileClassifier and at least one implementation class which we shall here give as FileClassifierImpl. Now interfaces cannot have static methods, so we shall have to turn the method into a public instance method. In addition, a class object for this interface, and later instances implementing this will need to be shipped around by RMI, so it should extend Serializable



package option1;

import java.io.Serializable;

/**
 * FileClassifier.java
 *
 *
 * Created: Wed Mar 17 14:14:36 1999
 *
 * @author Jan Newmarch
 * @version
 */

public interface FileClassifier extends Serializable {
    
    public MIMEType getMIMEType(String fileName);
    
} // FileClasssifier

The implementation of this is straightforward



package option1;

/**
 * FileClassifierImpl.java
 *
 *
 * Created: Wed Mar 17 14:22:13 1999
 *
 * @author Jan Newmarch
 * @version
 */

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

The class MIMEType does not need to be moved around, so its definition remains unaltered



package option1;

/**
 * MIMEType.java
 *
 *
 * Created: Wed Mar 17 14:17:32 1999
 *
 * @author Jan Newmarch
 * @version
 */

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

The server for this just needs to create an instance of the exportable service and register this. Note that this server only sticks around for 10 seconds (10,000 milliseconds) but it requests that the exported service should last forever (Lease.FOREVER. If the lease request is granted, then there is no need for the server to hang around longer than needed to upload the exported service.

3. Option 3: Uploading a Proxy

This file is Copyright ©Jan Newmarch (http://jan.newmarch.name) jan@newmarch.name

The copyright is the OpenContent License (http://www.opencontent.org/opl.shtml), which is the ``document'' version of the GNU OpenSource license.