/* * xFS.java -- * * java animation of xFS operations. * * Copyright 1995 Regents of the University of California * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that this copyright * notice appears in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. * * rcsid = "$Header: /disks/barad-dur/now/rywang/src/java/classes/xfs/xFS.java,v 1.9 1995/10/06 05:34:04 rywang Exp $ xFS (Berkeley)" */ import java.awt.*; import java.awt.image.*; import java.net.*; import java.applet.*; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import ryw.*; /* *********************************************************************** * some more low level widgets. *********************************************************************** */ /* * button with a picture and a label. used to denote a machine. */ class XButton extends Canvas { protected Image image; protected boolean down; protected String label; public XButton (String label, Image image) { this.label = label; this.image = image; } protected String getLabel () { return label; } public void paint (Graphics g) { Rectangle bounds = bounds (); Font font = getFont (); Font newFont; FontMetrics fm; String label = getLabel (); g.setColor (down ? Color.gray : Color.lightGray); g.fill3DRect (0, 0, bounds.width, bounds.height, !down); g.setFont (newFont = new Font (font.getName (), font.BOLD, font.getSize () + 2)); g.setColor (down ? Color.yellow : Color.blue); g.drawImage (image, 0, 0, this); fm = g.getFontMetrics (newFont); g.drawString (label, (bounds.width - fm.stringWidth (label)) / 2 - 6, 27); } public void setDown(boolean down) { if (down != this.down) { this.down = down; repaint (); } } public boolean mouseDown (Event ev, int x, int y) { setDown (inside (x, y)); getParent ().action (ev, "Focus"); return true; } public boolean mouseDrag(Event ev, int x, int y) { return true; } public boolean mouseUp(Event ev, int x, int y) { return true; } } /* * panel with further extensions. */ class XPanel extends XPanel0 { public boolean action (Event evt, Object arg) { if (evt.target instanceof XButton || evt.target instanceof Button) { getParent ().action (evt, arg); } return true; } } /* *********************************************************************** * some higher level panels (more xFS specific) *********************************************************************** */ /* * a bunch of buttons: the machine, "Create", "Flush", and "Manager", * in a stack. */ class HostPanel extends XPanel { final int buttonWidth = 60; final int buttonHeight = 20; final int hostWidth = 60; final int hostHeight = 56; public int hostID; public String hostName; public XButton machineButton; public HostPanel (int hostID, Image image) { int y; this.hostID = hostID; this.hostName = Integer.toString (hostID); machineButton = new XButton (hostName, image); add (machineButton, 0, 0, hostWidth, hostHeight); y = hostHeight; add (new Button ("Create"), 0, y, buttonWidth, buttonHeight); y += buttonHeight; add (new Button ("Flush"), 0, y, buttonWidth, buttonHeight); y += buttonHeight; add (new Button ("Manager"), 0, y, buttonWidth, buttonHeight); y += buttonHeight; resize (new Dimension (buttonWidth, y)); } } /* * the space occupied by one machine, including a bunch of buttons, * and the empty space around it. its main purpose is to figure out * how to place the "file blocks" in this space. */ class MachPanel extends XPanel { final int leftBlkX = 62; final int topBlkY = 3; final int slope = 3; final int shiftIncrX = 3; final int shiftIncrY = 60; protected int rightBlkX; protected int intercep; protected int currX; protected int currInter; public int hostID; public XButton machineButton; protected int side; public Vector dirty; public int direction; public MachPanel (Image image, int hostID, int side) { HostPanel hostPanel; this.hostID = hostID; this.side = side; rightBlkX = side - leftBlkX - 20; intercep = side - 30; currX = 0; currInter = intercep; add (new XFrame (side, side), 0, 0, side, side); add (hostPanel = new HostPanel (hostID, image), 2, 2); machineButton = hostPanel.machineButton; resize (new Dimension (side, side)); dirty = new Vector (16, 16); } public MachPanel (Image image, int hostID, int side, int dir) { this (image, hostID, side); direction = dir; } private int linear (int xoff, int yoff) { return slope * xoff + yoff; } private int solve (int intercep, int slope) { return -intercep/slope; } /* * returns where the next file block is supposed to go. * the file blocks are arranged so they form a bunch of rows * parallel to the "main diagonal". */ public Point nextBlkLoc () { int nextX, nextY, nextInter; Point location = location (); int x = location.x; int y = location.y; Point p = new Point (x + currX + leftBlkX, y + linear (currX, currInter) + topBlkY); /* * the default next position is to simply move along the * imaginery line whose slope is "slope" and whose y-intercept * is "intercep". */ nextX = currX + shiftIncrX; nextY = linear (nextX, currInter); if (nextX <= rightBlkX && nextY <= intercep) { /* * the default next position is still within our * alotted space, we are done! */ currX = nextX; } else { /* * we are moving out of the alloted space, so we have * to start a new line. */ nextInter = currInter - shiftIncrY; if (nextInter < -intercep) nextInter = intercep; nextX = 0; nextY = linear (0, nextInter); if (nextY < 0) nextX = solve (nextInter, slope); currX = nextX; currInter = nextInter; } return p; } public boolean action (Event evt, Object arg) { if (evt.target instanceof XButton || evt.target instanceof Button) { getParent ().action (new Event (this, evt.id, evt.arg), arg); return true; } return super.action (evt, arg); } } /* * one "file block". */ class Block extends Button implements Runnable { public static final int blkWidth = 20; public static final int blkHeight = 23; final int blkGrowSleep = 6; final String blkFont = "Helvetica"; public int blkID; public boolean owned; public MachPanel machine; protected boolean blkCreated; protected boolean blkKill; Thread sizer; boolean blkDrawn; public Block (String blkName, MachPanel machine) { super (blkName); this.blkID = Integer.parseInt (blkName); this.machine = machine; blkCreated = false; blkKill = false; blkDrawn = false; } public Block (int blkID, MachPanel machine) { this (Integer.toString (blkID), machine); } public void Kill () { if (blkKill) return; blkKill = true; start (); } public synchronized void paint (Graphics g) { int w, h; if ((blkDrawn || blkCreated) && !blkKill) { super.paint (g); return; } if (!blkDrawn && !blkCreated) { blkDrawn = true; start (); } } public synchronized void start () { if (sizer == null) { sizer = new Thread (this); } sizer.start (); } public synchronized void stop () { sizer.stop (); } public synchronized void run () { Graphics g = getGraphics (); int w,h; if (!blkCreated) { /* * this is the first time we are painting this block. * grow it slowly. */ for (w = 0, h = 0; w < blkWidth || h < blkHeight;) { if (w < blkWidth) w++; if (h < blkHeight) h++; this.resize (w, h); super.paint (g); try { Thread.sleep (blkGrowSleep); } catch (InterruptedException e) { resize (blkWidth, blkHeight); break; } } blkCreated = true; return; } if (blkKill) { /* * we are about to destroy this block, * shrink it down slowly. */ for (w = blkWidth, h = blkHeight; w > 0 || h > 0;) { if (w > 0) w --; if (h > 0) h --; this.resize (w, h); super.paint (g); try { Thread.sleep (blkGrowSleep); } catch (InterruptedException e) { resize (0, 0); break; } } getParent ().remove (this); return; } } } /* * read, write, or delete checkbox. */ class ActionPanel extends XPanel { public final static int READ = 0; public final static int WRITE = 1; public final static int DELETE = 2; xFSPanel xpanel; public ActionPanel (xFSPanel xpanel) { this.xpanel = xpanel; setLayout (new FlowLayout ()); CheckboxGroup group = new CheckboxGroup (); add (new Checkbox ("Read", group, true)); add (new Checkbox ("Write", group, false)); add (new Checkbox ("Delete", group, false)); xpanel.setAction (READ); } public boolean action (Event evt, Object arg) { if (evt.target instanceof Checkbox) { String label = ((Checkbox) evt.target).getLabel (); xpanel.tickerMsg ("switch action to: " + label + "... "); if (label.equals ("Read")) { xpanel.setAction (READ); } else if (label.equals ("Write")) { xpanel.setAction (WRITE); } else if (label.equals ("Delete")) { xpanel.setAction (DELETE); } } return true; } } /* * the "top-level" panel. */ class xFSPanel extends XPanel { final int netWidth = 176; final int netHeight = 113; final int quesWidth = 32; final int quesHeight = 32; final int bombWidth = 27; final int bombHeight = 39; final int spacing = 4; final int topOff = 140; final int leftOff = 80; final int bottomOff = 100; final int tickOff = 20; final int tickHeight = 30; final int stripeFragBlks = 2; final int stripeSSGsize = 4; final int stripeFragSleep = 1000; public static final int NORTH = 0; public static final int EAST = 1; public static final int SOUTH = 2; public static final int WEST = 3; public int numHosts; Image bombImage; Image quesImage; int xwidth; MachPanel currMach; Counter blkIDcount; MachPanel machines[]; Management management; Point netPos; Dimension quesDim; Dimension bombDim; XTicker ticker; int currAction; int stripeSegBlks; public xFSPanel () { blkIDcount = new Counter (); currMach = null; quesDim = new Dimension (quesWidth, quesHeight); bombDim = new Dimension (bombWidth, bombHeight); } public void initialize (int numHosts, int xwidth, Image hostImage, Image myriImage, Image bombImage, Image quesImage) { int quart; int i; int side; int x; int y; int wide; int high; int hosts; MachPanel thisMach; int tickWidth; this.numHosts = numHosts; this.xwidth = xwidth; this.bombImage = bombImage; this.quesImage = quesImage; /* * figure out how big things have to be and initialize. */ quart = numHosts / 4; side = (xwidth - (spacing * quart) - topOff - bottomOff) / (quart + 1); x = leftOff; y = topOff; hosts = 0; machines = new MachPanel [numHosts]; stripeSegBlks = (stripeSSGsize - 1) * stripeFragBlks; tickWidth = xwidth - topOff - bottomOff; /* * ticker tape first. */ add (ticker = new XTicker (tickWidth, tickHeight), x, tickOff, tickWidth, tickHeight); ticker.initialize ("initializing... "); ticker.start (); /* * checkboxes next */ add (new ActionPanel (this), x, tickOff + tickHeight + 5, tickWidth, 30); /* * arrange all the hosts on the perimeter of a square * in clockwise fashion. * the first loop draws the northern side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, NORTH), x, y, side, side); machines[hosts] = thisMach; if (currMach == null) { /* * by default, the first machine becomes active. */ currMach = thisMach; thisMach.machineButton.setDown (true); } x += side + spacing; } wide = x + side; /* * the eastern side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, EAST), x, y, side, side); machines[hosts] = thisMach; y += side + spacing; } high = y + side; /* * the southern side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, SOUTH), x, y, side, side); machines[hosts] = thisMach; x -= side + spacing; } /* * the western side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, WEST), x, y, side, side); machines[hosts] = thisMach; y -= side + spacing; } /* * finally draw the network. */ x = leftOff+ (wide - leftOff - netWidth) / 2; y = topOff + (high - topOff - netHeight) / 2; add (new XPict (myriImage), x, y, netWidth, netHeight); netPos = new Point (wide/2, high/2); /* * kludge: "prefetch" some pictures. */ add (new XPict (quesImage), leftOff, high + 8, quesWidth, quesHeight); add (new XPict (bombImage), leftOff + quesWidth + 8, high + 8, bombWidth, bombHeight); resize (xwidth, xwidth); /* * initialize the managers. */ management = new Management (machines); } public void tickerMsg (String msg) { ticker.addString (msg); } public void setAction (int currAction) { this.currAction = currAction; } /* * create a file block. */ protected void createBlock (MachPanel machine) { Block block; Point loc; /* * create an animation object. */ XMover mover = new XMover (); /* * get a new block ID. */ int newBlkID = blkIDcount.current (); /* * figure out who is the manager for this block. */ MachPanel manager = management.managerMachine (newBlkID); tickerMsg ("Creating block " + newBlkID + " blah... "); /* * animate a message from the creater to the manager. */ mover.addElement (new XMoveElement (this, quesImage, quesDim, machine.location (), netPos, manager.location (), true)); mover.start (); /* * add the new block to this machine. */ loc = machine.nextBlkLoc (); block = new Block (blkIDcount.nextStr (), machine); add (block, loc.x, loc.y); /* * update the manager meta data. */ management.createBlk (block); machine.dirty.addElement (block); } /* * make the machine that generated the event into the * current active machine. */ protected void changeCurrentMachine (MachPanel newMachine) { currMach.machineButton.setDown (false); currMach = newMachine; currMach.machineButton.setDown (true); } /* * read a block from a peer client. */ protected void readBlock (Block block) { tickerMsg ("Boom!! "); /* * if the current machine already has the block, then done. */ if (management.findMachine (block.blkID, currMach)) { return; } Point newBlockDest = currMach.nextBlkLoc (); Point newBlockSrc = block.location (); Block newBlock = new Block (block.blkID, currMach); /* * figure out who is the manager for this block. */ MachPanel manager = management.managerMachine (block.blkID); /* * create an animation object. */ XMover mover = new XMover (); /* * animate a message from the reader to the manager. */ mover.addElement (new XMoveElement (this, quesImage, quesDim, currMach.location (), netPos, manager.location (), true)); /* * animate a message from the manager to a peer client. */ mover.addElement (new XMoveElement (this, quesImage, quesDim, manager.location (), netPos, block.machine.location (), true)); /* * animate the block from the peer to the reader. */ mover.addElement (new XMoveElement (this, newBlock, newBlockSrc, netPos, newBlockDest, false, false, newBlock.size ())); mover.start (); /* * update the manager meta data. */ management.readBlk (newBlock); } /* * kill all the blocks, except "block" itself. */ protected void invalBlocks (Block block, MachPanel manager) { /* * get a manager entry for the block. */ ManageEntry others = management.removeMachine (block.blkID, currMach); /* * how many other machines have this block. */ int numOthers = others.size (); /* * position of these blocks. */ Point ends[] = new Point[numOthers]; /* * a list of these blocks. */ Enumeration machineBlocks = others.elements (); /* * find out the positions of these blocks. */ for (int i = 0; machineBlocks.hasMoreElements (); i++) { Block machineBlock = (Block) machineBlocks.nextElement (); ends[i] = machineBlock.location (); } /* * create an animation object. */ XMover mover = new XMover (); /* * animate a message from the current machine to the manager. */ mover.addElement (new XMoveElement (this, quesImage, quesDim, currMach.location (), netPos, manager.location (), true)); if (numOthers > 0) { /* * animate a bomb from the manager to the net. */ mover.addElement (new XMoveElement (this, bombImage, quesDim, manager.location (), netPos, true)); /* * animate a bunch of bombs from the net to the blocks. */ mover.addElement (new XMoveElement (this, bombImage, bombDim, netPos, ends, true)); } mover.start (); mover.waitTilDone (); /* * shrink down and kill these blocks. */ machineBlocks = others.elements (); for (int i = 0; machineBlocks.hasMoreElements (); i++) { Block machineBlock = (Block) machineBlocks.nextElement (); machineBlock.machine.dirty.removeElement (machineBlock); machineBlock.Kill (); } } /* * overwrites an existing block. */ protected void writeBlock (Block block) { tickerMsg ("foo "); int blkID = block.blkID; MachPanel manager = management.managerMachine (blkID); Block ownBlock = management.getBlock (blkID, currMach); /* * if the writer does not have/own the block, kill the others. */ if ((ownBlock == null) || (!ownBlock.owned)) invalBlocks (block, manager); /* * create and add the new block. */ if (ownBlock == null) { Point newLoc = currMach.nextBlkLoc (); ownBlock = new Block (blkID, currMach); add (ownBlock, newLoc); } /* * update manager meta data. */ management.writeBlk (ownBlock); currMach.dirty.addElement (ownBlock); } /* * flush dirty blocks on a machine. */ protected void flushBlocks (MachPanel machine) { Enumeration dirtyBlks = machine.dirty.elements (); int numBlks = machine.dirty.size (); int i; Block newBlks[] = new Block[stripeSegBlks]; Block moveBlks[]; Button frags[]; boolean more; for (i = 0;;) { Block blk = (Block) dirtyBlks.nextElement (); Block newBlk = new Block (blk.blkID, machine); add (newBlk, blk.location ()); System.out.println ("adding " + newBlk.getLabel ()); newBlks[i] = newBlk; i++; if ((more = dirtyBlks.hasMoreElements ()) && i < stripeSegBlks) { System.out.println ("continuing..."); continue; } System.out.println ("striping..."); moveBlks = moveToLog (machine, newBlks, i); frags = drawFrags (machine, moveBlks); if (!more) break; i = 0; } } /* * move a bunch of dirty blocks to the log area. */ protected Block[] moveToLog (MachPanel machine, Block newBlks[], int i) { Block moveBlks[] = new Block[i]; Point moveStarts[] = new Point[i]; Point moveEnds[] = new Point[i]; boolean incrX; int x, y; Point machineLoc = machine.location (); Dimension machineSize = machine.size (); LogLoc logLoc = new LogLoc (machine, new Dimension (Block.blkWidth, Block.blkHeight), machine.direction); x = logLoc.x; y = logLoc.y; incrX = logLoc.incrX; for (int j = 0; j < i; j++) { Block blk = newBlks[j]; blk.resize (Block.blkWidth, Block.blkHeight); moveBlks[j] = blk; moveStarts[j] = blk.location (); moveEnds[j] = new Point (x, y); if (incrX) x += Block.blkWidth; else y += Block.blkHeight; } XMover mover = new XMover (); mover.addElement (new XMoveElement (this, moveBlks, moveStarts, moveEnds, false, true, null)); mover.start (); mover.waitTilDone (); return moveBlks; } /* * draws a bunch of fragments over the blocks in the log. */ protected Button[] drawFrags (MachPanel machine, Block moveBlks[]) { int numBlks = moveBlks.length; int numFrags = (numBlks + 2) / 2; Button frags[] = new Button[numFrags]; Button frag; int x, y, j; int fragWidth; int fragHeight; LogLoc logLoc = new LogLoc (machine, new Dimension (Block.blkWidth, Block.blkHeight), machine.direction); if (logLoc.incrX) { fragWidth = Block.blkWidth * stripeFragBlks; fragHeight = Block.blkHeight; } else { fragWidth = Block.blkWidth; fragHeight = Block.blkHeight * stripeFragBlks; } x = logLoc.x; y = logLoc.y; for (j = 0; j < numFrags; j++) { frag = new Button ("F" + j); add (frag, x, y, fragWidth, fragHeight); try { Thread.sleep (stripeFragSleep); } catch (InterruptedException e) { } if (logLoc.incrX) x += fragWidth; else y += fragHeight; } for (j = 0; j < numBlks; j++) remove (moveBlks[j]); return frags; } /* * delete a block. */ protected void deleteBlock (Block block) { tickerMsg ("BANG!! "); int blkID = block.blkID; MachPanel manager = management.managerMachine (blkID); /* * kill all other blocks. */ invalBlocks (block, manager); /* * update manager meta data. */ management.deleteBlk (block); /* * kill my own block. */ block.Kill (); block.machine.dirty.removeElement (block); } /* * some interesting event happened in the "top" panel. */ public boolean action(Event evt, Object arg) { if (evt.target instanceof MachPanel) { if ("Create".equals (arg)) { createBlock ((MachPanel) evt.target); } if ("Flush".equals (arg)) { flushBlocks ((MachPanel) evt.target); } if ("Focus".equals (arg)) { changeCurrentMachine ((MachPanel) evt.target); } } if (evt.target instanceof Block) { Block block = (Block) evt.target; switch (currAction) { case ActionPanel.READ: { readBlock (block); break; } case ActionPanel.WRITE: { writeBlock (block); break; } case ActionPanel.DELETE: { deleteBlock (block); break; } } } return true; } } /* *********************************************************************** * misc. *********************************************************************** */ class LogLoc { final int spacing = 3; int x, y; boolean incrX; public LogLoc (Component comp, Dimension off, int direction) { Point loc = comp.location (); Dimension size = comp.size (); incrX = true; switch (direction) { case xFSPanel.NORTH: { x = loc.x; y = loc.y - off.height - spacing; break; } case xFSPanel.EAST: { x = loc.x + size.width + spacing; y = loc.y; incrX = false; break; } case xFSPanel.SOUTH: { x = loc.x; y = loc.y + size.height + spacing; break; } case xFSPanel.WEST: { x = loc.x - off.width - spacing; y = loc.y; incrX = false; break; } default: { System.err.println ("LogLoc: bad direction"); break; } } } } class Counter { protected int counter; public Counter () { counter = 0; } public Counter (int i) { counter = i; } public int next () { return counter++; } public String nextStr () { return Integer.toString (next ()); } public int current () { return counter; } } /* *********************************************************************** * location management *********************************************************************** */ /* * a manager is a hash table of some blocks. */ class Manager extends Hashtable { public MachPanel machine; public Manager (MachPanel machine) { this.machine = machine; } } /* * an entry kept by a manager is a list of blocks. */ class ManageEntry extends Vector { public int blkID; public ManageEntry (int blkID) { this.blkID = blkID; } public ManageEntry (int blkID, Block block) { this (blkID); block.owned = true; addElement (block); } /* * removes all elements. then add this block to the element list. */ public void setBlock (Block block) { removeAllElements (); block.owned = true; addElement (block); } /* * returns the block on a machine */ public Block getBlock (MachPanel machine) { Enumeration machineBlocks = this.elements (); while (machineBlocks.hasMoreElements ()) { Block machineBlock = (Block) machineBlocks.nextElement (); if (machine == machineBlock.machine) { return machineBlock; } } return null; } /* * is "machine" in the entry? */ public boolean findMachine (MachPanel machine) { return getBlock (machine) != null; } /* * remove "machine" from the entry if it's in it. */ public ManageEntry removeMachine (MachPanel machine) { ManageEntry newEntry = (ManageEntry) this.clone (); Enumeration machineBlocks = newEntry.elements (); while (machineBlocks.hasMoreElements ()) { Block machineBlock = (Block) machineBlocks.nextElement (); if (machine == machineBlock.machine) { newEntry.removeElement (machineBlock); break; } } return newEntry; } /* * turn off owned bits in all sharers. */ public void noOwner () { Enumeration blocks = this.elements (); while (blocks.hasMoreElements ()) { Block block = (Block) blocks.nextElement (); block.owned = false; } } } /* * location management: an array of managers. */ class Management { Manager managers []; public Management (MachPanel machines[]) { int i; int numMachines; numMachines = machines.length; managers = new Manager[numMachines]; for (i = 0; i < numMachines; i++) { managers[i] = new Manager (machines[i]); } } private int managerIndex (int blkID) { return blkID % managers.length; } private Manager whichManager (int blkID) { return managers[managerIndex (blkID)]; } public MachPanel managerMachine (int blkID) { return whichManager (blkID).machine; } public void createBlk (Block block) { ManageEntry entry = new ManageEntry (block.blkID, block); Manager manager = whichManager (block.blkID); manager.put (new Integer (block.blkID), entry); } public void deleteBlk (Block block) { Manager manager = whichManager (block.blkID); manager.remove (new Integer (block.blkID)); } private ManageEntry findEntry (int blkID) { Manager manager = whichManager (blkID); ManageEntry entry = (ManageEntry) manager.get (new Integer (blkID)); return entry; } /* * one more machine has read this block, * add the new block to the list of sharers. */ public void readBlk (Block blkCopy) { ManageEntry entry = findEntry (blkCopy.blkID); entry.addElement (blkCopy); entry.noOwner (); } /* * returns the block on a machine. */ public Block getBlock (int blkID, MachPanel machine) { ManageEntry entry = findEntry (blkID); return entry.getBlock (machine); } /* * does "machine" have block "blkID"? */ public boolean findMachine (int blkID, MachPanel machine) { ManageEntry entry = findEntry (blkID); return entry.findMachine (machine); } /* * find all other sharers of a block. */ public ManageEntry removeMachine (int blkID, MachPanel machine) { ManageEntry entry = findEntry (blkID); return entry.removeMachine (machine); } /* * overwrites an existing block. */ public void writeBlk (Block block) { ManageEntry entry = findEntry (block.blkID); entry.setBlock (block); } } /* *********************************************************************** * the top-level applet *********************************************************************** */ public class xFS extends Applet { final int defaultWidth = 600; final int defaultNumHosts = 12; public xFS () { this.setLayout (new BorderLayout ()); } public void init() { int xwidth; int numHosts; Image hostImage; Image myriImage; Image bombImage; Image quesImage; String s; xFSPanel xp; URL docBase; numHosts = defaultNumHosts; xwidth = defaultWidth; docBase = getDocumentBase (); hostImage = getImage (docBase, "pics/host.xbm"); myriImage = getImage (docBase, "pics/myri.gif"); bombImage = getImage (docBase, "pics/bomb.xbm"); quesImage = getImage (docBase, "pics/ques.gif"); /* * get the parameters from the "command arguments": * number of hosts, and the width of the main window. */ s = getParameter ("hosts"); if (s != null) numHosts = Integer.parseInt (s); s = getParameter ("width"); if (s != null) xwidth = Integer.parseInt (s); /* * create the top level panel. */ this.add (xp = new xFSPanel ()); xp.initialize (numHosts, xwidth, hostImage, myriImage, bombImage, quesImage); } public static void main (String args[]) { Frame f = new Frame ("xFS"); xFS xfs = new xFS (); xfs.init (); xfs.start (); f.add ("Center", xfs); f.resize (600, 600); f.show (); } }