package ejava.transactions.demo;

import ejava.examples.uid.ejb.UIDGeneratorRemote;
import ejava.examples.uid.ejb.UIDGeneratorRemoteHome;
import javax.transaction.TransactionRequiredException;
import javax.ejb.FinderException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
import javax.jms.TopicSession;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.JMSException;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.transaction.UserTransaction;
import javax.rmi.PortableRemoteObject;
import java.rmi.RemoteException;
import java.util.Hashtable;

public class TxClient {
   static final int FIRST_TYPE = 0;
   static final int NOT_SUPPORTED = 0;
   static final int SUPPORTS = 1;
   static final int REQUIRESNEW = 2;
   static final int LAST_TYPE = 2;
   static final int DEFAULT = 3;

   static String TX_TYPE[] = { 
      "NotSupported", "Supports", "RequiresNew", "DEFAULT" };
   Context jndi_;
   UIDGeneratorRemote uidGenerator_;
   TellerRemote teller_;
   Hashtable fromAccounts_;
   Hashtable toAccounts_;
   TopicSubscriber subscriber_;

   void transfer(int fromType, int toType) throws Exception {
      AccountRemote from = (AccountRemote)fromAccounts_.get(TX_TYPE[fromType]);
      AccountRemote to = (AccountRemote)toAccounts_.get(TX_TYPE[toType]);
      
      try {
         teller_.reset(from);
         teller_.reset(to);
      }
      finally {
	 if (subscriber_.receive(1000 * 1) == null) {
	    System.out.println("didn't get first reset message");
	 }
	 if (subscriber_.receive(1000 * 1) == null) {
	    System.out.println("didn't get second reset message");
	 }
      }
      
      float amount = 5.00F;
      try {
         teller_.transfer(from, to, amount);
      }
      catch (AccountException ex) {
         System.out.print("transfer failed: " + ex.getMessage());
      }
      finally {
         System.out.print(":from(" +from.getId()+ ")=" + from.getBalance() +
	                    ", to=(" +to.getId()+ ")=" + to.getBalance());
	 MapMessage msg = (MapMessage)subscriber_.receive(1000 * 1);
	 if (msg != null) { 
	    System.out.println("\nfrom=" + msg.getString("from") +
	                       ", to=" + msg.getString("to")); 
	 }
	 else             { System.out.println(":no message"); }
      }
   }

   TxClient(Context jndi) throws Exception {
      jndi_ = jndi;
      init();
   }
      
   void init() throws Exception {
      try { 
         UIDGeneratorRemoteHome uidHome = (UIDGeneratorRemoteHome)
            PortableRemoteObject.narrow(
	       jndi_.lookup(getUIDName()), UIDGeneratorRemoteHome.class);
         uidGenerator_ = uidHome.find();
         
         fromAccounts_ = new Hashtable(LAST_TYPE);
         toAccounts_ = new Hashtable(LAST_TYPE);
	 for(int i=FIRST_TYPE; i<=DEFAULT; i++) {
           String id = uidGenerator_.createUID().toString();
           AccountRemote account = createAccount(TX_TYPE[i], id);
	   toAccounts_.put(TX_TYPE[i], account);
           
           id = uidGenerator_.createUID().toString();
	   account = createAccount(TX_TYPE[i], id);
	   fromAccounts_.put(TX_TYPE[i], account);
         }

	 TellerRemoteHome tHome = (TellerRemoteHome)
	    PortableRemoteObject.narrow(
	       jndi_.lookup(getTellerName()), TellerRemoteHome.class);
         teller_ = tHome.create();

	 TopicConnectionFactory cFactory = (TopicConnectionFactory)
	    PortableRemoteObject.narrow(
	       jndi_.lookup(getConnectionFactoryName()),
	       TopicConnectionFactory.class);
         Topic topic = (Topic) jndi_.lookup(getTopicName());
	 TopicConnection connection = cFactory.createTopicConnection();
	 TopicSession session = connection.createTopicSession(
	    false, TopicSession.AUTO_ACKNOWLEDGE);
	 subscriber_ = session.createSubscriber(topic);
	 connection.start();
      }
      finally {
      }
   }

   AccountRemote createAccount(String txType, String id) 
      throws RemoteException, NamingException, CreateException {

      String context = getName(txType);
      Object object = jndi_.lookup(getName(txType));
      AccountRemoteHome aHome = (AccountRemoteHome)
            PortableRemoteObject.narrow(
            object, AccountRemoteHome.class);
      return aHome.create(id);
   }

   String getName(String txType) {
      if (!txType.equals(TX_TYPE[DEFAULT])) {
         return "ejava.transactions.demo.AccountRemoteHome_" + txType;
      }
      else {
         return "ejava.transactions.demo.AccountRemoteHome";
      }
   }

   String getUIDName() { return "UIDGeneratorRemoteHome"; }

   String getTellerName() { 
      return "ejava.transactions.demo.TellerRemoteHome"; 
   }

   String getConnectionFactoryName() { 
      return "ejava.jms.factories.publisher"; 
   }

   String getTopicName() { 
      return "ejava.jms.topics.general"; 
   }

   private static void execute(final Context jndi) 
      throws Exception {
      
      TxClient client = new TxClient(jndi);

      for(int i=FIRST_TYPE; i<=LAST_TYPE; i++) {
         for(int j=FIRST_TYPE; j<=LAST_TYPE; j++) {
	    System.out.println(TX_TYPE[i] + "->" + TX_TYPE[j]);
            client.transfer(i,j);
	 }
      }
      System.out.println(TX_TYPE[DEFAULT] + "->" + TX_TYPE[DEFAULT]);
      client.transfer(DEFAULT,DEFAULT);
   }

   public static void main(String args[]) throws Exception {
      String url = "t3://localhost:7001";
      for(int i=0; i<args.length; i++) {
         if (args[i].equals("-url")) { url = args[++i]; }
      }
      Hashtable props = new Hashtable();
      props.put(Context.INITIAL_CONTEXT_FACTORY, 
                "weblogic.jndi.WLInitialContextFactory");
      props.put(Context.PROVIDER_URL, url);
      Context jndi = new InitialContext(props);
      execute(jndi);
   }
}
