/* * 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.18 1996/02/10 05:13:57 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 java.io.*; import ryw.*; /* *********************************************************************** * some more low level widgets. *********************************************************************** */ class XBlob extends Canvas { public String label = ""; public Color bg = Color.blue; public Color fg = Color.yellow; int fontSize = 24; public XBlob (Dimension d) { resize (d); } public XBlob (Dimension d, String label) { this (d); this.label = label; } public XBlob (Dimension d, int label) { this (d, Integer.toString (label)); } public XBlob (Dimension d, int label, int fontSize) { this (d, Integer.toString (label)); this.fontSize = fontSize; } public XBlob (Dimension d, String label, Color bg) { this (d, label); this.bg = bg; } public XBlob (Dimension d, String label, Color bg, Color fg) { this (d, label, bg); this.fg = fg; } public XBlob (Dimension d, String label, Color bg, Color fg, int fontSize) { this (d, label, bg, fg); this.fontSize = fontSize; } public void paint (Graphics g) { Dimension size = size (); g.setColor (bg); g.fill3DRect (0, 0, size.width, size.height, true); g.setColor (fg); Font theFont = g.getFont (); if (fontSize != 0) { theFont = new Font (theFont.getName (), theFont.BOLD, fontSize); g.setFont (theFont); } FontMetrics fm = g.getFontMetrics (theFont); Rectangle bounds = bounds (); g.drawString (label, (bounds.width - fm.stringWidth (label)) / 2, (bounds.height - fm.getHeight ()) / 2 + fm.getHeight ()); } } class XBalloon extends Canvas { final int defaultFontSize = 14; final int bigFontSize = 30; final int resizeInter = 10; final int resizeInc = 25; final int resizeSteps = 5; final int bigSleep = 6000; public String messages[]; protected Image image; public int fontSize = defaultFontSize; public boolean skipWords = false; public XBalloon (Image image, Dimension d, String messages[]) { this.image = image; this.messages = messages; resize (d); } public XBalloon (Image image, Dimension d, String messages[], int fontSize) { this (image, d, messages); this.fontSize = fontSize; } public void paint (Graphics g) { int xp[] = new int[3]; int yp[] = new int[3]; Dimension d = size (); /* g.drawImage (image, 0, 0, d.width, d.height, this); */ xp[0] = 0; yp[0] = 40; xp[1] = 10; yp[1] = 60; xp[2] = 10; yp[2] = 80; g.setColor (Color.red); /* g.drawPolygon (xp, yp, 6); */ for (int i = 10; i < 15; i++) g.drawRoundRect (i, i, d.width-i-i, d.height-i-i, 50, 50); g.fillPolygon (xp, yp, 3); g.setColor (Color.blue); if (skipWords) return; Rectangle bounds = bounds (); Font font = g.getFont (); Font newFont = new Font (font.getName (), font.BOLD, fontSize); g.setFont (newFont); FontMetrics fm = g.getFontMetrics (newFont); int fontHeight = fm.getHeight (); int numStrings = messages.length; for (int i = 0; i < numStrings; i++) { g.drawString (messages[i], (bounds.width - fm.stringWidth (messages[i])) / 2 + 2, (bounds.height - fontHeight * numStrings) / 2 + fontHeight * (i + 1)); } } public void expand () { Dimension d; d = size (); d.width += d.width; d.height += d.height; resize (d); } } /** * 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 = g.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, 36)); 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, 40); } 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 = 85; final int buttonHeight = 40; final int hostWidth = 85; final int hostHeight = 80; final int fontSize = 24; public int hostID; public String hostName; public XButton machineButton; public HostPanel (int hostID, Image image, boolean hasAction) { int y; Button but; Font theFont; this.hostID = hostID; this.hostName = Integer.toString (hostID); machineButton = new XButton (hostName, image); add (machineButton, 0, 0, hostWidth, hostHeight); y = hostHeight; add (but = new Button ("Create"), 0, y, buttonWidth, buttonHeight); theFont = new Font ("Dialog", Font.PLAIN, fontSize); but.setFont (theFont); but.setForeground (Color.blue); y += buttonHeight; add (but = new Button ("Flush"), 0, y, buttonWidth, buttonHeight); but.setFont (theFont); but.setForeground (Color.blue); y += buttonHeight; // add (new Button ("Manager"), 0, y, buttonWidth, buttonHeight); if (hasAction) { add (but = new Button ("XTerm"), 0, y, buttonWidth, buttonHeight); but.setFont (theFont); but.setForeground (Color.blue); 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 = 92; 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, int dir, boolean hasAction) { HostPanel hostPanel; this.hostID = hostID; this.side = side; direction = dir; rightBlkX = side - leftBlkX - 20; intercep = side - 55; currX = 0; currInter = intercep; add (new XFrame (side, side), 0, 0); add (hostPanel = new HostPanel (hostID, image, hasAction), 2, 2); machineButton = hostPanel.machineButton; resize (new Dimension (side, side)); dirty = new Vector (16, 16); } public Point topLeft () { Point loc = location (); return new Point (loc.x + leftBlkX, loc.y + topBlkY); } 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 { public static final int blkWidth = 40; public static final int blkHeight = 46; final String blkFont = "Helvetica"; public int blkID; public boolean owned; public MachPanel machine; public Block (String blkName, MachPanel machine) { super (blkName); this.blkID = Integer.parseInt (blkName); this.machine = machine; resize (new Dimension (blkWidth, blkHeight)); this.setFont (new Font("Dialog", Font.PLAIN, 24)); } public Block (int blkID, MachPanel machine) { this (Integer.toString (blkID), machine); } public Block (int blkID, MachPanel machine, boolean drawn) { this (blkID, machine); } public synchronized void Kill () { getParent ().remove (this); } } /** * 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 balloonWidth = 120; final int balloonHeight = 170; final int bombWidth = 27; final int bombHeight = 39; final int spacing = 4; // final int topOff = 175; final int topOff = 160; final int leftOff = 80; final int bottomOff = 100; final int titleOff = 20; final int titleHeight= 50; // final int tickOff = 10; final int tickOff = 0; // final int tickHeight = 25; final int tickHeight = 0; final int stripeFragBlks = 2; final int stripeSSGsize = 4; final int stripeFragSleep = 2000; final int balloonInterSleep = 2000; final int balloonWaitSleep = 3000; final int adjust4 = 115; 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; Image balloonImage; int xwidth; MachPanel currMach; Counter blkIDcount; MachPanel machines[]; Management management; Point netPos; Dimension quesDim; Dimension bombDim; XTicker ticker; int currAction; int stripeSegBlks; String actionServer; int actionPort; Dimension balloonDimension = new Dimension (balloonWidth, balloonHeight); XPict quesPrefetch = null; XPict bombPrefetch = null; 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, Image balloonImage, String actionServer, int actionPort) { int quart; int i; int side; int x; int y; int wide; int high; int hosts; MachPanel thisMach; int tickWidth; boolean hasAction; this.numHosts = numHosts; this.xwidth = xwidth; this.bombImage = bombImage; this.quesImage = quesImage; this.balloonImage = balloonImage; this.actionServer = actionServer; this.actionPort = actionPort; hasAction = actionServer != null; MediaTracker tracker = new MediaTracker (this); tracker.addImage (bombImage, 0); tracker.addImage (quesImage, 1); tracker.addImage (balloonImage, 2); tracker.addImage (hostImage, 3); tracker.addImage (myriImage, 4); try { tracker.waitForAll (); } catch (InterruptedException e) { System.err.println ("unable to load images"); return; } /* * figure out how big things have to be and initialize. */ quart = numHosts / 4; side = (xwidth - (spacing * quart) - topOff - bottomOff) / (quart + 1); if (numHosts == 4) side -= adjust4; x = leftOff; y = topOff; hosts = 0; machines = new MachPanel [numHosts]; stripeSegBlks = (stripeSSGsize - 1) * stripeFragBlks; tickWidth = xwidth - topOff - bottomOff; /* * title first */ add (new XBlob (new Dimension (tickWidth, titleHeight), "xFS: Serverless File Service", Color.lightGray, Color.blue, 24), x, titleOff, tickWidth, titleHeight); /* * ticker tape first. */ /* add (ticker = new XTicker (tickWidth, tickHeight), x, titleOff + titleHeight + tickOff, tickWidth, tickHeight); ticker.initialize ("initializing... "); ticker.start (); */ /* * checkboxes next */ add (new ActionPanel (this), x, titleOff + titleHeight + 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, hasAction), 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; if (numHosts == 4) { wide += adjust4; x += adjust4; } /* * the eastern side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, EAST, hasAction), x, y, side, side); machines[hosts] = thisMach; y += side + spacing; } high = y + side; if (numHosts == 4) { high += adjust4; y += adjust4; } /* * the southern side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, SOUTH, hasAction), x, y, side, side); machines[hosts] = thisMach; x -= side + spacing; } if (numHosts == 4) x -= adjust4; /* * the western side. */ for (i = 0; i < quart; i++, hosts++) { add (thisMach = new MachPanel (hostImage, hosts, side, WEST, hasAction), x, y, side, side); machines[hosts] = thisMach; y -= side + spacing; } if (numHosts == 4) y -= adjust4; /* * 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 (quesPrefetch = new XPict (quesImage), leftOff, high + 8, quesWidth, quesHeight); add (bombPrefetch = new XPict (bombImage), leftOff + quesWidth + 8, high + 8, bombWidth, bombHeight); */ resize (xwidth, xwidth); /* * initialize the managers. */ management = new Management (machines); } protected void writeActionStream (int currMach, String action, int destMach, int destBlok) { OutputStream outStream; PrintStream actionStream; Socket actionSock; if (actionServer == null) return; try { actionSock = new Socket (actionServer, actionPort, true); outStream = actionSock.getOutputStream (); actionStream = new PrintStream (outStream, true); actionStream.println ("xFS command:" + currMach + ":" + action + ":" + destMach + ":" + destBlok); actionStream.close (); outStream.close (); actionSock.close (); } catch (Exception e) { System.err.println ("unable to connect to " + actionServer + " at port " + actionPort); } } 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 blk " + newBlkID + ", informing mngr " + manager.hostID + ". "); Component balloon1 = balloon3Msgs (mover, machine, "host " + machine.hostID, "creating", "block " + newBlkID); Component balloon2 = balloon3Msgs (mover, manager, "host " + manager.hostID + ",", "manager of", "block " + newBlkID); mover.addElement (new XSleepElement (balloonWaitSleep)); mover.addElement (new CompKillAnimElement (this, balloon1)); mover.addElement (new CompKillAnimElement (this, balloon2)); /* * animate a message from the creater to the manager. */ mover.addElement (new XMoveElement (this, quesImage, quesDim, machine.location (), netPos, manager.location (), true)); /* * add the new block to this machine. */ loc = machine.nextBlkLoc (); block = new Block (blkIDcount.nextStr (), machine); mover.addElement (new BlkAddAnimElement (this, loc, block)); mover.start (); /* * update the manager meta data. */ management.createBlk (block); machine.dirty.addElement (block); writeActionStream (currMach.hostID, "create", block.machine.hostID, block.blkID); } /** * make the machine that generated the event into the * current active machine. */ protected void changeCurrentMachine (MachPanel newMachine) { if (quesPrefetch != null) { remove (quesPrefetch); quesPrefetch = null; } if (bombPrefetch != null) { remove (bombPrefetch); bombPrefetch = null; } currMach.machineButton.setDown (false); currMach = newMachine; currMach.machineButton.setDown (true); } protected Component balloonMsg (XMover mover, MachPanel machine, String messages[]) { XBalloon balloon = new XBalloon (balloonImage, balloonDimension, messages); mover.addElement (new XAddElement (this, balloon, machine.topLeft (), balloonDimension)); for (int i = 0; i < balloon.resizeSteps; i++) { mover.addElement (new XSleepElement (balloon.resizeInter)); mover.addElement (new BalloonSkipElem (balloon, true)); mover.addElement (new ItemResizeElem (balloon, balloon.resizeInc)); } mover.addElement (new BalloonSkipElem (balloon, false)); mover.addElement (new BalloonFontElem (balloon, balloon.bigFontSize)); mover.addElement (new ItemResizeElem (balloon, balloon.resizeInc)); mover.addElement (new XSleepElement (balloon.bigSleep)); mover.addElement (new BalloonSkipElem (balloon, true)); mover.addElement (new BalloonFontElem (balloon, balloon.defaultFontSize)); for (int i = 0; i < balloon.resizeSteps; i++) { mover.addElement (new XSleepElement (balloon.resizeInter)); mover.addElement (new ItemResizeElem (balloon, -balloon.resizeInc)); } mover.addElement (new BalloonSkipElem (balloon, false)); mover.addElement (new ItemResizeElem (balloon, -balloon.resizeInc)); return balloon; } protected Component balloon3Msgs (XMover mover, MachPanel machine, String msg1, String msg2, String msg3) { String blnMsgs[] = new String[3]; blnMsgs[0] = msg1; blnMsgs[1] = msg2; blnMsgs[2] = msg3; return balloonMsg (mover, machine, blnMsgs); } protected Component balloon4Msgs (XMover mover, MachPanel machine, String msg1, String msg2, String msg3, String msg4) { String blnMsgs[] = new String[4]; blnMsgs[0] = msg1; blnMsgs[1] = msg2; blnMsgs[2] = msg3; blnMsgs[3] = msg4; return balloonMsg (mover, machine, blnMsgs); } protected Component balloon5Msgs (XMover mover, MachPanel machine, String msg1, String msg2, String msg3, String msg4, String msg5) { String blnMsgs[] = new String[5]; blnMsgs[0] = msg1; blnMsgs[1] = msg2; blnMsgs[2] = msg3; blnMsgs[3] = msg4; blnMsgs[4] = msg5; return balloonMsg (mover, machine, blnMsgs); } protected Component balloon7Msgs (XMover mover, MachPanel machine, String msg1, String msg2, String msg3, String msg4, String msg5, String msg6, String msg7) { String blnMsgs[] = new String[7]; blnMsgs[0] = msg1; blnMsgs[1] = msg2; blnMsgs[2] = msg3; blnMsgs[3] = msg4; blnMsgs[4] = msg5; blnMsgs[5] = msg6; blnMsgs[6] = msg7; return balloonMsg (mover, machine, blnMsgs); } protected void killBalloons (XMover mover, Vector balloons) { Enumeration blns = balloons.elements (); while (blns.hasMoreElements ()) { Component balloon = (Component) blns.nextElement (); mover.addElement (new CompKillAnimElement (this, balloon)); } } /** * read a block from a peer client. */ protected void readBlock (Block block) { /* * if the current machine already has the block, then done. */ if (management.findMachine (block.blkID, currMach)) { tickerMsg ("host " + currMach.hostID + " already has blk " + block.blkID + ". "); 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); tickerMsg ("host " + currMach.hostID + " reading blk " + block.blkID + " -> mngr " + manager.hostID + " -> host " + block.machine.hostID + " forwards. "); /* * create an animation object. */ XMover mover = new XMover (); Component balloon1 = balloon3Msgs (mover, currMach, "host " + currMach.hostID, "reading", "block " + block.blkID); Component balloon2 = balloon4Msgs (mover, manager, "host " + manager.hostID, "manages", "locations of", "block " + block.blkID); Component balloon3 = balloon4Msgs (mover, block.machine, "host " + block.machine.hostID, "will forward", "copy of", "block " + block.blkID); mover.addElement (new XSleepElement (balloonWaitSleep)); mover.addElement (new CompKillAnimElement (this, balloon1)); mover.addElement (new CompKillAnimElement (this, balloon2)); mover.addElement (new CompKillAnimElement (this, balloon3)); /* * 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, new XBlob (newBlock.size (), newBlock.blkID), newBlockSrc, netPos, newBlockDest, true, false, newBlock.size ())); mover.addElement (new BlkAddAnimElement (this, newBlockDest, newBlock)); mover.start (); /* * update the manager meta data. */ management.readBlk (newBlock); writeActionStream (currMach.hostID, "read", block.machine.hostID, block.blkID); } /** * kill all the blocks, except "block" itself. */ protected void invalBlocks (XMover mover, Block block, MachPanel manager, Vector oldBalloons) { /* * 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. */ String invalMachList = " "; XVector balloons = new XVector (); for (int i = 0; machineBlocks.hasMoreElements (); i++) { Block machineBlock = (Block) machineBlocks.nextElement (); ends[i] = machineBlock.location (); invalMachList += + machineBlock.machine.hostID + ","; Component balloon = balloon4Msgs (mover, machineBlock.machine, "host " + machineBlock.machine.hostID, "has obsolete", "copy of", "block " + block.blkID); balloons.addElement (balloon); } mover.addElement (new XSleepElement (balloonWaitSleep)); if (oldBalloons != null) { balloons.addVector (oldBalloons); } killBalloons (mover, balloons); /* * animate a message from the current machine to the manager. */ mover.addElement (new XMoveElement (this, quesImage, quesDim, currMach.location (), netPos, manager.location (), true)); tickerMsg (" -> mngr " + manager.hostID + " -> inval copies on hosts" + invalMachList + " "); 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)); } // System.out.println ("invalBlocks: there..."); mover.addElement (new BlksKillAnimElement (this, others.elements ())); } /** * overwrites an existing block. */ protected void writeBlock (Block block) { int blkID = block.blkID; MachPanel destMach = block.machine; MachPanel manager = management.managerMachine (blkID); Block ownBlock = management.getBlock (blkID, currMach); tickerMsg ("host " + currMach.hostID + " writing blk " + block.blkID); /* * create an animation object. */ XMover mover = new XMover (); /* * if the writer does not have/own the block, kill the others. */ if ((ownBlock == null) || (!ownBlock.owned)) { Vector balloons = new Vector (); Component balloon = balloon3Msgs (mover, currMach, "host " + currMach.hostID, "overwriting", "block " + block.blkID); balloons.addElement (balloon); balloon = balloon4Msgs (mover, manager, "host " + manager.hostID, "manages", "locations of", "block " + block.blkID); balloons.addElement (balloon); invalBlocks (mover, block, manager, balloons); } else { tickerMsg (" which it owns. "); } /* * create and add the new block. */ if (ownBlock == null) { ownBlock = new Block (blkID, currMach); mover.addElement (new BlkAddAnimElement (this, currMach.nextBlkLoc (), ownBlock)); } /* * start the animation. */ mover.start (); /* * update manager meta data. */ management.writeBlk (ownBlock); currMach.dirty.addElement (ownBlock); writeActionStream (currMach.hostID, "write", destMach.hostID, blkID); } /** * delete a block. */ protected void deleteBlock (Block block) { tickerMsg ("host " + currMach.hostID + " deleting blk " + block.blkID); /* * create an animation object. */ XMover mover = new XMover (); int blkID = block.blkID; MachPanel destMach = block.machine; MachPanel manager = management.managerMachine (blkID); Vector balloons = new Vector (); Component balloon = balloon3Msgs (mover, currMach, "host " + currMach.hostID, "deleting", "block " + block.blkID); balloons.addElement (balloon); balloon = balloon4Msgs (mover, manager, "host " + manager.hostID, "manages", "locations of", "block " + block.blkID); balloons.addElement (balloon); /* * kill all other blocks. */ invalBlocks (mover, block, manager, balloons); /* * update manager meta data. */ management.deleteBlk (block); /* * kill my own block. */ mover.addElement (new BlkKillAnimElement (this, block)); /* * start the animation. */ mover.start (); writeActionStream (currMach.hostID, "delete", destMach.hostID, blkID); } /** * flush dirty blocks on a machine. */ protected void flushBlocks (MachPanel machine) { Enumeration dirtyBlks = machine.dirty.elements (); int numBlks = machine.dirty.size (); if (numBlks <= 0) return; tickerMsg ("host " + machine.hostID + " striping dirty blks to sw RAID. "); int i; int segs; XBlob newBlks[] = new XBlob[stripeSegBlks]; XBlob moveBlks[]; XBlob frags[]; boolean more; XMover mover = new XMover (); Component balloon = balloon3Msgs (mover, machine, "host " + machine.hostID, "syncing dirty", "blocks"); mover.addElement (new CompKillAnimElement (this, balloon)); for (segs = i = 0;;) { Block blk = (Block) dirtyBlks.nextElement (); XBlob newBlk = new XBlob (blk.size (), blk.blkID, 24); 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, mover, newBlks, i); mover.addElement (new XSleepElement (stripeFragSleep * 2)); balloon = balloon7Msgs (mover, machine, "dirty data", "grouped", "into", "fragments, ", "calculate", "parity on", "them"); mover.addElement (new XSleepElement (stripeFragSleep * 2)); frags = drawFrags (machine, mover, moveBlks); mover.addElement (new BlksRemoveAnimElement (this, moveBlks)); mover.addElement (new CompKillAnimElement (this, balloon)); mover.addElement (new XSleepElement (stripeFragSleep)); ssgBalloons (mover, frags, segs); segGather (machine, mover, frags); mover.addElement (new XSleepElement (stripeFragSleep)); segScatter (machine, mover, frags, segs); if (!more) break; i = 0; segs++; } mover.start (); machine.dirty.removeAllElements (); writeActionStream (currMach.hostID, "flush", machine.hostID, 0); } /** * move a bunch of dirty blocks to the log area. */ protected XBlob[] moveToLog (MachPanel machine, XMover mover, XBlob newBlks[], int i) { XBlob moveBlks[] = new XBlob[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++) { XBlob 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; } mover.addElement (new XMoveElement (this, moveBlks, moveStarts, moveEnds, false, true, null, 10, 0)); return moveBlks; } /** * draws a bunch of fragments over the blocks in the log. */ protected XBlob[] drawFrags (MachPanel machine, XMover mover, XBlob moveBlks[]) { int numBlks = moveBlks.length; int numFrags = (numBlks + 3) / 2; XBlob frags[] = new XBlob[numFrags]; XBlob frag; int x, y, j; int fragWidth; int fragHeight; String fragName; Color fragBg; 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; } Dimension fragDim = new Dimension (fragWidth, fragHeight); x = logLoc.x; y = logLoc.y; for (j = 0; j < numFrags; j++) { if (j != numFrags - 1) { fragName = "F" + j; fragBg = Color.green; } else { fragName = "P"; fragBg = Color.red; } frags[j] = frag = new XBlob (fragDim, fragName, fragBg, Color.black, 24); // System.out.println ("drawFrags: " + x + y); mover.addElement (new XAddElement (this, frag, new Point (x, y), new Dimension (fragWidth, fragHeight))); mover.addElement (new XSleepElement (stripeFragSleep)); frag.move (x, y); if (logLoc.incrX) x += fragWidth; else y += fragHeight; } return frags; } /** * gather the fragments for transmission. */ protected void segGather (MachPanel machine, XMover mover, XBlob frags[]) { int numFrags = frags.length; Point starts[] = new Point[numFrags]; Point ends[] = new Point[numFrags]; Point machLoc = machine.location (); int x = machLoc.x; int y = machLoc.y; for (int i = 0; i < numFrags; i++) { starts[i] = frags[i].location (); ends[i] = new Point (x, y); frags[i].move (x, y); x += 4; y += 4; } mover.addElement (new XMoveElement (this, frags, starts, ends, false, true, null, 40, 0)); } protected void ssgBalloons (XMover mover, XBlob frags[], int segs) { int numFrags = frags.length; int machI = segs * stripeSSGsize; Vector balloons = new Vector (); Component balloon; for (int i = 0; i < numFrags; i++) { machI %= numHosts; String fragName = i == numFrags - 1 ? "parity frag" : "fragment " + i; balloon = balloon4Msgs (mover, machines[machI], "storage", "server " + machines[machI].hostID, "receives", fragName); balloons.addElement (balloon); machI++; } mover.addElement (new XSleepElement (balloonWaitSleep)); killBalloons (mover, balloons); } /** * scatter the fragments into the network. */ protected void segScatter (MachPanel machine, XMover mover, XBlob frags[], int segs) { int numFrags = frags.length; Point starts[] = new Point[numFrags]; Point mids[] = new Point[numFrags]; Point ends[] = new Point[numFrags]; int machI = segs * stripeSSGsize; for (int i = 0; i < numFrags; i++) { machI %= numHosts; starts[i] = frags[i].location (); mids[i] = netPos; ends[i] = machines[machI].location (); machI++; } mover.addElement (new XMoveElement (this, frags, starts, mids, ends, true, true, null, 40, 0)); } protected void xterm (MachPanel machine) { writeActionStream (currMach.hostID, "xterm", machine.hostID, 0); } /** * 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 ("XTerm".equals (arg)) { xterm ((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 BlkKillAnimElement extends XMoverItem { xFSPanel xfsPanel; Block blk; public BlkKillAnimElement (xFSPanel xfsPanel, Block blk) { this.xfsPanel = xfsPanel; this.blk = blk; } public void doMove () { blk.machine.dirty.removeElement (blk); xfsPanel.remove (blk); /* blk.Kill (); */ } } class CompKillAnimElement extends XMoverItem { xFSPanel xfsPanel; Component comp; public CompKillAnimElement (xFSPanel xfsPanel, Component comp) { this.xfsPanel = xfsPanel; this.comp = comp; } public void doMove () { xfsPanel.remove (comp); } } class BlksKillAnimElement extends XMoverItem { xFSPanel xfsPanel; Enumeration deadBlks; public BlksKillAnimElement (xFSPanel xfsPanel, Enumeration deadBlks) { this.xfsPanel = xfsPanel; this.deadBlks = deadBlks; } public void doMove () { /* * shrink down and kill these blocks. */ // System.out.println ("BlksKillAnimElement: doMove: entered..."); for (int i = 0; deadBlks.hasMoreElements (); i++) { Block machineBlock = (Block) deadBlks.nextElement (); // System.out.println ("BlksKillAnimElement: doMove: killing " + // machineBlock.blkID); machineBlock.machine.dirty.removeElement (machineBlock); xfsPanel.remove (machineBlock); /* machineBlock.Kill (); */ } } } class BlkAddAnimElement extends XMoverItem { xFSPanel xfsPanel; Point location; Component newBlk; public BlkAddAnimElement (xFSPanel xfsPanel, Point location, Component newBlk) { this.xfsPanel = xfsPanel; this.location = location; this.newBlk = newBlk; } public void doMove () { xfsPanel.add (newBlk, location); } } class BlksRemoveAnimElement extends XMoverItem { xFSPanel xfsPanel; Component blks[]; public BlksRemoveAnimElement (xFSPanel xfsPanel, Component blks[]) { this.xfsPanel = xfsPanel; this.blks = blks; } public void doMove () { int numBlks = blks.length; for (int j = 0; j < numBlks; j++) xfsPanel.remove (blks[j]); } } class ItemResizeElem extends XMoverItem { Component comp; int inc; public ItemResizeElem (Component comp, int inc) { this.comp = comp; this.inc = inc; } public void doMove () { Dimension d; d = comp.size (); d.width += this.inc; d.height += this.inc; comp.resize (d); } } class BalloonFontElem extends XMoverItem { XBalloon ball; int fontSize; public BalloonFontElem (XBalloon ball, int fontSize) { this.ball = ball; this.fontSize = fontSize; } public void doMove () { this.ball.fontSize = this.fontSize; } } class BalloonSkipElem extends XMoverItem { XBalloon ball; boolean skip; public BalloonSkipElem (XBalloon ball, boolean skip) { this.ball = ball; this.skip = skip; } public void doMove () { this.ball.skipWords = this.skip; } } 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; final int defaultActionPort = 8989; public xFS () { this.setLayout (new BorderLayout ()); } public void init() { int xwidth; int numHosts; Image hostImage; Image myriImage; Image bombImage; Image quesImage; Image balloonImage; String s; xFSPanel xp; URL docBase; String actionServer; int actionPort; Socket actionSock; OutputStream outStream; PrintStream actionStream; 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"); balloonImage = getImage (docBase, "pics/balloon.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); /* * should the applet talk to an action server? */ actionServer = getParameter ("actionServer"); s = getParameter ("actionPort"); if (s != null) actionPort = Integer.parseInt (s); else actionPort = defaultActionPort; actionStream = null; actionSock = null; if (actionServer != null) { try { actionSock = new Socket (actionServer, actionPort, true); outStream = actionSock.getOutputStream (); actionStream = new PrintStream (outStream, true); System.out.println ("xFS.init: successfully connected to " + actionServer + " at port " + actionPort); actionStream.println ("here"); actionStream.close (); outStream.close (); actionSock.close (); } catch (Exception e) { System.err.println ("unable to connect to " + actionServer + " at port " + actionPort); } } else { // System.out.println ("no action server"); } /* * create the top level panel. */ this.add (xp = new xFSPanel ()); xp.initialize (numHosts, xwidth, hostImage, myriImage, bombImage, quesImage, balloonImage, actionServer, actionPort); } 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 (); } }