import com.sun.jini.lease.LeaseRenewalManager; import com.sun.jini.lookup.JoinManager; import com.sun.jini.lookup.ServiceIDListener; import java.io.IOException; import java.rmi.activation.*; import java.rmi.Remote; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Arrays; import java.util.Map; import java.util.Iterator; import java.util.StringTokenizer; import java.util.Properties; import net.jini.core.entry.Entry; import net.jini.core.lease.LeaseDeniedException; import net.jini.core.lookup.ServiceID; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.transaction.CannotJoinException; import net.jini.core.transaction.server.CrashCountException; import net.jini.core.transaction.server.TransactionConstants; import net.jini.core.transaction.server.TransactionManager.Created; import net.jini.core.transaction.server.TransactionManager; import net.jini.core.transaction.server.TransactionParticipant; import net.jini.core.transaction.UnknownTransactionException; import net.jini.discovery.LookupDiscovery; import net.jini.lookup.entry.Name; import sdsu.util.ProgramProperties; import sdsu.io.LocalRepository; import sdsu.logging.*; import net.jini.core.transaction.CannotCommitException; public class Auctioneer extends UnicastRemoteObject implements AuctionInterface, ServiceIDListener, TransactionParticipant { private ServiceID auctionServiceID; int earnings = 0; int itemsToSell = 100; TransactionManager clerk; AuctionClock timer; Map pendingBids = Collections.synchronizedMap( new HashMap()); LocalRepository dataStore; int winningBid = NOT_SET; static final int NOT_SET = -1; static final String PENDING_BIDS = "pendingBids"; static final String SERVICE_ID = "serviceID"; static final String EARNINGS = "earnings"; static final String ITEMS_TO_SELL = "itemsToSell"; public Auctioneer( String[] groups, String database) throws Exception { try { System.out.println( "Start Auctioneer" ); if (database == null ) System.out.println( "Null database" ); System.out.println( "Start repository" ); dataStore = new LocalRepository( database ); System.out.println( "B repository" ); if ( dataStore.exists() ) restoreState(); else dataStore.create(); System.out.println( "Look for reggie" ); ServiceRegistrar lookupSevice = joinReggie( groups ); setClerk( lookupSevice ); if ( pendingBids.size() > 0 ) validateBids(); timer = new AuctionClock( 1000 * 30 ); timer.setDaemon( false); timer.start(); } catch (Exception error) { System.out.println( "Startup error" ); Logger.error( error ); throw error; } } private void validateBids() { synchronized (pendingBids) { Iterator bids = pendingBids.keySet().iterator(); while (bids.hasNext() ) { Long wrapped =(Long) bids.next(); int status = getStatus( wrapped.longValue() ); switch (status) { case VOTING: case ABORTED: bids.remove(); break; case COMMITTED: Integer bidAmount = (Integer) pendingBids.get( wrapped ); commitBid( bidAmount.intValue() ); bids.remove(); break; case ACTIVE: boolean rejoined = rejoinTransaction( wrapped.longValue() ); if (!rejoined) bids.remove(); } } } } private boolean rejoinTransaction( long transactionID ) { try { clerk.join( transactionID, this, 0 ); return true; } catch (RemoteException connectionProblem ) {// should retry here return false; } catch (Exception cantJoin) { return false; } } private int getStatus( long bid ) { try { return clerk.getState( bid ); } catch (UnknownTransactionException removeIt) { return ABORTED; } catch (RemoteException whatToDoHere) { Logger.error( "Remote exception on validating a bid " + whatToDoHere ); return 0; } } private void restoreState() { System.out.println( "RestoreState" ); try { if (dataStore.containsKey( PENDING_BIDS ) ) pendingBids = (Map) dataStore.get( PENDING_BIDS ); if (dataStore.containsKey( SERVICE_ID ) ) auctionServiceID = (ServiceID) dataStore.get( SERVICE_ID ); if (dataStore.containsKey( EARNINGS ) ) { Integer wrapped = (Integer) dataStore.get( EARNINGS ); earnings = wrapped.intValue(); } if (dataStore.containsKey( ITEMS_TO_SELL ) ) { Integer wrapped = (Integer) dataStore.get( EARNINGS ); itemsToSell = wrapped.intValue(); } } catch (IOException saveError ) { Logger.error( "IOException on restoring state " + saveError ); } } private void saveState() { System.out.println( "Save State" ); try { dataStore.put( PENDING_BIDS, pendingBids ); dataStore.put( SERVICE_ID, auctionServiceID ); dataStore.put( EARNINGS, new Integer( earnings) ); dataStore.put( ITEMS_TO_SELL, new Integer( itemsToSell) ); } catch (IOException saveError ) { Logger.error( "IOException on saving state " + saveError ); } } private ServiceRegistrar joinReggie( String[] groups ) { JoinManager myManager = null; ServiceRegistrar[] joinSet = null;; try { Entry[] labels = new Entry[1]; labels[0] = new Name("Auction"); myManager = new JoinManager (this, labels,groups,null, this, new LeaseRenewalManager () ); do { System.out.println( "Looking for reggie" ); Thread.sleep( 1000 * 10 ); joinSet = myManager.getJoinSet(); } while ( joinSet.length == 0 ); } catch (Exception startupProblem) { System.err.println( "Could not start Auction server: " + startupProblem ); Logger.error( "Could not start Auction server: " + startupProblem); System.exit( 0 ); } return joinSet[0]; } public void serviceIDNotify (ServiceID uniqueID) { auctionServiceID = uniqueID; System.out.println("server: ID set: " + auctionServiceID ); try { dataStore.put( PENDING_BIDS, uniqueID ); } catch (IOException writeProblem) { Logger.error( "Could not store serviceID" ); } } private void setClerk( ServiceRegistrar registrar ) throws RemoteException { Entry[] serverAttributes = new Entry[1]; serverAttributes[0] = new Name ("TransactionManager"); ServiceTemplate template = new ServiceTemplate (null, null, serverAttributes); System.out.println( "Get Transaction M" ); clerk = (TransactionManager) registrar.lookup (template); } public int earnings() { return earnings; } public synchronized Bid bid( int amount, TransactionParticipant bidder) throws RemoteException, LeaseDeniedException { System.out.println( "bid: " + amount ); try { Created leasedTransaction = clerk.create( 1000 * 60 * 2 ); long bidID = leasedTransaction.id; clerk.join( bidID, this, 0 ); clerk.join(bidID, bidder, 0 ); System.out.println( "Bid joined" ); putBid( bidID , amount ); saveState(); return new Bid( clerk, bidID, 1); } catch ( Exception bidProblem ) { bidProblem.printStackTrace(); throw new LeaseDeniedException(" Can not accept bid " ); } } void processAuction() { System.out.println( "Process auction Bids" ); Object bids[]; synchronized (pendingBids) { bids = pendingBids.keySet().toArray(); } // Process bids from largest to smallest in case // largest bid aborts. Arrays.sort( bids ); for ( int k = bids.length-1; k >= 0; k-- ) { long bidID = ((Long) bids[k]).longValue(); try { clerk.commit( bidID ); } catch (UnknownTransactionException badTransaction ) { removeBid( bidID ); } catch (CannotCommitException killBid ) { removeBid( bidID ); } catch ( RemoteException retryLater ) { Logger.error("RemoteException on commit: " + retryLater ); } } //prepare for next auction winningBid = NOT_SET; saveState(); } public void abort(TransactionManager manager, long transactionID) throws UnknownTransactionException, RemoteException { System.out.println( "Abort" ); identifyTransaction( manager, transactionID ); int invalidBid = getBid( transactionID ); removeBid( transactionID ); if (winningBid == invalidBid ) winningBid = getMaxBid(); } public void commit(TransactionManager manager, long transactionID) throws UnknownTransactionException, RemoteException { System.out.println( "commit" ); identifyTransaction( manager, transactionID ); commitBid( getBid( transactionID )); removeBid( transactionID ); } private void commitBid( int bidAmount ) { earnings = earnings + bidAmount; System.out.println( "commit: bid " + bidAmount + " earnings " + earnings ); itemsToSell--; try { dataStore.put( EARNINGS, new Integer( earnings) ); dataStore.put( ITEMS_TO_SELL, new Integer( itemsToSell) ); } catch (IOException writeError ) { Logger.error( "IO exception on committing bid " ); Logger.error( writeError ); } } public int prepare(TransactionManager manager, long transactionID) throws UnknownTransactionException, RemoteException { System.out.println( "prepare" ); identifyTransaction( manager, transactionID ); int bidValue = getBid( transactionID ); System.out.println( "prepare bidValue: " + bidValue ); if ( bidValue == 0 ) return NOTCHANGED; System.out.println( "prepare maxBid: " + getMaxBid() ); if (winningBid == NOT_SET) winningBid = getMaxBid(); if ( bidValue < winningBid ) { abort( manager, transactionID ); return ABORTED; } return PREPARED; } public int prepareAndCommit(TransactionManager mgr, long transactionID) throws UnknownTransactionException, RemoteException { System.out.println( "prepare & commit" ); int result = prepare( mgr, transactionID ); if ( result == PREPARED ) { commit( mgr, transactionID ); result = COMMITTED; } return result; } private void putBid( long bidID, int bidAmount ) throws IOException { try { pendingBids.put( new Long( bidID ), new Integer( bidAmount ) ); dataStore.put( PENDING_BIDS, pendingBids ); } catch (IOException writeProblem) { Logger.error( "IOException in writing pendingBids to dataStore on put" + writeProblem ); pendingBids.remove( new Long( bidID )); throw writeProblem; } } private void removeBid(long bidID) { try { pendingBids.remove( new Long( bidID ) ); dataStore.put( PENDING_BIDS, pendingBids ); } catch (IOException writeProblem) { Logger.error( "IOException in writing pendingBids to dataStore on remove" + writeProblem ); } } private int getBid(long bidID) { Integer bid =(Integer) pendingBids.get( new Long( bidID ) ); return bid.intValue(); } private int getMaxBid() { Integer maxBid; synchronized (pendingBids) { Collection values = pendingBids.values(); maxBid = (Integer) Collections.max( values ); } return maxBid.intValue(); } private void identifyTransaction(TransactionManager manager, long transactionID) throws UnknownTransactionException { System.out.println( "Start identify"); if ( !manager.equals(clerk) ) throw new UnknownTransactionException( "Unknown Transaction Manager"); Long id = new Long( transactionID ); if ( !pendingBids.containsKey( id ) ) throw new UnknownTransactionException( "Unknown bid id " + id); } public static void main( String[] args ) throws Exception { System.setSecurityManager(new RMISecurityManager()); System.out.println("Start main"); String database = "/export/home/whitney/java/whitney/jini/examples/transaction/AuctionData"; System.out.println("Middle"); ProgramProperties flags = new ProgramProperties( args); String groupsString = flags.getString( "groups", "ALL" ); String[] groups = formateGroupList( groupsString ); Auctioneer anAuctioneer = new Auctioneer( groups, database ); System.out.println("Class activated" + anAuctioneer.earnings() ); } private static String[] formateGroupList( String groupList ) { if (groupList.equals( "NONE") ) { System.out.println( "Usage: java HelloServer -groups=group1,group2 " ); System.exit( 0 ); } if (groupList.equals( "ALL") ) return LookupDiscovery.ALL_GROUPS; if ( groupList.indexOf( ',' ) < 0 ) return new String[] { groupList.trim() }; StringTokenizer groups = new StringTokenizer( groupList, ",'"); String[] formatedGroups = new String[ groups.countTokens() ]; for ( int k = 0; k < formatedGroups.length; k++ ) { formatedGroups[k] = groups.nextToken().trim(); } return formatedGroups; } private class AuctionClock extends Thread { long auctionDuration; public AuctionClock( long duration ) { auctionDuration = duration; } public void run() { System.out.println( "Start clock" ); try { while( true ) { System.out.println( "Sleep" ); sleep( auctionDuration ); processAuction(); } } catch (InterruptedException exitTime ) { Logger.log( "AuctionClock stoped" ); } } } }