20-CS-694 Advanced Programming Techniques Spring 2012
Homework Assignment 3

Interfaces, Exceptions, Graphics, Animation, Threads, Reflection, Networking, RMI, JDBC, JNI

Threads

Due: April 26, 2012 (submit instructions: here)

Rationale:
    Demand-driven architectures may be implemented by abstractions similar to class Stream which we developed in class. But the idea of Stream, as we discussed it, has quite a few limitations such as only allowing one-one connections between producer and consumer. We want to add the following features to class Stream to make it more useful:
  • A waiting consumer may cancel a request for a producer's token if one has not been returned in a specified length of time.
  • Two or more consumers may connect to a single producer's output stream so that every token output to the producer's stream is generated just once yet consumers' next calls work as before.
  • A consumer may request tokens from several producers, go off and do some other things, then, when all the tokens have been collected, the consumer will be notified and immediately process the collected tokens.
  • A producer connected to several consumers may wait for a next request from a specific thread type before releasing the next token in its stream.
  • A two-way Stream connection may be opened between two processes so that each may send tokens to the other.
  • A Stream connection may be opened dynamically.
  • A Stream connection may be closed dynamically.
  • Some producers will only be allowed to "connect" to a single consumer at a time although that connection can be to any of a group of consuming processes.
 
Homework Problem:      
Write Java classes which support:
    A collection of processes, each representing a "Company," and such that a process will "output" a quote for the stock of that company at random time intervals.

A "Bank" process which 1) maintains records on investors including the amount of capital and stocks owned and 2) delivers information to Company processes to enable them to decide what their stock prices are going to be and how often they will change.

A "Market" process which may be accessed to determine the current value of a stock, the number of shares outstanding, the price to earnings ratio of a stock. The Market process periodically recomputes market values by taking into account recent trade transactions. Companies may be grouped in "Cartels" and if one Investor owns the majority of stock in all companies of a cartel, that also has an effect on the price of a stock.

A collection of processes, each representing an "Investor," and such that an Investor process will "interrogate" other Investor and Company processes for information on stock prices with the intention of buying and selling stock. Buying and selling stock will be carried out at market value through the Bank and Market processes. An Investor process has an account with the Bank process and funds from that account are used to purchase stocks. When stocks are sold, the capital gained is added to the Investor's account.

A "Maintainer" process that starts the action.

Note: if this is too hard for you or if you just do not have enough time, try the alternate homework number three.

 
The following classes are expected to be part of your solution (/* comments */ say what functionality needs to be implemented where):
public class ConnectionRefusedException extends Exception {
   String msg;
   public ConnectionRefusedException (String msg) {
      this.msg = msg;
   }
}

// SubscriberInfo object sends stock info to investors in Streams
public class SubscriberInfo {
   int number;
   double stock_value;
   Company company;
   
   public SubscriberInfo (int number, double value, Company company) { 
      this.number = number; 
      this.stock_value = value;
      this.company = company;
   }
}

// Stock objects are sent in Streams to Companies
public class Stock {
   double stock_value, price_to_earnings;
   Company company;
   
   public Stock (double pe, double value, Company company) { 
      this.stock_value = value;
      this.company = company;
      this.price_to_earnings = pe;
   }
}

// General Streams class
public class YourModifiedStream implements Runnable {

   synchronized public void putIt (Object t) {
      ...
   }

   synchronized public Object next () {
      ...
   }
}

// Bank object maintains investor accounts
public class Bank extends YourModifiedStream {
import java.util.*;

   Hashtable investors;
   Market market;

   public Bank(Market market) { 
      this.market = market; 
      investors = new Hashtable();
   }

   public boolean buy (int n_shares, Company company, double price, Investor investor) {
      ...
   }

   public boolean sell (int n_shares, Company company, double price, Investor investor) {
      ...
   }
}

public class Market extends YourModifiedStream {
   Hashtable stocks;
   Hashtable companies;
   Bank bank;

   public Market () {
      Company company;
      companies = new Hashtable();
      stocks = new Hashtable();
      companies.put("A Company", company = new Company("A Company", this));
      stocks.put(company, new Stock(3.4, 54.33, company));
      ...
      this.bank = new Bank();
   }

   public Stock getQuote (String company_name) {
      Company company;
      Stock stock;

      if ((company = (Company)companies.get(company_name)) == null) 
         return null;
      if ((stock = (Stock)stocks.get(company)) == null) return null;
      return stock;
   }

   public Stock getQuote (Company company) {
      Stock stock;

      if ((stock = (Stock)stocks.get(company)) == null) return null;
      return stock;
   }

   ...

   public void run () {
      while (true) {
	 try { sleep(4000); } catch (InterruptedException e) {}
	 System.out.print("Market calculating...");
	 try { sleep(100); } catch (InterruptedException e) {}
	 System.out.println("done");
      }
   }
}

public class Company extends YourModifiedStream {
   Market market;
   String ident;
   Hashtable investors;
   int count;
   /* any other variables you need */

   Company (String ident, Market market) {
      this.ident = ident;  
      investors = new Hashtable();
      this.market = market;
      count = 0;
   }

   public void connect (InvestorChecker ic) throws ConnectionRefusedException {
      if (/* the investor is not allowed to connect */) 
	 throw new ConnectionRefusedException ("Not allowed to connect!");
      /* record the investor as connected */
   }

   public void disconnect (InvestorChecker ic) 
      throws ConnectionRefusedException {
      if (/* investor tries to disconnect someone else's connection */) 
	 throw new ConnectionRefusedException("Not allowed to disconnect others!");
      /* record the disconnect */
   }

   public void run () {
      // market object figures out the stock values
      market.connect(this);
      while (true) {
         putIt(new SubscriberInfo(count++, ((Stock)market.next()).stock_value, this));
      }
   }
}

The "Investor" process is best divided into other threaded processes, each communicating independently with the Bank and Market processes. Call them "InvestorBanker" and "InvestorChecker". The following suggest in part how to write these classes.

import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

public class Investor extends Frame implements ActionListener {
   Market market;
   String ident;
   InvestorChecker ic;
   InvestorBanker ib;
   ...

   public Investor (String ident, Market market) { 
      this.ident = ident;  
      this.market = market;
      ic = new InvestorChecker (this);
      ic.start();
      ib = new InvestorBanker (this);
      ib.start();

      setLayout(new BorderLayout());
      ...
      setSize(400,105);
      show();
   }

   public void actionPerformed (ActionEvent evt) {
      ...
   }
}

public class InvestorChecker extends Thread {
   Investor investor;
   Market market;
   Company company[];
   int idx;

   public InvestorChecker (Investor investor) {
      this.investor = investor;
      this.market = investor.market;
      idx = 0;
      company = new Company[100];
   }

   public void connect (String ident) throws ConnectionRefusedException {
      Company c;

      for (int i=0 ; i < idx ; i++)
	 if (company[i].ident.equals(ident))
	    throw new ConnectionRefusedException("Already connected!");
      int i=0;
      if ((c = (Company)market.companies.get(ident)) == null) 
	 throw new ConnectionRefusedException ("Company does not exist");
      company[idx] = c;
      company[idx++].connect(this);
   }

   public void disconnect (String ident) throws ConnectionRefusedException {
      for (int i=0 ; i < idx ; i++) {
	 if (company[i].ident.equals(ident)) {
	    company[i].disconnect(this);
	    if (i != idx-1) company[i] = company[idx-1];
	    idx--;
	    return;
	 }
      }
      throw new ConnectionRefusedException("Cannot disconnect from company");
   }

   public void run () {
      while (true) {
	 for (int i=0 ; i < idx ; i++) {
	    SubscriberInfo subscriber = (SubscriberInfo)(company[i].next());
            ...
	 }
	 try { sleep(10); } catch (InterruptedException e) {}
      }
   }
}

public class InvestorBanker extends Thread {
   Investor investor;

   public InvestorBanker (Investor investor) {  this.investor = investor;  }

   public void run () {
      while (true) {
	 try { sleep(10000); } catch (InterruptedException e) {}
	 if (!investor.market.bank.buy(10, 
			(Company)investor.market.companies.get("First"),
			 34.44)) 
         ...
      }
   }
}