import edu.mit.ai.psg.traveler.*;
import edu.mit.ai.psg.jexa.*;
import edu.mit.ai.psg.ui.outliner.*;
import edu.mit.ai.psg.ui.patches.CloseableFrame;
import java.lang.reflect.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
@author
class SharedAccountBioTraced {
public static void run(int msPerYear) {
final CallRecord runRecord =
Trace.receivingStatic(runMethod, new Object[]{});
final AccountTraced account = new AccountTraced("MyChecking", 0);
final int yearLength = msPerYear; final int monthLength = yearLength/12;
final int weekLength = yearLength/52;
final long yearStart = System.currentTimeMillis();
final long yearEnd = yearStart + yearLength;
Thread billee = new Thread("Billee") {
public void run() {
Method billeeMeth= Trace.getMethod(this.getClass(),"run",null);
CallRecord billeeRecord= Trace.receiving(this, billeeMeth,null);
int salary = 300;
for (int week = 1; week <= 52; week++) {
billeeRecord.addChild(new StringRecord("...Sleeping to work..."));
sleepTo(yearStart + week * weekLength);
account.deposit(salary);
}
Trace.returningVoid(billeeRecord);
}};
Thread lordy = new Thread("Lordy") {
public void run() {
Method lordyMeth = Trace.getMethod(this.getClass(),"run",null);
final CallRecord lordyRecord =
Trace.receiving(this, lordyMeth, null);
final int rent = 1000; final int penalty = 10;
final Collection outstandingCollectors =
Collections.synchronizedCollection(new LinkedList());
int collectorCount = 0;
for (int month = 1; month <= 12; month++) {
try { account.withdraw(rent); }
catch (IllegalArgumentException overdraft) {
Thread collector = new Thread("Collector"+(++collectorCount)) {
int due = rent + penalty;
long startDate = System.currentTimeMillis();
int week = 0;
public void run() {
Method collectorMeth =
Trace.getMethod(this.getClass(),"run",null);
CallRecord collectorRecord =
Trace.receiving(this, collectorMeth, null);
while (System.currentTimeMillis() <= yearEnd) {
try {
account.withdraw(due);
break;
} catch (IllegalArgumentException overdraft2) {
week++; due += penalty;
collectorRecord.addChild(new StringRecord
("...snoring..."));
sleepTo(startDate + week * weekLength);
}
}
outstandingCollectors.remove(this);
Trace.returningVoid(collectorRecord);
return;
}};
outstandingCollectors.add(collector);
Trace.starting(collector);
collector.start(); Thread.yield();
}
lordyRecord.addChild(new StringRecord
(outstandingCollectors.isEmpty()?
"...sweet dreams..." : "...fitful naps ..."));
sleepTo(yearStart + month * monthLength);
}
Trace.returningVoid(lordyRecord);
return;
}};
Trace.starting(billee);
billee.start();
Trace.starting(lordy);
lordy.start();
try { billee.join(); lordy.join(); } catch (InterruptedException e) {}
Trace.returningVoid(runRecord);
return;
}
static void sleepTo(long wakeupTime) {
long now = System.currentTimeMillis();
try { Thread.sleep(wakeupTime - now); }
catch (InterruptedException e) {}
catch (IllegalArgumentException e) { } }
static Method runMethod =
Trace.getMethod(SharedAccountBioTraced.class,
"run", new Class[]{int.class});
}
class AccountTraced
implements Cloneable, SharedAccountBioApplet.CloneablePublic
{
private String myName;
private int myBalance;
public AccountTraced(String name, int initBalance) {
CallRecord record =
Trace.receivingStatic(accountConstructor,
new Object[]{name, new Integer(initBalance)});
myName = name;
myBalance = initBalance;
Trace.returning(record, this);
}
static final Constructor accountConstructor =
Trace.getConstructor(AccountTraced.class,
new Class[]{String.class, int.class});
public Object clone() throws CloneNotSupportedException {
return super.clone(); }
public synchronized int getBalance() {
CallRecord record =
Trace.receiving(this, balanceMethod, null);
int result = myBalance;
return Trace.returning(record, result);
}
static final Method balanceMethod =
Trace.getMethod(AccountTraced.class, "getBalance", null);
public synchronized void deposit(int amount) {
CallRecord record =
Trace.receiving(this, depositMethod,
new Object[]{new Integer(amount)});
myBalance += amount;
Trace.returningVoid(record);
}
static final Method depositMethod =
Trace.getMethod(AccountTraced.class,"deposit", new Class[]{int.class});
public synchronized void withdraw(int amount) {
CallRecord record =
Trace.receiving(this, withdrawMethod,
new Object[]{new Integer(amount)});
if (amount <= myBalance)
myBalance -= amount;
else
throw ((IllegalArgumentException)
Trace.throwing
(record, new IllegalArgumentException("Overdraft")));
Trace.returningVoid(record);
}
static final Method withdrawMethod =
Trace.getMethod(AccountTraced.class,"withdraw",new Class[]{int.class});
public String toString() {
return "Account(name="+myName+", balance="+myBalance+")"; }
}
public class SharedAccountBioApplet extends JexaApplet {
public static void main(String[] args) {
if (args.length > 0) initSecsPerSimYear = args[0];
Frame frame = new SharedAccountBioApplet().initFrameApplet();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }});
}
static String initSecsPerSimYear = "12";
public static ActivityRecord run(final String parameter) {
ActivityRecord record =
Trace.entering(new StringRecordObservable
("Tracing SharedAccountBio and calls to Account"));
Thread thread = new Thread(new Runnable() {
public void run() {
SharedAccountBioTraced.run(1000*Integer.parseInt(parameter));
}});
Trace.starting(thread);
thread.start();
return record;
}
public String getAppletTitle() { return "Traveler: SharedAccountBio"; }
public Object getObjectToExamine() {
return null;
}
public void initPanelInPlaceApplet() {
Trace.traceRecorder = new TraceRecorderObservable();
TravelerOutliner.ensureInitialized();
super.initPanelInPlaceApplet();
Panel headPanel = (Panel) getComponent(0);
Panel jexaPanel = (Panel) getComponent(1);
headPanel.add(makeParamPanel(jexaPanel), BorderLayout.SOUTH);
}
public Frame initFrameApplet() {
Trace.traceRecorder = new TraceRecorderObservable();
TravelerOutliner.ensureInitialized();
Frame frame = new CloseableFrame(getAppletTitle());
Panel jexaPanel = new Panel();
OutlineNode node =
Outliner.outlineMaker.makeOutlineNode(getObjectToExamine());
Outliner.outlineMaker.initializePane(jexaPanel, node);
frame.add(makeParamPanel(jexaPanel), BorderLayout.NORTH);
frame.add(jexaPanel, BorderLayout.CENTER);
frame.setBounds(60, 28, 580, 300);
frame.show();
return frame;
}
public Panel makeParamPanel(final Container jexaPane) {
Panel paramPanel = new Panel(new FlowLayout(FlowLayout.CENTER, 0, 0));
paramPanel.setBackground(SystemColor.control);
paramPanel.add(new Label("1 simulated year = "));
final TextField paramField =
new TextField(initSecsPerSimYear, 2);
paramPanel.add(paramField);
paramPanel.add(new Label("seconds"));
final Button paramButton = new Button("Go");
paramButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String param = paramField.getText().trim();
try {
int secs = Integer.parseInt(param);
if (secs <= 0 || secs > 120) throw new NumberFormatException();
} catch (NumberFormatException err) {
paramField.setText(param = initSecsPerSimYear);
}
ActivityRecord record = run(param);
OutlineNode node = Outliner.outlineMaker.makeOutlineNode(record);
jexaPane.invalidate();
Outliner.outlineMaker.initializePane(jexaPane, node);
jexaPane.validate();
}});
paramPanel.add(paramButton);
return paramPanel;
}
public interface CloneablePublic {
public Object clone() throws CloneNotSupportedException;
}
}