package csci3366sample.masterworker.rmi; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; import java.util.List; import csci3366sample.masterworker.FakeTasks; /** * Generic example of master/worker program. * "Tasks" just consist of sleeping for a specified time; times to wait are * generatedly randomly from 1 to a specified maximum. * * Command-line arguments: total tasks, maximum task time, * (optional) "--verbose" for verbose output * * Master process for client/server version using RMI. * * This version does not try to measure total execution time; it's not clear * that would be meaningful. * * RMI specifics in this example are loosely based on an example from * Sun's online tutorial. */ public class Master implements MasterInterface { /* variables */ private int numTasks; private int maxTaskTime; private boolean verbose = false; private FakeTasks.SynchTaskQueue taskQueue; private int nextWorker = 0; private int activeWorkers = 0; private int totalTaskTime = 0; private int maxWorkerTotalTaskTime; private int minWorkerTotalTaskTime; /* MasterInterface methods */ /** Register new worker (returns worker ID, or -1 if no more tasks). */ public int registerNewWorker(WorkerInterface w) throws RemoteException { int workerID = addWorker(); if (verbose) { System.out.printf("connection from %s, worker %d\n", w.getHostName(), workerID); } return workerID; } /** Get task from queue (null if none). */ public FakeTasks.Task getTask(WorkerInterface w) throws RemoteException { if (verbose) { System.out.printf("(worker %d) requested task\n", w.getID()); } return taskQueue.get(); } /** Accept result for single task from worker. */ public void reportResult(WorkerInterface w, FakeTasks.Task t, FakeTasks.TaskResult tr) throws RemoteException { if (verbose) { System.out.printf("(worker %d) sent result: %s\n", w.getID(), FakeTasks.toString(t, tr)); } } /** Accept summary result for worker. */ public void reportSummaryResults(WorkerInterface w, int numTasks, int totalTaskTime) throws RemoteException { System.out.printf( "worker %d number of tasks = %d, total task time = %d\n", w.getID(), numTasks, totalTaskTime); updateGlobals(totalTaskTime); removeWorker(); } /* other methods */ /** * Main method. */ public static void main(String[] args) { String usageMessage = "parameters: numTasks maxTaskTime [--verbose]"; final int REQUIRED_PARMS = 2; int numTasks = 0; int maxTaskTime = 0; boolean verbose = false; /* process command-line arguments */ if (args.length < REQUIRED_PARMS) { System.err.println(usageMessage); System.exit(1); } try { numTasks = Integer.parseInt(args[0]); maxTaskTime = Integer.parseInt(args[1]); } catch (NumberFormatException e) { System.err.println(usageMessage); System.exit(1); } if (args.length > REQUIRED_PARMS) { if (args[REQUIRED_PARMS].equals("--verbose")) verbose = true; else { System.err.println(usageMessage); System.exit(1); } } /* do processing */ Master m = null; try { m = new Master(numTasks, maxTaskTime, verbose); m.go(); } /* TODO: improve error handling? */ catch (Exception e) { System.err.println("error in master:\n\t" + e); } /* * shut down: be sure there are no remaining references to * remote objects, since otherwise RMI keeps process(es) running */ if (m != null) { m.cleanupRMI(); } m = null; } /** Constructor. */ public Master(int _numTasks, int _maxTaskTime, boolean _verbose) { numTasks = _numTasks; maxTaskTime = _maxTaskTime; verbose = _verbose; /* initialize variables */ maxWorkerTotalTaskTime = 0; minWorkerTotalTaskTime = maxTaskTime * numTasks; taskQueue = new FakeTasks.SynchTaskQueue( FakeTasks.randomTasks(numTasks, maxTaskTime)); } /** Perform processing. */ public void go() throws MalformedURLException, NotBoundException, RemoteException { /* register self as ready for remote calls */ MasterInterface stub = (MasterInterface) UnicastRemoteObject.exportObject(this, 0); LocateRegistry.getRegistry().rebind(BINDING_NAME, stub); if (verbose) { System.out.println("waiting for workers"); } /* wait for all tasks to be assigned */ synchronized (taskQueue) { while (!taskQueue.isEmpty()) { try { taskQueue.wait(); } catch (InterruptedException e) { System.err.println("this should not happen"); e.printStackTrace(); } } } if (verbose) { System.out.println("all tasks assigned"); } /* wait for all workers to report results */ synchronized (this) { while (activeWorkers > 0) { try { wait(); } catch (InterruptedException e) { System.err.println("this should not happen"); e.printStackTrace(); } } } /* print summary information */ System.out.printf("\nRMI client/server version\n"); System.out.printf("number of workers = %d\n", nextWorker); System.out.printf("number of tasks = %d\n", numTasks); System.out.printf("max task time = %d\n", maxTaskTime); System.out.printf("total task time = %d\n", totalTaskTime); System.out.printf("total task time in workers ranges from %d to %d\n", minWorkerTotalTaskTime, maxWorkerTotalTaskTime); } /** * Clean up all RMI-related resources. * (If references still exist to remote objects, RMI keeps process(es) * running.) */ public void cleanupRMI() { /* TODO: improve error handling? */ /* unregister self */ try { UnicastRemoteObject.unexportObject(this, false); } catch (Exception e) { System.err.println("error shutting down master:\n\t" + e); } try { LocateRegistry.getRegistry().unbind(BINDING_NAME); } catch (Exception e) { System.err.println("error shutting down master:\n\t" + e); } } /** * Add worker and return ID (thread-safe). */ private synchronized int addWorker() { int workerID = nextWorker++; ++activeWorkers; return workerID; } /** * Update globals when worker finishes (thread-safe). */ private synchronized void updateGlobals(int workerTotalTaskTime) { maxWorkerTotalTaskTime = Math.max(maxWorkerTotalTaskTime, workerTotalTaskTime); minWorkerTotalTaskTime = Math.min(minWorkerTotalTaskTime, workerTotalTaskTime); totalTaskTime += workerTotalTaskTime; } /** * Remove worker (thread-safe). */ private synchronized void removeWorker() { if (--activeWorkers == 0) { notifyAll(); } } }