IM269 Coursework 3: HitServer.java


// IM269 - Coursework 3: RMI Client/Server
// Semester A, 7th December 1998
// Eamonn Martin (BSc Computing)
// Student ID: 96/D59682
// efm001@unl.ac.uk

// RMI Hit Counter Server

package hitter;

import java.io.*;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.UnicastRemoteObject;
import java.text.*;
import java.util.*;

import hitter.HitCounter.Info;

// Static methods for reading and updating a hit-count properties file
class HitsLog {
	private static final String
		title =      "# HitCounter Log File",		// Log title
		p_created =  "created",				// Creation date property
		p_set =      "",				// Set-ID property
		hitset =     "Hit",				// Hit-count set-ID
		p_url =      "url",				// File URL property
		p_hits =     "hits";				// Hit-count property

	// Return a DateFormat object to use for date conversions
	private static DateFormat getDateFormat() {
		return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
	}

	// Open and return an output stream for a new log-file
	private static OutputStream logCreate(File log) throws IOException {
		OutputStream o = new FileOutputStream(log);	// Create new log
		Lib.writeLine(title, o);			// Write log title
		return o;
	}

	// Create and initialize a new log file (if none already exists)
	private static boolean logInit(String logfile) {
		File f = new File(logfile);
		OutputStream o = null;
		boolean ok = true;
		if (!f.exists()) try {				// No log exists?
			o = logCreate(f);			// Create a log-file
			PropertySet p = new PropertySet();	// Create log properties
			String s = getDateFormat().format(new Date());
			p.put(p_created, s);			// Set creation date
			p.save(o);				// Save log properties
		} catch (IOException e) {
			ok = false;
		} finally {
			try { if (o!=null) o.close(); } catch (IOException e) {}
		}
		return ok;
	}	

	// Return true if a property set represents the hit-counter for a URL
	private static boolean isFileEntry(PropertySet p, String url) {
		return	!p.hasProperty(p_set, hitset) ? false :
			!p.hasProperty(p_url, url)    ? false : true;
	}

	// Update logfile by updating or appending an entry for file
	public static int newHit(String url, String logfile) {
		String tmpfile = logfile + ".tmp";
		File f = new File(logfile), t = new File(tmpfile);
		InputStream i = null;
		OutputStream o = null;
		int c = -1;
		if (logInit(logfile)) try {			// Log file exists?
			i = new FileInputStream(f);		// Existing log file
			o = logCreate(t);			// Updated log file
			String s;
			PropertySet p = new PropertySet();	// Entry properties
			while (p.loadSet(i)) {			// Get next set
				if (isFileEntry(p, url)) {	// Found url entry?
				 	s = p.getProperty(p_hits);
					c = Lib.intValue(s)+1;	// Get new count
					p.put(p_hits, "" + c);	// Update property
				}
				p.save(o);			// Write property set
			}
			if (c==-1) {				// No entry for file?
				p.put(p_set, hitset);		// Property-set ID
				p.put(p_url, url);		// Set the URL
				p.put(p_hits, "" + (c = 1));	// Initialize count
				p.save(o);			// Write property set
			}
		} catch (IOException e) {
			c = -1;
		} finally {
			try { if (o!=null) o.close(); } catch (IOException e) {}
			try { if (i!=null) i.close(); } catch (IOException e) {}
		}
		if (c!=-1) {	// Delete logfile, rename tmpfile
			if (f.exists()) if (!f.delete()) c = -1;
			if (c!=-1) if (!t.renameTo(f)) c = -1;
		}
		return c;
	}

	// Return log info (all properties not within a named set) from logfile
	private static Properties logInfo(String logfile) {
		PropertySet globals = new PropertySet();	// Global properties
		InputStream i = null;
		try {
			i = new FileInputStream(logfile);	// Open log-file
			PropertySet p = new PropertySet();
			while (p.loadSet(i))			// Load next set
				if (p.getProperty("")==null)	// Untitled set?
					globals.addProperties(p);
		} catch (IOException e) {}
		finally {
			try { if (i!=null) i.close(); } catch (IOException e) {}
		}
		return globals;
	}

	// Update the hit-count for url (if !null) and return info from the log-file
	public static Info getInfo(String url, String logfile) {
 		if (!logInit(logfile)) return null;		// Check/create log
 		Info i = new Info();				// Create Info object
 		i.logfile = logfile;				// Set log-file name
 		Properties p = logInfo(logfile);		// Get global log info
 		String s = p.getProperty(p_created);		// Get creation date
		if (s==null) return null;			// No date = not a log
		try { i.created = getDateFormat().parse(s); }	// Parse date string
		catch (ParseException e) {}
 		if (url!=null) {				// URL specified?
 			i.url = url;				// Set target URL
 			i.hits = newHit(url, logfile);		// Get URL hit-count
 		}
 		return i;
	}

	// Display the results of a local test (update local log - no RMI)
	public static void test(String url, String log) {
		Info i = getInfo(url, log);
		if (i==null) return;
		System.out.println("Log File.: " + i.logfile);
		System.out.println("Created..: " + getDateFormat().format(i.created));
		System.out.println("URL......: " + i.url);
		System.out.println("Hits.....: " + i.hits);
	}
}

// HitCounter RMI Server Implementation
public class HitServer extends UnicastRemoteObject implements HitCounter {
	private static final String name = "HitCounter";	// RMI registry name
	private static String log = "hits.log";			// Default log-file

	// Remote constructor
	public HitServer() throws RemoteException { super(); }

	// Update and return the hit-count for a URL (no log-info)
	public int getHits(String url, String log) throws RemoteException {
 		return HitsLog.newHit(url, (log!=null) ? log : this.log);
	}

	// Update the hit-count for a URL and return info from the log-file
	public Info getInfo(String url, String log) throws RemoteException {
 		return HitsLog.getInfo(url, (log!=null) ? log : this.log);
	}

	// Create and register a HitServer object with the local RMI registry
	public static void main(String args[]) {
		System.setSecurityManager(new RMISecurityManager());
		if (args.length > 0) log = args[0];		// Change default log?
		if (args.length > 1) HitsLog.test(args[1], log);// Local test?
		else try {					// Start server
			Registry r = LocateRegistry.getRegistry();
			HitServer obj = new HitServer();	// Create a HitServer
			r.rebind(name, obj);			// Bind in registry
			System.out.println(name + " bound in registry");
		} catch (Exception e) {
			System.out.println(name + " error: " + e.getMessage());
			e.printStackTrace();
		}
	}
}

Go To: IM269: Programming The Internet