geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ge...@apache.org
Subject svn commit: r290479 [6/16] - in /geronimo/trunk/sandbox/daytrader: ./ bin/ derby/ modules/ modules/core/ modules/core/src/ modules/core/src/conf/ modules/core/src/java/ modules/core/src/java/org/ modules/core/src/java/org/apache/ modules/core/src/java/...
Date Tue, 20 Sep 2005 16:08:17 GMT
Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBean.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBean.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBean.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBean.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,1201 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.ejb;
+
+import javax.ejb.*;
+import javax.jms.*;
+import javax.naming.*;
+
+import org.apache.geronimo.samples.daytrader.util.*;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.geronimo.samples.daytrader.*;
+
+public class TradeBean implements SessionBean {
+
+	private SessionContext context = null;
+	private LocalAccountHome accountHome = null;
+	private LocalAccountProfileHome profileHome = null;
+	private LocalHoldingHome holdingHome = null;
+	private LocalQuoteHome quoteHome = null;
+	private LocalOrderHome orderHome = null;
+	private LocalKeySequenceHome keySequenceHome = null;
+	private LocalKeySequence keySequence;	
+	
+	private ConnectionFactory qConnFactory = null;
+	private Queue queue = null; 
+	private ConnectionFactory tConnFactory = null;
+	private Topic streamerTopic = null; 
+
+	//Boolean to signify if the Order By clause is supported by the app server.
+	// This can be set to false by an env. variable
+	private boolean orderBySQLSupported = true;  
+	private boolean publishQuotePriceChange = true;  
+	private boolean updateQuotePrices = true;  
+   
+    private void queueOrderInternal(Integer orderID, boolean twoPhase)
+    	throws javax.jms.JMSException
+    {
+		if (Log.doTrace() ) Log.trace("TradeBean:queueOrderInternal", orderID);
+
+		Connection conn = null;
+		Session sess = null;
+		try
+		{
+			conn = qConnFactory.createConnection();                        
+			sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+			MessageProducer msgProducer = sess.createProducer(queue);
+			TextMessage   message = sess.createTextMessage();
+
+			message.setStringProperty("command", "neworder");
+			message.setIntProperty("orderID", orderID.intValue());
+			message.setBooleanProperty("twoPhase", twoPhase);
+			message.setText("neworder: orderID="+orderID + " runtimeMode=EJB twoPhase="+twoPhase);
+			message.setLongProperty("publishTime", System.currentTimeMillis());						
+
+			if (Log.doTrace()) Log.trace("TradeBean:queueOrder Sending message: " + message.getText());
+			msgProducer.send(message);
+		}
+		catch (javax.jms.JMSException e)
+		{
+			throw e; // pass the exception back
+		}
+		
+		finally {
+			if (conn != null )
+				conn.close();
+			if (sess != null)
+				sess.close();
+		}
+	}
+	
+   /** 
+	 * @see TradeServices#queueOrder(Integer)
+	 */
+
+	public void queueOrder(Integer orderID, boolean twoPhase)
+	throws Exception
+	{
+		if (Log.doTrace() ) Log.trace("TradeBean:queueOrder", orderID, new Boolean(twoPhase));
+		if (twoPhase)
+			queueOrderInternal(orderID, true);
+		else
+		{
+			// invoke the queueOrderOnePhase method -- which requires a new transaction
+			// the queueOrder will run in it's own transaction thus not requiring a 
+			// 2-phase commit
+			((Trade)context.getEJBObject()).queueOrderOnePhase(orderID);
+		}
+	}
+	
+	
+   /** 
+	 * @see TradeServices#queueOrderOnePhase(Integer)
+	 * Queue the Order identified by orderID to be processed in a One Phase commit
+	 * 
+	 * In short, this method is deployed as TXN REQUIRES NEW to avoid a 
+	 * 2-phase commit transaction across Entity and MDB access
+	 * 
+	 */
+
+	public void queueOrderOnePhase(Integer orderID)
+	    	throws javax.jms.JMSException, Exception
+	{
+		if (Log.doTrace() ) Log.trace("TradeBean:queueOrderOnePhase", orderID);
+		queueOrderInternal(orderID, false);
+	}
+	
+	class quotePriceComparator implements java.util.Comparator {
+   		public int compare(Object quote1, Object quote2)
+   		{
+   			double change1 = ((LocalQuote) quote1).getChange();
+   			double change2 = ((LocalQuote) quote2).getChange();
+   			return new Double(change2).compareTo(new Double(change1));
+   		}
+   	}		
+
+	public MarketSummaryDataBean getMarketSummary()
+	throws Exception {
+		
+
+		MarketSummaryDataBean marketSummaryData = null;		
+		try
+		{
+			if (Log.doTrace() ) {
+				Log.trace("TradeBean:getMarketSummary -- getting market summary");
+			}
+
+			//Find Trade Stock Index Quotes (Top 100 quotes) 
+			//ordered by their change in value
+			Collection quotes=null;
+			if ( orderBySQLSupported )
+				quotes = quoteHome.findTSIAQuotesOrderByChange();
+			else
+				quotes = quoteHome.findTSIAQuotes();
+
+			//SORT by price change the collection of stocks if the AppServer
+			//     does not support the "ORDER BY" SQL clause
+			if (! orderBySQLSupported) {
+				//if (Log.doTrace()) 
+					Log.trace("TradeBean:getMarketSummary() -- Sorting TSIA quotes");
+		        ArrayList sortedQuotes = new ArrayList(quotes);
+			    java.util.Collections.sort(sortedQuotes, new quotePriceComparator());	
+			    quotes = sortedQuotes;
+			}
+		    //SORT END 
+		    Object[] quoteArray = quotes.toArray();
+		    ArrayList topGainers = new ArrayList(5);
+		    ArrayList topLosers = new ArrayList(5);		    
+		    for (int i=0; i<5; i++) topGainers.add(quoteArray[i]);
+		    for (int i=quoteArray.length-1; i>=quoteArray.length-5; i--) topLosers.add(quoteArray[i]);
+		    
+			BigDecimal TSIA = FinancialUtils.ZERO;
+			BigDecimal openTSIA = FinancialUtils.ZERO;			
+			double totalVolume = 0.0;
+			for (int i=0; i<quoteArray.length; i++) 
+			{
+				LocalQuote quote = (LocalQuote)quoteArray[i];
+			  	BigDecimal price = quote.getPrice();
+			  	BigDecimal open  = quote.getOpen();
+			  	double volume = quote.getVolume();
+			  	TSIA = TSIA.add(price);
+			  	openTSIA = openTSIA.add(open);			  	
+			  	totalVolume += volume;
+			}
+			TSIA = TSIA.divide(new BigDecimal(quoteArray.length), FinancialUtils.ROUND);
+			openTSIA = openTSIA.divide(new BigDecimal(quoteArray.length), FinancialUtils.ROUND);
+			
+			/* This is an alternate approach using ejbSelect methods
+			 *   In this approach an ejbSelect is used to select only the 
+			 *   current price and open price values for the TSIA
+				LocalQuote quote = quoteHome.findOne();
+				BigDecimal TSIA = quote.getTSIA();
+				openTSIA = quote.getOpenTSIA();
+				Collection topGainers = quote.getTopGainers(5);
+				Collection topLosers = quote.getTopLosers(5);
+				LocalQuote quote = (LocalQuote)topGainers.iterator().next();
+ 	 			double volume = quote.getTotalVolume();							
+			 *
+			 */			
+
+			// Convert the collections of topGainer/topLoser entities 			 
+			// to collections of QuoteDataBeans
+			Collection topGainersData = getDataBeansCollection(topGainers);
+			Collection topLosersData = getDataBeansCollection(topLosers);			
+
+			marketSummaryData = new MarketSummaryDataBean(TSIA, openTSIA, totalVolume, topGainersData, topLosersData);
+		}
+		catch (Exception e)
+		{
+			Log.error("TradeBean:getMarketSummary", e);
+			throw new EJBException("TradeBean:getMarketSummary -- error ", e);
+		}
+		return marketSummaryData;
+	}
+
+	public QuoteDataBean createQuote(String symbol, String companyName, BigDecimal price)
+			throws CreateException, Exception
+	{
+		try 
+		{
+			LocalQuote quote = quoteHome.create(symbol, companyName, price);
+			QuoteDataBean quoteData = quote.getDataBean();
+			if (Log.doTrace()) Log.trace("TradeBean:createQuote-->" + quoteData);
+			return quoteData;
+		}
+		catch (Exception e)
+		{
+			Log.error("TradeBean:createQuote -- exception creating Quote", e);
+			throw new EJBException(e);
+		}
+	}
+
+	public QuoteDataBean getQuote(String symbol)
+		throws Exception
+	{
+
+		if (Log.doTrace()) Log.trace("TradeBean:getQuote", symbol);
+		QuoteDataBean quoteData;
+		try {
+			LocalQuote quote = quoteHome.findByPrimaryKey(symbol);
+			quoteData = quote.getDataBean();
+		} catch (FinderException fe) {
+				//Cannot find quote for given symbol
+				Log.error("TradeBean:getQuote--> Symbol: " + symbol + " cannot be found");
+				BigDecimal z = new BigDecimal(0.0);
+				quoteData = new QuoteDataBean("Error: Symbol " + symbol + " not found", "", 0.0, z, z, z, z, 0.0 );
+		}
+		return quoteData;
+	}
+
+	public Collection getAllQuotes() 
+		throws Exception	
+	{
+		if (Log.doTrace()) Log.trace("TradeBean:getAllQuotes");
+
+		Collection quoteBeans = new ArrayList();
+
+		try {
+			Collection quotes = quoteHome.findAll();
+			for (Iterator it = quotes.iterator(); it.hasNext(); ) {
+				LocalQuote quote = (LocalQuote)it.next();
+				quoteBeans.add(quote.getDataBean());
+			}
+		}
+		catch (FinderException fe) {
+			Log.error("TradeBean:getAllQuotes");
+		}
+		return quoteBeans;
+	}
+
+	public QuoteDataBean updateQuotePriceVolume(String symbol, BigDecimal changeFactor, double sharesTraded) 
+		throws Exception
+	{
+		
+		if ( TradeConfig.getUpdateQuotePrices() == false ) 
+			return new QuoteDataBean();	
+		
+		if (Log.doTrace()) 
+			Log.trace("TradeBean:updateQuote", symbol, changeFactor);	
+		
+		QuoteDataBean quoteData;
+		try {
+			LocalQuote quote = quoteHome.findByPrimaryKeyForUpdate(symbol);
+			BigDecimal oldPrice = quote.getPrice();
+
+			if (quote.getPrice().equals(TradeConfig.PENNY_STOCK_PRICE)) {
+				changeFactor = TradeConfig.PENNY_STOCK_RECOVERY_MIRACLE_MULTIPLIER;
+			}
+
+			BigDecimal newPrice = changeFactor.multiply(oldPrice).setScale(2, BigDecimal.ROUND_HALF_UP);
+
+			quote.updatePrice(newPrice);
+			quote.addToVolume(sharesTraded);
+			quoteData = quote.getDataBean();
+			((Trade)context.getEJBObject()).publishQuotePriceChange(quoteData, oldPrice, changeFactor, sharesTraded);
+
+		} catch (FinderException fe) {
+			//Cannot find quote for given symbol
+			Log.error("TradeBean:updateQuotePriceVolume--> Symbol: " + symbol + " cannot be found");
+			quoteData = new QuoteDataBean("Error: Symbol " + symbol + " not found");
+		}
+		return quoteData;
+	}
+	
+	public void publishQuotePriceChange(QuoteDataBean quoteData, BigDecimal oldPrice, BigDecimal changeFactor, double sharesTraded)
+	throws Exception
+	{
+		if ( publishQuotePriceChange == false)
+			return;
+		if (Log.doTrace())
+			Log.trace("TradeBean:publishQuotePricePublishing -- quoteData = " + quoteData);		
+
+		Connection conn = null;
+		Session sess = null;
+		
+		try
+		{			
+			conn = tConnFactory.createConnection();		            
+			sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+			MessageProducer msgProducer = sess.createProducer(streamerTopic);
+			TextMessage message = sess.createTextMessage();
+
+			String command = "updateQuote";
+			message.setStringProperty("command", command);
+			message.setStringProperty("symbol",  quoteData.getSymbol() );
+			message.setStringProperty("company", quoteData.getCompanyName() );		
+			message.setStringProperty("price",   quoteData.getPrice().toString());
+			message.setStringProperty("oldPrice",oldPrice.toString());		
+			message.setStringProperty("open",    quoteData.getOpen().toString());
+			message.setStringProperty("low",     quoteData.getLow().toString());
+			message.setStringProperty("high",    quoteData.getHigh().toString());
+			message.setDoubleProperty("volume",  quoteData.getVolume());		
+					
+			message.setStringProperty("changeFactor", changeFactor.toString());		
+			message.setDoubleProperty("sharesTraded", sharesTraded);				
+			message.setLongProperty("publishTime", System.currentTimeMillis());					
+			message.setText("Update Stock price for " + quoteData.getSymbol() + " old price = " + oldPrice + " new price = " + quoteData.getPrice());
+
+			msgProducer.send(message);
+		}
+		catch (Exception e)
+		{
+			throw e; // pass the exception back
+		}	
+		finally {	
+			if (conn != null)
+				conn.close();	
+			if (sess != null)
+			sess.close();		
+		}
+	}	
+
+	public OrderDataBean buy(String userID, String symbol, double quantity, int orderProcessingMode)
+	throws Exception 
+	{
+
+		LocalOrder order=null;
+		BigDecimal total;		
+		try {
+			if (Log.doTrace()) 
+				Log.trace("TradeBean:buy", userID, symbol, new Double(quantity), new Integer(orderProcessingMode));					
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 * 			
+			LocalAccount account = accountHome.findByUserIDForUpdate(userID);	
+ 
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccountForUpdate();
+		 */
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+
+			LocalQuote quote = quoteHome.findByPrimaryKey(symbol);
+			LocalHolding holding = null;  //The holding will be created by this buy order
+			Integer orderID  = keySequence.getNextID("order");
+
+			order = createOrder(orderID, account, quote, holding, "buy", quantity);
+
+			//UPDATE - account should be credited during completeOrder
+			BigDecimal price = quote.getPrice();
+			BigDecimal orderFee = order.getOrderFee();
+			BigDecimal balance = account.getBalance();
+			total   = (new BigDecimal(quantity).multiply(price)).add(orderFee);
+			account.setBalance(balance.subtract(total));			
+			
+			if (orderProcessingMode == TradeConfig.SYNCH) 
+				completeOrderInternal(order.getOrderID());
+			else if (orderProcessingMode == TradeConfig.ASYNCH)
+				// Invoke the queueOrderOnePhase method w/ TXN requires new attribute
+				// to side-step a 2-phase commit across DB and JMS access
+				queueOrder(order.getOrderID(), false);
+			else //TradeConfig.ASYNC_2PHASE
+				queueOrder(order.getOrderID(), true);
+		}
+		catch (Exception e)
+		{
+			Log.error("TradeBean:buy("+userID+","+symbol+","+quantity+") --> failed", e);
+			/* On exception - cancel the order */
+			if (order != null) order.cancel();
+			throw new EJBException(e);
+		}	
+		return order.getDataBean();
+	}
+
+	public OrderDataBean sell(String userID, Integer holdingID, int orderProcessingMode) 
+	throws Exception 
+	{
+		
+		LocalOrder order=null;
+		BigDecimal total;		
+		try {
+			if (Log.doTrace()) 
+				Log.trace("TradeBean:sell", userID, holdingID, new Integer(orderProcessingMode));					
+			
+			/* Some databases cannot allocate an update lock on a JOIN
+			 * use the second approach below to acquire update lock
+			LocalAccount account = accountHome.findByUserIDForUpdate(userID);
+			*/
+		 
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+			LocalHolding holding;
+			try {
+				holding = holdingHome.findByPrimaryKeyForUpdate(holdingID);
+			}
+			catch (ObjectNotFoundException oe)
+			{
+				Log.error("TradeBean:sell User " + userID + " attempted to sell holding " + holdingID + " which has already been sold");
+				OrderDataBean orderData = new OrderDataBean();
+				orderData.setOrderStatus("cancelled");
+				return orderData;
+			}
+			LocalQuote   quote = holding.getQuote();
+			double		 quantity = holding.getQuantity();
+			Integer orderID = keySequence.getNextID("order");
+			order = createOrder(orderID, account, quote, holding, "sell", quantity);
+
+			//UPDATE the holding purchase data to signify this holding is "inflight" to be sold
+			//    -- could add a new holdingStatus attribute to holdingEJB
+			holding.setPurchaseDate(new java.sql.Timestamp(0));
+			
+			//UPDATE - account should be credited during completeOrder
+			BigDecimal price = quote.getPrice();
+			BigDecimal orderFee = order.getOrderFee();
+			BigDecimal balance = account.getBalance();	
+			total   = (new BigDecimal(quantity).multiply(price)).subtract(orderFee);				
+			account.setBalance(balance.add(total));
+			
+			if (orderProcessingMode == TradeConfig.SYNCH) 
+				completeOrderInternal(order.getOrderID());
+			else if (orderProcessingMode == TradeConfig.ASYNCH)
+				queueOrder(order.getOrderID(), false);
+			else //TradeConfig.ASYNC_2PHASE
+				queueOrder(order.getOrderID(), true);
+
+		}
+		catch (Exception e)
+		{		
+			Log.error("TradeBean:sell("+userID+","+holdingID+") --> failed", e);
+			if (order != null) order.cancel();
+			//UPDATE - handle all exceptions like:
+			throw new EJBException("TradeBean:sell("+userID+","+holdingID+")",e);	
+		}
+		return order.getDataBean();
+	}
+
+
+	public Collection getOrders(String userID) 
+		throws FinderException, Exception
+	{
+		
+		if (Log.doTrace()) 
+			Log.trace("TradeBean:getOrders", userID);
+
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 * 					
+		
+			Collection orders = accountHome.findByUserID(userID).getOrders();
+			
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccountForUpdate();
+		 */
+		LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+		Collection orders = account.getOrders();
+
+		ArrayList dataBeans = new ArrayList();
+		if ( orders == null ) return dataBeans;
+		
+		Iterator it = orders.iterator();
+		//TODO: return top 5 orders for now -- next version will add a getAllOrders method
+		//      also need to get orders sorted by order id descending
+		int i=0;
+		while ( (it.hasNext()) && (i++ < 5)) 
+			dataBeans.add(((LocalOrder) it.next()).getDataBean());
+		
+		return dataBeans;
+	}
+
+	public Collection getClosedOrders(String userID) 
+		throws FinderException, Exception
+	{
+		if (Log.doTrace()) 
+			Log.trace("TradeBean:getClosedOrders", userID);
+
+		ArrayList dataBeans = new ArrayList();		
+		try {
+
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 * 			
+			Collection orders = orderHome.findClosedOrdersForUpdate(userID);	 	
+		 *
+  		 	LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccount();							
+		 */
+
+  		 	LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();				  		 	
+  		 	//Get the primary keys for all the closed Orders for this account.
+  		 	Collection ordersKeys = account.getClosedOrders();
+			if ( ordersKeys == null ) return dataBeans;
+		
+			Iterator it = ordersKeys.iterator();
+			while (it.hasNext())
+			{
+				Integer orderKey = (Integer) it.next();
+				LocalOrder order = (LocalOrder) orderHome.findByPrimaryKeyForUpdate(orderKey);
+				//Complete the order 
+				order.setOrderStatus("completed");
+				dataBeans.add(order.getDataBean());
+			}
+			
+		}
+		catch (Exception e)
+		{
+			Log.error("TradeBean.getClosedOrders", e);
+			throw new EJBException("TradeBean.getClosedOrders - error", e);
+		}
+		return dataBeans;
+	}
+	
+
+	public OrderDataBean completeOrder(Integer orderID, boolean twoPhase)
+	throws Exception {
+		if (Log.doTrace()) Log.trace("TradeBean:completeOrder", orderID + " twoPhase="+twoPhase);
+		if (twoPhase)
+			return completeOrderInternal(orderID);
+		else
+		{
+			// invoke the completeOrderOnePhase -- which requires a new transaction
+			// the completeOrder will run in it's own transaction thus not requiring a 
+			// 2-phase commit
+			return ((Trade)context.getEJBObject()).completeOrderOnePhase(orderID);
+		}
+	}
+	
+	//completeOrderOnePhase method is deployed w/ TXN_REQUIRES_NEW
+	//thus the completeOrder call from the MDB should not require a 2-phase commit
+	public OrderDataBean completeOrderOnePhase(Integer orderID)
+	throws Exception {
+		if (Log.doTrace()) Log.trace("TradeBean:completeOrderOnePhase", orderID);		
+		return completeOrderInternal(orderID);
+	}
+
+	private OrderDataBean completeOrderInternal(Integer orderID) 
+			throws Exception
+	{
+
+		LocalOrder order = orderHome.findByPrimaryKeyForUpdate(orderID);
+		
+		if (order == null)
+		{
+			Log.error("TradeBean:completeOrderInternal  -- Unable to find Order " + orderID + " FBPK returned " + order);
+			order.cancel();			
+			return order.getDataBean();
+		}
+
+		String orderType = order.getOrderType();
+		String orderStatus = order.getOrderStatus();
+
+		if (order.isCompleted())
+			throw new EJBException("Error: attempt to complete Order that is already completed\n" + order);
+
+		LocalAccount account = order.getAccount();
+		LocalQuote     quote = order.getQuote();
+		LocalHolding holding = order.getHoldingForUpdate();
+		BigDecimal     price = order.getPrice();
+		double	    quantity = order.getQuantity();
+		BigDecimal  orderFee = order.getOrderFee();
+
+		BigDecimal balance = account.getBalance();
+
+		/* 
+		 * 	getProfile is marked as Pess. Update to get a DB lock
+		 * Here we invoke getProfileForRead which is deployed to not
+		 * lock the DB (Pess. Read)
+		 */	 
+		 String userID = account.getProfile().getUserID();
+		 	
+		/*	 
+		 * total = (quantity * purchasePrice) + orderFee
+		 */
+
+			
+		if (Log.doTrace()) Log.trace(
+					"TradeBeanInternal:completeOrder--> Completing Order " + order.getOrderID()
+					 + "\n\t Order info: "   +   order
+					 + "\n\t Account info: " + account
+					 + "\n\t Quote info: "   +   quote
+					 + "\n\t Holding info: " + holding);
+			
+
+		if (order.isBuy()) {
+			/* Complete a Buy operation
+			 *	- create a new Holding for the Account
+			 *	- deduct the Order cost from the Account balance
+			 */
+
+			LocalHolding newHolding = createHolding(account, quote, quantity, price);
+			order.setHolding(newHolding);
+		}
+		
+		if (order.isSell()) {
+			/* Complete a Sell operation
+			 *	- remove the Holding from the Account
+			 *	- deposit the Order proceeds to the Account balance
+			 */
+			if ( holding == null )
+			{
+				Log.error("TradeBean:completeOrderInternal -- Unable to sell order " + order.getOrderID() + " holding already sold");
+				order.cancel();
+				return order.getDataBean();
+			}
+			else
+			{	
+				holding.remove();
+				holding = null;
+			}
+
+			// This is managed by the container
+			// order.setHolding(null);
+
+		}
+		order.setOrderStatus("closed");
+
+		order.setCompletionDate(new java.sql.Timestamp(System.currentTimeMillis()));
+		
+		if (Log.doTrace()) Log.trace(
+					"TradeBean:completeOrder--> Completed Order " + order.getOrderID()
+					 + "\n\t Order info: "   +   order
+					 + "\n\t Account info: " + account
+					 + "\n\t Quote info: "   +   quote
+					 + "\n\t Holding info: " + holding);
+
+		BigDecimal priceChangeFactor = TradeConfig.getRandomPriceChangeFactor();
+		if(Log.doTrace())
+			Log.trace("Calling TradeAction:orderCompleted from Session EJB using Session Object");		
+			//FUTURE All getEJBObjects could be local -- need to add local I/F
+
+		TradeServices trade = (TradeServices)context.getEJBObject();
+		TradeAction tradeAction = new TradeAction(trade);
+
+		//signify this order for user userID is complete
+		tradeAction.orderCompleted(userID, orderID);
+		return order.getDataBean();
+	}
+	
+	
+	//These methods are used to provide the 1-phase commit runtime option for TradeDirect
+	// Basically these methods are deployed as txn requires new and invoke TradeDirect methods
+	// There is no mechanism outside of EJB to start a new transaction
+    public OrderDataBean completeOrderOnePhaseDirect(Integer orderID)
+    throws Exception {
+    	if (Log.doTrace())
+    		Log.trace("TradeBean:completeOrderOnePhaseDirect -- completing order by calling TradeDirect orderID=" +orderID);
+    	return (new org.apache.geronimo.samples.daytrader.direct.TradeDirect()).completeOrderOnePhase(orderID);
+    }
+	public void cancelOrderOnePhaseDirect(Integer orderID) 
+	throws Exception {
+    	if (Log.doTrace())
+    		Log.trace("TradeBean:cancelOrderOnePhaseDirect -- cancelling order by calling TradeDirect orderID=" +orderID);
+		(new org.apache.geronimo.samples.daytrader.direct.TradeDirect()).cancelOrderOnePhase(orderID);
+	}
+	
+	
+	public void cancelOrder(Integer orderID, boolean twoPhase)
+	throws Exception {
+		if (Log.doTrace()) Log.trace("TradeBean:cancelOrder", orderID + " twoPhase="+twoPhase);
+		if (twoPhase)
+			cancelOrderInternal(orderID);
+		else
+		{
+			// invoke the cancelOrderOnePhase -- which requires a new transaction
+			// the completeOrder will run in it's own transaction thus not requiring a 
+			// 2-phase commit
+			((Trade)context.getEJBObject()).cancelOrderOnePhase(orderID);
+		}
+	}
+
+	//cancelOrderOnePhase method is deployed w/ TXN_REQUIRES_NEW
+	//thus the completeOrder call from the MDB should not require a 2-phase commit
+	public void cancelOrderOnePhase(Integer orderID)
+	throws Exception {
+		if (Log.doTrace()) Log.trace("TradeBean:cancelOrderOnePhase", orderID);		
+		cancelOrderInternal(orderID);
+	}
+	
+	
+	private void cancelOrderInternal(Integer orderID)
+	throws Exception
+	{
+		LocalOrder order = orderHome.findByPrimaryKeyForUpdate(orderID);
+		order.cancel();
+	}
+	
+		
+	public void orderCompleted(String userID, Integer orderID) 
+	throws Exception
+	{
+		throw new UnsupportedOperationException("TradeBean:orderCompleted method not supported");
+	}
+	public LocalHolding createHolding(
+		LocalAccount account,
+		LocalQuote quote,
+		double quantity,
+		BigDecimal purchasePrice) 
+		throws Exception		
+	{
+		LocalHolding newHolding = null;
+		Integer holdingID = null;
+		try {
+
+		if (Log.doTrace())
+			Log.trace("TradeBean:createHolding");
+
+			holdingID = keySequence.getNextID("holding");
+			newHolding =
+				holdingHome.create(holdingID, account, quote, quantity, purchasePrice);
+		} catch (Exception e) {
+			String error = "Failed to create Holding for account: " + account.getAccountID() 
+							+ " with quote: " + quote.getSymbol() 
+							+ " holdingID: " + holdingID 
+							+ " quantity: " + quantity + "\n";
+			Log.error(e, error );
+			throw new EJBException(error,e);
+		}
+		return newHolding;
+	}
+
+	public Collection getHoldings(String userID) 
+		throws FinderException, Exception
+	{
+		if (Log.doTrace())
+			Log.trace("TradeBean:getHoldings", userID);
+		Collection holdings = holdingHome.findByUserID(userID);
+		if (Log.doTrace())
+			Log.trace("Got holdings collection size="+holdings.size());		
+		ArrayList dataBeans = new ArrayList();
+		if ( holdings == null ) return dataBeans;
+	
+		Iterator it = holdings.iterator();
+		while (it.hasNext()) {
+			HoldingDataBean holdingData = ((LocalHolding) it.next()).getDataBean();
+			dataBeans.add(holdingData);
+		}
+		return dataBeans;
+	}
+	
+	public HoldingDataBean getHolding(Integer holdingID) 
+		throws FinderException, Exception
+	{
+		if (Log.doTrace())
+			Log.trace("TradeBean:getHolding", holdingID);
+		LocalHolding holding = holdingHome.findByPrimaryKey(holdingID);
+		HoldingDataBean holdingData = holding.getDataBean();		
+		if (Log.doTrace())
+			Log.trace("TradeBean:getHolding " + holdingData);		
+		return holdingData;
+	}
+
+
+
+	public LocalOrder createOrder(
+		int orderID,
+		LocalAccount account,
+		LocalQuote quote,
+		LocalHolding holding,
+		String orderType,
+		double quantity)
+	throws Exception {
+		try
+		{
+			return createOrder(new Integer(orderID), account, quote, holding, orderType, quantity);
+		}
+		catch(Exception e)
+		{
+			Log.error("TradeBean:createOrder -- failed to create Order", e);
+			throw new EJBException("TradeBean:createOrder -- failed to create Order", e);
+		}
+	}
+
+	public LocalOrder createOrder(
+		Integer orderID,
+		LocalAccount account,
+		LocalQuote quote,
+		LocalHolding holding,
+		String orderType,
+		double quantity)
+	throws CreateException, Exception {
+
+		LocalOrder order = null;
+		
+		if (Log.doTrace() )
+			Log.trace(
+			"TradeBean:createOrder(orderID="
+				+ orderID
+				+ " account="
+				+ ((account == null) ? null : account.getPrimaryKey())
+				+ " quote="
+				+ ((quote == null) ? null : quote.getPrimaryKey())
+				+ " orderType="
+				+ orderType
+				+ " quantity="
+				+ quantity);
+		try
+		{			
+			order =	orderHome.create(orderID, account, quote, holding, orderType, quantity);
+		}
+		catch(Exception e)
+		{
+			Log.error("TradeBean:createOrder -- failed to create Order", e);
+			throw new EJBException("TradeBean:createOrder -- failed to create Order", e);
+		}		
+		return order;
+	}
+
+	public AccountDataBean login(String userID, String password)
+	throws FinderException, Exception {
+		
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 *
+			LocalAccount account = accountHome.findByUserIDForUpdate(userID);
+		 
+		    LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccountForUpdate();
+		 */
+		 LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+		 
+		if (Log.doTrace())
+			Log.trace("TradeBean:login", userID, password);
+		account.login(password);
+		AccountDataBean accountData = account.getDataBean();
+		if (Log.doTrace())
+			Log.trace("TradeBean:login(" + userID + "," + password + ") success" + accountData);
+		return accountData;
+	}
+
+	public void logout(String userID)
+	throws FinderException, Exception {
+		if (Log.doTrace())
+			Log.trace("TradeBean:logout", userID);
+			
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 
+		 * 	LocalAccount account = accountHome.findByUserIDForUpdate(userID);
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccountForUpdate();
+		 *
+		 */
+		LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+		
+		
+		if (Log.doTrace()) Log.trace("TradeBean:logout(" + userID + ") success");
+		account.logout();
+	}
+
+	public AccountDataBean register(
+		String userID,
+		String password,
+		String fullname,
+		String address,
+		String email,
+		String creditcard,
+		BigDecimal openBalance)
+		throws CreateException, Exception {
+
+		LocalAccount account = null;
+
+		try {
+		if (Log.doTrace())
+			Log.trace("TradeBean:register", userID, password, fullname, address, email, creditcard, openBalance);
+			
+			Integer accountID = keySequence.getNextID("account");
+			account =
+				accountHome.create(
+					accountID,
+					userID,
+					password,
+					openBalance,
+					fullname,
+					address,
+					email,
+					creditcard);
+		}
+		catch (Exception e)
+		{
+				Log.error("Failed to register new Account\n" + e);
+				throw new EJBException("Failed to register new Account\n", e);
+		}	
+		AccountDataBean accountData = account.getDataBean();		
+		return accountData;
+	}
+
+	public AccountDataBean getAccountData(String userID)
+	throws FinderException, Exception {
+
+		if (Log.doTrace())
+			Log.trace("TradeBean:getAccountData", userID);
+
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 * 					
+			LocalAccount account = accountHome.findByUserID(userID);
+			
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccountForUpdate();
+		 */
+		LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+
+		AccountDataBean accountData = account.getDataBean();
+		return accountData;
+	}
+	public AccountProfileDataBean getAccountProfileData(String userID)
+	throws FinderException, Exception {
+		if (Log.doTrace())
+			Log.trace("TradeBean:getAccountProfileData", userID);
+
+		/*  The following commented code shows alternative forms of the finder needed for this
+		 *  method
+		 *  The first alternative requires a 2-table join. Some database cannot allocate an Update
+		 *  Lock on a join select.
+		 * 
+		 *  The second alternative shows the finder being executed without allocation an update
+		 *  lock on the row. Normally, an update lock would not be necessary, but is required if
+		 *  the same user logs in multiple times to avoid a deadlock situation.
+		 * 
+		 *  The third alternative runs the finder and allocates an update lock on the row(s)
+		 * 					
+			LocalAccount account = accountHome.findByUserID(userID);
+			
+			LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKey(userID)).getAccountForUpdate();
+		 */
+		LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(userID)).getAccountForUpdate();
+
+		AccountProfileDataBean accountProfileData = account.getProfileDataBean();
+		return accountProfileData;
+	}
+
+	public AccountProfileDataBean updateAccountProfile(AccountProfileDataBean accountProfileData)
+	throws FinderException, Exception {
+		
+		if (Log.doTrace())
+			Log.trace("TradeBean:updateAccountProfileData", accountProfileData);
+		
+		LocalAccount account = ((LocalAccountProfile) profileHome.findByPrimaryKeyForUpdate(accountProfileData.getUserID())).getAccountForUpdate();
+
+		accountProfileData =
+			account.updateAccountProfile(accountProfileData);
+		return accountProfileData;
+	}
+	
+	public RunStatsDataBean resetTrade(boolean deleteAll)
+	throws Exception
+	{
+		if (Log.doTrace())
+			Log.trace("TradeBean:resetTrade", new Boolean(deleteAll));
+
+		//Clear MDB Statistics		
+		MDBStats.getInstance().reset();
+		// Reset Trade
+		return new org.apache.geronimo.samples.daytrader.direct.TradeDirect().resetTrade(deleteAll);
+	}
+
+	private Collection getDataBeansCollection(Collection entities)
+	{
+		ArrayList dataBeans = new ArrayList();
+	
+		if ( (entities == null) || (entities.size()<= 0) )
+		return dataBeans;
+
+		Iterator it = entities.iterator();
+		while (it.hasNext()) {
+			LocalQuote entity = (LocalQuote) it.next();
+			Object o = (Object)entity.getDataBean();
+			dataBeans.add(o);
+		}
+		return dataBeans;
+	}
+
+	/**
+	 * provides a simple session method with no database access to test performance of a simple
+	 * path through a stateless session 
+	 * @param investment amount
+	 * @param NetValue current value
+	 * @return return on investment as a percentage
+	 */
+	public double investmentReturn(double investment, double NetValue)
+		throws Exception
+	{
+		if (Log.doTrace())
+			Log.trace("TradeBean:investmentReturn");
+		
+		double diff = NetValue - investment;
+		double ir = diff / investment;
+		return ir;
+
+	}
+	
+	/**
+	 * This method provides a ping test for a 2-phase commit operation
+	 * 
+	 * @param symbol to lookup
+	 * @return quoteData after sending JMS message
+	 */	
+	public QuoteDataBean pingTwoPhase(String symbol)
+	throws Exception
+	{
+		if (Log.doTrace()) Log.trace("TradeBean:pingTwoPhase", symbol);
+		QuoteDataBean quoteData=null;
+		Connection conn = null;
+		Session sess = null;		
+		try {
+			
+			//Get a Quote and send a JMS message in a 2-phase commit
+			LocalQuote quote = quoteHome.findByPrimaryKey(symbol);
+			quoteData = quote.getDataBean();
+
+			conn = qConnFactory.createConnection();                        
+			sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+			MessageProducer msgProducer = sess.createProducer(queue);
+			TextMessage   message = sess.createTextMessage();
+
+			String command= "ping";
+			message.setStringProperty("command", command);
+			message.setLongProperty("publishTime", System.currentTimeMillis());			
+			message.setText("Ping message for queue java:comp/env/jms/TradeBrokerQueue sent from TradeSessionEJB:pingTwoPhase at " + new java.util.Date());
+
+			msgProducer.send(message);	
+		} 
+		catch (Exception e) {
+			Log.error("TradeBean:pingTwoPhase -- exception caught",e);
+		}
+
+		finally {
+			if (conn != null)
+				conn.close();	
+			if (sess != null)
+				sess.close();
+		}			
+		
+		return quoteData;
+	}	
+
+
+    /* Required javax.ejb.SessionBean interface methods */
+
+	public TradeBean() {
+	}
+
+	private static boolean warnJMSFailure = true;
+	public void ejbCreate() throws CreateException {
+		try {
+
+			if (Log.doTrace())
+				Log.trace("TradeBean:ejbCreate  -- JNDI lookups of EJB and JMS resources");
+
+			InitialContext ic = new InitialContext();
+			quoteHome 		= (LocalQuoteHome)   ic.lookup("java:comp/env/ejb/Quote");
+			accountHome 	= (LocalAccountHome) ic.lookup("java:comp/env/ejb/Account");
+			profileHome 	= (LocalAccountProfileHome) ic.lookup("java:comp/env/ejb/AccountProfile");
+			holdingHome 	= (LocalHoldingHome) ic.lookup("java:comp/env/ejb/Holding");
+			orderHome 		= (LocalOrderHome)   ic.lookup("java:comp/env/ejb/Order");
+			keySequenceHome = (LocalKeySequenceHome) ic.lookup("java:comp/env/ejb/KeySequence");			
+
+			orderBySQLSupported = ( (Boolean) ic.lookup("java:comp/env/orderBySQLSupported") ).booleanValue();
+			publishQuotePriceChange  = ( (Boolean) ic.lookup("java:comp/env/publishQuotePriceChange") ).booleanValue();
+			updateQuotePrices  = ( (Boolean) ic.lookup("java:comp/env/updateQuotePrices") ).booleanValue();
+			TradeConfig.setUpdateQuotePrices(updateQuotePrices);
+
+			try
+			{
+				qConnFactory = (ConnectionFactory) ic.lookup("java:comp/env/jms/QueueConnectionFactory");
+				queue = (Queue) ic.lookup("java:comp/env/jms/TradeBrokerQueue");
+				tConnFactory = (ConnectionFactory) ic.lookup("java:comp/env/jms/TopicConnectionFactory");
+				streamerTopic = (Topic) ic.lookup("java:comp/env/jms/TradeStreamerTopic");
+			}
+			catch (Exception e)
+			{
+				if (warnJMSFailure == true)
+				{
+					warnJMSFailure = false;
+					Log.error("TradeBean:ejbCreate  Unable to lookup JMS Resources\n\t -- Asynchronous mode will not work correctly and Quote Price change publishing will be disabled", e);
+				}				
+				publishQuotePriceChange = false;			
+			}		
+			
+		} catch (Exception e) {
+			Log.error("TradeBean:ejbCreate: Lookup of Local Entity Homes Failed\n" + e);
+			e.printStackTrace();
+			//UPDATE
+			//throw new CreateException(e.toString());
+		}
+
+		if ((quoteHome 	== null) ||
+			(     accountHome	== null) ||
+			(     holdingHome	== null) ||
+			(       orderHome	== null) ||
+			( keySequenceHome	== null) //||			 		
+//			(    qConnFactory	== null) ||
+//			(           queue	== null) ||
+//			(    tConnFactory	== null) ||
+//			(   streamerTopic	== null) ||
+			)
+			{
+				String error = "TradeBean:ejbCreate()  JNDI lookup of Trade resource failed\n" +
+					"\n\t quoteHome="+quoteHome+
+					"\n\t accountHome="+ accountHome+
+					"\n\t holdingHome="+ holdingHome+
+					"\n\t orderHome="+ orderHome+
+					"\n\t qConnFactory="+ qConnFactory+
+					"\n\t queue="+ queue+
+					"\n\t tConnFactory="+ tConnFactory+
+					"\n\t streamerTopic="+ streamerTopic;
+				Log.error(error);
+				//UPDATE
+				//throw new EJBException(error);
+			}
+		keySequence = keySequenceHome.create();
+	}
+
+	public void ejbRemove() 
+	{
+		try
+		{
+			if (Log.doTrace())
+				Log.trace("TradeBean:ejbRemove");
+		}
+		catch (Exception e)
+		{
+			Log.error(e,"Unable to close Queue or Topic connection on Session EJB remove");
+		}
+	}
+	public void ejbActivate() {
+	}
+	public void ejbPassivate() {
+	}
+
+	public void setSessionContext(SessionContext sc) {
+		context = sc;
+	}
+}

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBrokerMDB.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBrokerMDB.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBrokerMDB.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeBrokerMDB.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,178 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.ejb;
+
+import javax.ejb.*;
+import javax.jms.*;
+import javax.naming.*;
+
+import org.apache.geronimo.samples.daytrader.direct.*;
+import org.apache.geronimo.samples.daytrader.util.*;
+
+import org.apache.geronimo.samples.daytrader.*;
+
+public class TradeBrokerMDB 
+	implements MessageDrivenBean, MessageListener {
+
+
+    private transient MessageDrivenContext mdc = null;
+    private Context context;
+    
+	private TradeHome tradeHome;
+	
+ 	//Message send/receive timing Statistics
+ 	private MDBStats mdbStats;
+ 	private int statInterval = 100;
+
+    public void onMessage(Message message) {
+        try {
+        	if (Log.doTrace()) 
+        		Log.trace("TradeBroker:onMessage -- received message -->" 
+        			+ ((TextMessage)message).getText() + "command-->" 
+        			+	message.getStringProperty("command") + "<--");
+			
+			if (message.getJMSRedelivered())
+			{
+				Log.log("TradeBrokerMDB: The following JMS message was redelivered due to a rollback:\n"+ ((TextMessage)message).getText() );
+				//Order has been cancelled -- ignore returned messages 
+				return;        			
+			}
+        	String command = message.getStringProperty("command");
+        	if (command==null) 
+        	{
+        		Log.debug("TradeBrokerMDB:onMessage -- received message with null command. Message-->"+message);
+        		return;
+	        }
+            if (command.equalsIgnoreCase("neworder")) 
+            {
+            	/* Get the Order ID and complete the Order */
+		        Integer orderID = new Integer(message.getIntProperty("orderID"));
+		        boolean twoPhase = message.getBooleanProperty("twoPhase");
+		        boolean direct = message.getBooleanProperty("direct");
+        		long publishTime = message.getLongProperty("publishTime");
+				long receiveTime = System.currentTimeMillis();			
+		        
+
+		        TradeServices trade = null;
+
+		        try {
+		        	trade = getTrade(direct);
+				
+					if (Log.doTrace()) 
+						Log.trace("TradeBrokerMDB:onMessage - completing order " + orderID + " twoPhase=" +twoPhase + " direct="+direct);
+
+					trade.completeOrder(orderID, twoPhase);
+					
+					TimerStat currentStats = mdbStats.addTiming("TradeBrokerMDB:neworder", publishTime, receiveTime );
+					
+					if ( (currentStats.getCount() % statInterval) == 0)
+					{
+							Log.log(new java.util.Date()+ "\nTradeBrokerMDB: processed 100 stock trading orders. " + 
+  								"\nCurrent NewOrder Message Statistics\n\tTotal NewOrders process = " + currentStats.getCount() + 
+								"\n\tTime to receive messages (in seconds):" +
+								"\n\t\tmin: " +currentStats.getMinSecs()+
+								"\n\t\tmax: " +currentStats.getMaxSecs()+
+								"\n\t\tavg: " +currentStats.getAvgSecs()+
+								"\n\n\n\tThe current order being processed is:\n\t"+((TextMessage)message).getText());
+					}
+		        }
+		        catch (Exception e) 
+		        {
+		        	Log.error("TradeBrokerMDB:onMessage Exception completing order: " + orderID + "\n",  e);
+       	            mdc.setRollbackOnly();
+       	            /* UPDATE - order is cancelled in trade if an error is caught
+		        	try
+		        	{ 
+		   	        	trade.cancelOrder(orderID, twoPhase);
+		        	}
+		        	catch (Exception e2)
+		        	{
+			        	Log.error("order cancel failed", e);
+		        	}*/
+		        }
+        	}
+        	else if (command.equalsIgnoreCase("ping")) {
+        		if (Log.doTrace())
+	        		Log.trace("TradeBrokerMDB:onMessage  received test command -- message: " + ((TextMessage)message).getText());
+	        		
+        		long publishTime = message.getLongProperty("publishTime");
+				long receiveTime = System.currentTimeMillis();			
+
+				TimerStat currentStats = mdbStats.addTiming("TradeBrokerMDB:ping", publishTime, receiveTime );
+					
+				if ( (currentStats.getCount() % statInterval) == 0)
+				{
+					Log.log(new java.util.Date()+ "\nTradeBrokerMDB: received 100 ping messages. " + 
+  							"\nCurrent Ping Message Statistics\n\tTotal ping message count = " + currentStats.getCount() + 
+							"\n\tTime to receive messages (in seconds):" +
+							"\n\t\tmin: " +currentStats.getMinSecs()+
+							"\n\t\tmax: " +currentStats.getMaxSecs()+
+							"\n\t\tavg: " +currentStats.getAvgSecs()+
+							"\n\n\n\tThe current message is:\n\t"+((TextMessage)message).getText());
+	        	}
+        	}
+        	else
+        		Log.error("TradeBrokerMDB:onMessage - unknown message request command-->" + command + "<-- message=" + ((TextMessage)message).getText());
+        } 
+        catch (Throwable t) 
+        {
+        	//JMS onMessage should handle all exceptions
+			Log.error("TradeBrokerMDB: Error rolling back transaction", t);			
+            mdc.setRollbackOnly();
+        }
+    }  
+    
+    private TradeServices getTrade(boolean direct)
+    throws Exception
+    {
+    	TradeServices trade;
+       	if (direct)
+			trade = new TradeDirect();
+		else
+			trade = tradeHome.create();
+
+		return trade;
+    }
+    
+
+    public TradeBrokerMDB() {
+        if (Log.doTrace()) Log.trace("TradeBrokerMDB:TradeBrokerMDB()");
+    }
+
+    public void setMessageDrivenContext(MessageDrivenContext mdc) {
+        if (Log.doTrace()) Log.trace("TradeBrokerMDB:setMessageDriventContext()");
+		this.mdc = mdc;		
+    }
+
+    public void ejbCreate() {
+        if (Log.doTrace()) Log.trace("TradeBrokerMDB:ejbCreate()");
+		try {
+			InitialContext ic = new InitialContext();
+			tradeHome 	= (TradeHome) ic.lookup("java:comp/env/ejb/Trade");
+			statInterval = ( (Integer) ic.lookup("java:comp/env/statInterval") ).intValue();
+			if ( statInterval <= 0 ) statInterval = 100;
+			mdbStats = MDBStats.getInstance();
+		} catch (Exception e) {
+			Log.error("TradeBrokerMDB:ejbCreate Lookup of Local Entity Homes Failed\n" + e);
+		}        
+    }
+       
+    public void ejbRemove() {
+        if (Log.doTrace()) Log.trace("TradeBrokerMDB:ejbRemove()");
+    }
+
}
\ No newline at end of file

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeHome.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeHome.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeHome.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeHome.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,28 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.ejb;
+
+import java.rmi.RemoteException;
+import javax.ejb.CreateException;
+import javax.ejb.EJBHome;
+
+public interface TradeHome extends EJBHome {
+ 
+    Trade create() throws RemoteException, CreateException;
+
+}

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeStreamerMDB.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeStreamerMDB.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeStreamerMDB.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/ejb/TradeStreamerMDB.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,134 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.ejb;
+
+import javax.ejb.*;
+import javax.jms.*;
+import javax.naming.*;
+
+import org.apache.geronimo.samples.daytrader.util.*;
+
+public class TradeStreamerMDB 
+	implements MessageDrivenBean, MessageListener {
+
+
+    private transient MessageDrivenContext mdc = null;
+    private Context context;
+    
+	private TradeHome tradeHome;
+	
+ 	//Message send/receive timing Statistics
+ 	private MDBStats mdbStats;
+ 	private int statInterval = 100;
+
+    
+    public void onMessage(Message message) {
+        try {
+        	if (Log.doTrace()) 
+        		Log.trace("TradeStream:onMessage -- received message -->" 
+        			+ ((TextMessage)message).getText() + "command-->" 
+        			+	message.getStringProperty("command") + "<--");
+        	String command = message.getStringProperty("command");
+        	if (command==null) 
+        	{
+        		Log.debug("TradeStreamerMDB:onMessage -- received message with null command. Message-->"+message);
+        		return;
+	        }
+            if (command.equalsIgnoreCase("updateQuote")) 
+            {
+        	if (Log.doTrace()) 
+        		Log.trace("TradeStreamer:onMessage -- received message -->" 
+        			+ ((TextMessage)message).getText()
+        			+ "\n\t symbol = " + message.getStringProperty("symbol")
+        			+ "\n\t current price =" + message.getStringProperty("price")
+        			+ "\n\t old price =" + message.getStringProperty("oldPrice")
+        			);
+        		long publishTime = message.getLongProperty("publishTime");
+				long receiveTime = System.currentTimeMillis();			
+				
+				TimerStat currentStats = mdbStats.addTiming("TradeBrokerStreamer:udpateQuote", publishTime, receiveTime );
+				
+				if ( (currentStats.getCount() % statInterval) == 0)
+				{
+					Log.log(new java.util.Date()+ "\nTradeStreamerMDB: 100 Trade stock prices updated:  " + 
+  							"\nCurrent Statistics\n\tTotal update Quote Price message count = " + currentStats.getCount() + 
+							"\n\tTime to receive stock update alerts messages (in seconds):" +
+							"\n\t\tmin: " +currentStats.getMinSecs()+
+							"\n\t\tmax: " +currentStats.getMaxSecs()+
+							"\n\t\tavg: " +currentStats.getAvgSecs()+
+							"\n\n\n\tThe current price update is:\n\t"+((TextMessage)message).getText()) ;
+	        	}
+        	}
+        	else if (command.equalsIgnoreCase("ping")) {
+        		if (Log.doTrace())
+	        		Log.trace("TradeStreamerMDB:onMessage  received ping command -- message: " + ((TextMessage)message).getText());	        		
+
+        		long publishTime = message.getLongProperty("publishTime");
+				long receiveTime = System.currentTimeMillis();			
+
+				TimerStat currentStats = mdbStats.addTiming("TradeStreamerMDB:ping", publishTime, receiveTime );
+
+				if ( (currentStats.getCount() % statInterval) == 0)
+				{
+					Log.log(new java.util.Date()+ "\nTradeStreamerMDB: received 100 ping messages. " + 
+  							"\nCurrent Ping Message Statistics\n\tTotal ping message count = " + currentStats.getCount() + 
+							"\n\tTime to receive messages (in seconds):" +
+							"\n\t\tmin: " +currentStats.getMinSecs()+
+							"\n\t\tmax: " +currentStats.getMaxSecs()+
+							"\n\t\tavg: " +currentStats.getAvgSecs()+
+							"\n\n\n\tThe current message is:\n\t"+((TextMessage)message).getText());
+				}
+        	}
+        	else
+        		Log.error("TradeStreamerMDB:onMessage - unknown message request command-->" + command + "<-- message=" + ((TextMessage)message).getText());
+        } 
+        catch (Throwable t) 
+        {
+        	//JMS onMessage should handle all exceptions
+			Log.error("TradeStreamerMDB: Exception", t);			
+			//UPDATE - Not rolling back for now -- so error messages are not redelivered
+            //mdc.setRollbackOnly();
+        }
+    }      	
+    	
+
+    public TradeStreamerMDB() {
+        if (Log.doTrace()) Log.trace("TradeStreamerMDB:TradeStreamerMDB()");
+    }
+
+    public void setMessageDrivenContext(MessageDrivenContext mdc) {
+        if (Log.doTrace()) Log.trace("TradeStreamerMDB:setMessageDriventContext()");
+		this.mdc = mdc;		
+    }
+
+    public void ejbCreate() {
+        if (Log.doTrace()) Log.trace("TradeStreamerMDB:ejbCreate()");
+		try {
+			InitialContext ic = new InitialContext();
+			statInterval = ( (Integer) ic.lookup("java:comp/env/statInterval") ).intValue();
+			if ( statInterval <= 0 ) statInterval = 100;
+			mdbStats = MDBStats.getInstance();
+		} catch (Exception e) {
+			Log.error("TradeStreamerMDB:ejbCreate Lookup of EJB environment failed\n" + e);
+		}        
+    }
+       
+    public void ejbRemove() {
+        if (Log.doTrace()) Log.trace("TradeStreamerMDB:ejbRemove()");
+    }
+
}
\ No newline at end of file

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/soap/TradeWebSoapProxy.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/soap/TradeWebSoapProxy.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/soap/TradeWebSoapProxy.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/soap/TradeWebSoapProxy.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,375 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.soap;
+
+import org.apache.geronimo.samples.daytrader.*;
+
+import java.util.*;
+import java.net.*;
+import javax.xml.rpc.*;
+import javax.naming.*;
+import javax.xml.namespace.*;
+
+import java.rmi.RemoteException;
+import java.math.BigDecimal;
+import javax.ejb.FinderException;
+
+import org.apache.geronimo.samples.daytrader.util.*;
+
+public class TradeWebSoapProxy implements org.apache.geronimo.samples.daytrader.TradeServices {
+
+	private static String servicePort;
+	private static org.apache.geronimo.samples.daytrader.client.ws.TradeWSServices trade;
+	private static final String jndiName = "java:comp/env/service/Trade";
+
+    public TradeWebSoapProxy() {
+    	if (trade == null) {
+    		trade = getTrade();
+    	}
+    }
+
+	public static org.apache.geronimo.samples.daytrader.client.ws.TradeWSServices getTrade() {
+		try {
+			trade = getPortFromFactory();
+			((Stub)trade)._setProperty("javax.xml.rpc.service.endpoint.address", TradeConfig.getSoapURL());
+		}
+		catch (Exception e) {
+			System.out.println("problem getting trade port " + e);
+			e.printStackTrace();
+		}
+		return trade;
+	}
+
+	private static org.apache.geronimo.samples.daytrader.client.ws.TradeWSServices getPortFromFactory() throws ServiceException, MalformedURLException {
+		if (Log.doTrace()) {
+			Log.traceEnter("TradeWebSoapProxt.getPortFromFactory()");
+		}
+		// JSR 109 lookup
+		try {
+			InitialContext context = new InitialContext();
+			if (Log.doTrace()) {
+				Log.trace("attempting JSR109 lookup with jndi of " + jndiName);
+			}
+			org.apache.geronimo.samples.daytrader.client.ws.Trade tradeService1 = (org.apache.geronimo.samples.daytrader.client.ws.Trade)context.lookup(jndiName);
+			return tradeService1.getTradeWSServices();
+		}
+		catch (Exception e) {
+			Log.error(e, "JSR 109 lookup failed .. defaulting to JSR 101");
+		}
+
+		// JSR 101 lookup
+		if (Log.doTrace()) {
+			Log.trace("attempting JSR101 lookup with url of " + TradeConfig.getSoapURL());
+		}
+		URL wsdlLoc = new URL(TradeConfig.getSoapURL());
+		QName serviceName = new QName("http://daytrader.samples.geronimo.apache.org", "Trade");
+		Service tService = ServiceFactory.newInstance().createService(wsdlLoc, serviceName);
+		QName portName = new QName("http://daytrader.samples.geronimo.apache.org", "TradeWSServices");
+		return (org.apache.geronimo.samples.daytrader.client.ws.TradeWSServices)tService.getPort(portName, org.apache.geronimo.samples.daytrader.client.ws.TradeWSServices.class);
+	}
+	
+	public static void updateServicePort() {
+		// reconstruct Trade as service port has changed
+		trade = getTrade();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#buy(java.lang.String, java.lang.String, double, int)
+	 */
+	public org.apache.geronimo.samples.daytrader.OrderDataBean buy(String userID, String symbol, double quantity,	int orderProcessingMode) throws Exception, RemoteException {
+		return convertOrderDataBean(getTrade().buy(userID, symbol, quantity, orderProcessingMode));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#cancelOrder(java.lang.Integer, boolean)
+	 */
+	public void cancelOrder(Integer orderID, boolean twoPhase) throws Exception, RemoteException {
+		getTrade().cancelOrder(orderID, twoPhase);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#completeOrder(java.lang.Integer, boolean)
+	 */
+	public OrderDataBean completeOrder(Integer orderID, boolean twoPhase) throws Exception, RemoteException {
+		return convertOrderDataBean(getTrade().completeOrder(orderID, twoPhase));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#createQuote(java.lang.String, java.lang.String, java.math.BigDecimal)
+	 */
+	public QuoteDataBean createQuote(String symbol, String companyName, BigDecimal price) throws Exception, RemoteException {
+		return convertQuoteDataBean(getTrade().createQuote(symbol, companyName, price));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getAccountData(java.lang.String)
+	 */
+	public AccountDataBean getAccountData(String userID) throws FinderException, Exception {
+		return convertAccountDataBean(getTrade().getAccountData(userID));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getAccountProfileData(java.lang.String)
+	 */
+	public AccountProfileDataBean getAccountProfileData(String userID) throws Exception, RemoteException {
+		return convertAccountProfileDataBean(getTrade().getAccountProfileData(userID));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getAllQuotes()
+	 */
+	public Collection getAllQuotes() throws Exception, RemoteException {
+		return convertQuoteDataBeanWSArrayToCollectionBase(getTrade().getAllQuotes());
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getClosedOrders(java.lang.String)
+	 */
+	public Collection getClosedOrders(String userID) throws Exception, RemoteException {
+		Object[] orders = getTrade().getClosedOrders(userID);
+		ArrayList ordersRet = new ArrayList();
+		if (orders.length == 0) {
+			return ordersRet;
+		}
+		for (int ii = 0; ii < orders.length; ii++) {
+			ordersRet.add(convertOrderDataBean((org.apache.geronimo.samples.daytrader.client.ws.OrderDataBean)orders[ii]));
+		}
+		return ordersRet;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getHolding(java.lang.Integer)
+	 */
+	public HoldingDataBean getHolding(Integer holdingID) throws Exception, RemoteException {
+		return convertHoldingDataBean(getTrade().getHolding(holdingID));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getHoldings(java.lang.String)
+	 */
+	public Collection getHoldings(String userID) throws Exception, RemoteException {
+		Object[] holdings = getTrade().getHoldings(userID);
+		ArrayList holdingsRet = new ArrayList();
+		if (holdings.length == 0) {
+			return holdingsRet;
+		}
+		
+		for (int ii = 0; ii < holdings.length; ii++) {
+			holdingsRet.add(convertHoldingDataBean((org.apache.geronimo.samples.daytrader.client.ws.HoldingDataBean)holdings[ii]));
+		}
+		return holdingsRet;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getMarketSummary()
+	 */
+	public MarketSummaryDataBean getMarketSummary() throws Exception, RemoteException {
+		return convertMarketSummaryDataBean(getTrade().getMarketSummary());
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getOrders(java.lang.String)
+	 */
+	public Collection getOrders(String userID) throws Exception, RemoteException {
+		Object[] orders = getTrade().getOrders(userID);
+		ArrayList ordersRet = new ArrayList();
+		if (orders.length == 0) {
+			return ordersRet;
+		}
+		for (int ii = 0; ii < orders.length; ii++) {
+			ordersRet.add(convertOrderDataBean((org.apache.geronimo.samples.daytrader.client.ws.OrderDataBean)orders[ii]));
+		}
+		return ordersRet;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#getQuote(java.lang.String)
+	 */
+	public QuoteDataBean getQuote(String symbol) throws Exception, RemoteException {
+		return convertQuoteDataBean(getTrade().getQuote(symbol));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#login(java.lang.String, java.lang.String)
+	 */
+	public AccountDataBean login(String userID, String password) throws Exception, RemoteException {
+		return convertAccountDataBean(getTrade().login(userID, password));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#logout(java.lang.String)
+	 */
+	public void logout(String userID) throws Exception, RemoteException {
+		getTrade().logout(userID);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#orderCompleted(java.lang.String, java.lang.Integer)
+	 */
+	public void orderCompleted(String userID, Integer orderID) throws Exception, RemoteException {
+		getTrade().orderCompleted(userID, orderID);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#queueOrder(java.lang.Integer, boolean)
+	 */
+	public void queueOrder(Integer orderID, boolean twoPhase) throws Exception, RemoteException {
+		getTrade().queueOrder(orderID, twoPhase);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#register(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.math.BigDecimal)
+	 */
+	public AccountDataBean register(String userID, String password, String fullname, String address, String email, String creditcard, BigDecimal openBalance) throws Exception, RemoteException {
+		return convertAccountDataBean(getTrade().register(userID, password, fullname, address, email, creditcard, openBalance));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#resetTrade(boolean)
+	 */
+	public RunStatsDataBean resetTrade(boolean deleteAll) throws Exception, RemoteException {
+		return convertRunStatsDataBean(getTrade().resetTrade(deleteAll));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#sell(java.lang.String, java.lang.Integer, int)
+	 */
+	public OrderDataBean sell(String userID, Integer holdingID,	int orderProcessingMode) throws Exception, RemoteException {
+		return convertOrderDataBean(getTrade().sell(userID, holdingID, orderProcessingMode));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#updateAccountProfile(org.apache.geronimo.samples.daytrader.AccountProfileDataBean)
+	 */
+	public AccountProfileDataBean updateAccountProfile(AccountProfileDataBean profileData) throws Exception, RemoteException {
+		return convertAccountProfileDataBean(getTrade().updateAccountProfile(convertAccountProfileDataBeanToWS(profileData)));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.apache.geronimo.samples.daytrader.TradeServices#updateQuotePriceVolume(java.lang.String, java.math.BigDecimal, double)
+	 */
+	public QuoteDataBean updateQuotePriceVolume(String symbol, BigDecimal newPrice, double sharesTraded) throws Exception, RemoteException {
+		return convertQuoteDataBean(getTrade().updateQuotePriceVolume(symbol, newPrice, sharesTraded));		
+	}
+
+	private org.apache.geronimo.samples.daytrader.OrderDataBean convertOrderDataBean(org.apache.geronimo.samples.daytrader.client.ws.OrderDataBean bean) {
+		return new org.apache.geronimo.samples.daytrader.OrderDataBean(
+			bean.getOrderID(),
+			bean.getOrderType(),
+			bean.getOrderStatus(),
+			bean.getOpenDate() != null ? bean.getOpenDate().getTime() : null,
+			bean.getCompletionDate() != null ? bean.getCompletionDate().getTime() : null,
+			bean.getQuantity(),
+			bean.getPrice(),
+			bean.getOrderFee(),
+			bean.getSymbol());
+	}
+
+	private org.apache.geronimo.samples.daytrader.QuoteDataBean convertQuoteDataBean(org.apache.geronimo.samples.daytrader.client.ws.QuoteDataBean bean) {
+		return new org.apache.geronimo.samples.daytrader.QuoteDataBean(
+			bean.getSymbol(),
+			bean.getCompanyName(),
+			bean.getVolume(),
+			bean.getPrice(),
+			bean.getOpen(),
+			bean.getLow(),
+			bean.getHigh(),
+			bean.getChange());
+	}
+
+	private Collection convertQuoteDataBeanWSArrayToCollectionBase(Object[] quotes) {	
+		ArrayList quotesRet = new ArrayList();
+		if (quotes.length == 0) {
+			return quotesRet;
+		}
+		for (int ii = 0; ii < quotes.length; ii++) {
+			quotesRet.add(convertQuoteDataBean((org.apache.geronimo.samples.daytrader.client.ws.QuoteDataBean)quotes[ii]));
+		}
+		return quotesRet;
+	}
+
+	private org.apache.geronimo.samples.daytrader.HoldingDataBean convertHoldingDataBean(org.apache.geronimo.samples.daytrader.client.ws.HoldingDataBean bean) {
+		return new org.apache.geronimo.samples.daytrader.HoldingDataBean(
+			bean.getHoldingID(),
+			bean.getQuantity(),
+			bean.getPurchasePrice(),
+			bean.getPurchaseDate().getTime(),
+			bean.getQuoteID());
+	}
+
+	private org.apache.geronimo.samples.daytrader.AccountDataBean convertAccountDataBean(org.apache.geronimo.samples.daytrader.client.ws.AccountDataBean bean) {
+		return new org.apache.geronimo.samples.daytrader.AccountDataBean(
+			bean.getAccountID(),
+			bean.getLoginCount(),
+			bean.getLogoutCount(),
+			bean.getLastLogin().getTime(),
+			bean.getCreationDate().getTime(),
+			bean.getBalance(),
+			bean.getOpenBalance(),
+			bean.getProfileID());			
+	}
+
+	private org.apache.geronimo.samples.daytrader.AccountProfileDataBean convertAccountProfileDataBean(org.apache.geronimo.samples.daytrader.client.ws.AccountProfileDataBean bean) {
+		return new org.apache.geronimo.samples.daytrader.AccountProfileDataBean(
+			bean.getUserID(),
+			bean.getPassword(),
+			bean.getFullName(),
+			bean.getAddress(),
+			bean.getEmail(),
+			bean.getCreditCard());
+	}
+
+	private org.apache.geronimo.samples.daytrader.client.ws.AccountProfileDataBean convertAccountProfileDataBeanToWS(org.apache.geronimo.samples.daytrader.AccountProfileDataBean bean) {
+		org.apache.geronimo.samples.daytrader.client.ws.AccountProfileDataBean beanRet = new org.apache.geronimo.samples.daytrader.client.ws.AccountProfileDataBean();
+		beanRet.setUserID(bean.getUserID());
+		beanRet.setPassword(bean.getPassword());
+		beanRet.setFullName(bean.getFullName());
+		beanRet.setAddress(bean.getAddress());
+		beanRet.setEmail(bean.getEmail());
+		beanRet.setCreditCard(bean.getCreditCard());
+		return beanRet;
+	}
+
+	private org.apache.geronimo.samples.daytrader.MarketSummaryDataBean convertMarketSummaryDataBean(org.apache.geronimo.samples.daytrader.client.ws.MarketSummaryDataBeanWS bean) {
+		org.apache.geronimo.samples.daytrader.MarketSummaryDataBean retBean = new org.apache.geronimo.samples.daytrader.MarketSummaryDataBean();
+		retBean.setTopGainers(convertQuoteDataBeanWSArrayToCollectionBase(bean.getTopGainers()));
+		retBean.setTopLosers(convertQuoteDataBeanWSArrayToCollectionBase(bean.getTopLosers()));
+		retBean.setTSIA(bean.getTSIA());
+		retBean.setOpenTSIA(bean.getOpenTSIA());
+		retBean.setVolume(bean.getVolume());
+		// can't use contructor of MSBean as it sets this to the current time
+		retBean.setSummaryDate(bean.getSummaryDate().getTime());
+		return retBean; 
+	}
+	
+	private org.apache.geronimo.samples.daytrader.RunStatsDataBean convertRunStatsDataBean(org.apache.geronimo.samples.daytrader.client.ws.RunStatsDataBean bean) {
+		org.apache.geronimo.samples.daytrader.RunStatsDataBean beanRet = new org.apache.geronimo.samples.daytrader.RunStatsDataBean();
+		beanRet.setTradeUserCount(bean.getTradeUserCount());
+		beanRet.setNewUserCount(bean.getNewUserCount());
+		beanRet.setSumLoginCount(bean.getSumLoginCount());
+		beanRet.setSumLogoutCount(bean.getSumLogoutCount());
+		beanRet.setHoldingCount(bean.getHoldingCount());
+		beanRet.setOrderCount(bean.getOrderCount());
+		beanRet.setBuyOrderCount(bean.getBuyOrderCount());
+		beanRet.setSellOrderCount(bean.getSellOrderCount());
+		beanRet.setCancelledOrderCount(bean.getCancelledOrderCount());
+		beanRet.setOpenOrderCount(bean.getOpenOrderCount());
+		beanRet.setDeletedOrderCount(bean.getDeletedOrderCount());
+		return beanRet;
+	}
+}

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/FinancialUtils.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/FinancialUtils.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/FinancialUtils.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/FinancialUtils.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,114 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.math.BigDecimal;
+import org.apache.geronimo.samples.daytrader.*;
+
+public class FinancialUtils {
+	//TODO -- FinancialUtils should have parts reimplemented as JSPTaglibs 
+
+	public final static int ROUND = BigDecimal.ROUND_HALF_UP;
+	public final static int SCALE = 2;	
+	public final static BigDecimal ZERO = (new BigDecimal(0.00)).setScale(SCALE);
+	public final static BigDecimal ONE = (new BigDecimal(1.00)).setScale(SCALE);
+	public final static BigDecimal HUNDRED = (new BigDecimal(100.00)).setScale(SCALE);
+
+	public static BigDecimal computeGain(BigDecimal currentBalance,
+											BigDecimal openBalance) 
+	{
+		return currentBalance.subtract(openBalance).setScale(SCALE);
+	}
+	
+	public static BigDecimal computeGainPercent(BigDecimal currentBalance,
+												BigDecimal openBalance) 
+	{
+		if (openBalance.doubleValue() == 0.0) return ZERO;
+		BigDecimal gainPercent =
+			currentBalance.divide(openBalance, ROUND).subtract(ONE).multiply(HUNDRED);
+		return gainPercent;
+	}
+
+	public static BigDecimal computeHoldingsTotal(Collection holdingDataBeans) {
+		BigDecimal holdingsTotal = new BigDecimal(0.0).setScale(SCALE);
+		if (holdingDataBeans == null)
+			return holdingsTotal;
+		Iterator it = holdingDataBeans.iterator();
+		while (it.hasNext()) {
+			HoldingDataBean holdingData = (HoldingDataBean) it.next();
+			BigDecimal total =
+				holdingData.getPurchasePrice().multiply(new BigDecimal(holdingData.getQuantity()));
+			holdingsTotal = holdingsTotal.add(total);
+		}
+		return holdingsTotal.setScale(SCALE);
+	}
+
+	public static String printGainHTML(BigDecimal gain) {
+		String htmlString, arrow;
+		if (gain.doubleValue() < 0.0) {
+			htmlString = "<FONT color=\"#ff0000\">";
+			arrow = "arrowdown.gif";
+		} else {
+			htmlString = "<FONT color=\"#009900\">";
+			arrow = "arrowup.gif";			
+		}
+
+		htmlString += gain.setScale(SCALE, ROUND) + "</FONT><IMG src=\"images/" + arrow + "\" width=\"10\" height=\"10\" border=\"0\"></IMG>";
+		return htmlString;
+	}
+
+	public static String printChangeHTML(double change) {
+		String htmlString, arrow;
+		if (change < 0.0) {
+			htmlString = "<FONT color=\"#ff0000\">";
+			arrow = "arrowdown.gif";						
+		} else {
+			htmlString = "<FONT color=\"#009900\">";
+			arrow = "arrowup.gif";						
+		}
+
+
+		htmlString += change + "</FONT><IMG src=\"images/" + arrow + "\" width=\"10\" height=\"10\" border=\"0\"></IMG>";
+		return htmlString;
+	}
+
+	public static String printGainPercentHTML(BigDecimal gain) {
+		String htmlString, arrow;
+		if (gain.doubleValue() < 0.0) {
+			htmlString = "(<B><FONT color=\"#ff0000\">";
+			arrow = "arrowdown.gif";									
+		} else {
+			htmlString = "(<B><FONT color=\"#009900\">+";
+			arrow = "arrowup.gif";									
+		}
+
+		htmlString += gain.setScale(SCALE, ROUND);
+		htmlString += "%</FONT></B>)<IMG src=\"images/" + arrow + "\" width=\"10\" height=\"10\" border=\"0\"></IMG>";
+		return htmlString;
+	}
+	
+    public static String printQuoteLink(String symbol)	
+    {
+    	String htmlString;
+    	return "<A href=\"app?action=quotes&symbols="+ symbol+"\">" + symbol + "</A>";
+    }
+	
+
+}
\ No newline at end of file

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/KeyBlock.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/KeyBlock.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/KeyBlock.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/KeyBlock.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,130 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.util;
+
+import java.util.AbstractSequentialList;
+import java.util.ListIterator;
+public class KeyBlock extends AbstractSequentialList 
+{
+
+	// min and max provide range of valid primary keys for this KeyBlock
+	private int min = 0;
+	private int max = 0;
+	private int index = 0;
+
+	/**
+	 * Constructor for KeyBlock
+	 */
+	public KeyBlock() {
+		super();
+		min = 0;
+		max = 0;
+		index = min;
+	}
+
+	/**
+	 * Constructor for KeyBlock 
+	 */
+	public KeyBlock(int min, int max) {
+		super();
+		this.min = min;
+		this.max = max;
+		index = min;
+	}
+
+	/**
+	 * @see AbstractCollection#size()
+	 */
+	public int size() {
+		return (max - min) + 1;
+	}
+
+	/**
+	 * @see AbstractSequentialList#listIterator(int)
+	 */
+	public ListIterator listIterator(int arg0) {
+		return new KeyBlockIterator();
+	}
+
+	class KeyBlockIterator implements ListIterator {
+
+		/**
+		 * @see ListIterator#hasNext()
+		 */
+		public boolean hasNext() {
+			return index <= max;
+		}
+
+		/**
+		 * @see ListIterator#next()
+		 */
+		public synchronized Object next() {
+			if (index > max)
+				throw new java.lang.RuntimeException("KeyBlock:next() -- Error KeyBlock depleted");
+			return new Integer(index++);
+		}
+
+		/**
+		 * @see ListIterator#hasPrevious()
+		 */
+		public boolean hasPrevious() {
+			return index > min;
+		}
+
+		/**
+		 * @see ListIterator#previous()
+		 */
+		public Object previous() {
+			return new Integer(--index);
+		}
+
+		/**
+		 * @see ListIterator#nextIndex()
+		 */
+		public int nextIndex() {
+			return index-min;
+		}
+
+		/**
+		 * @see ListIterator#previousIndex()
+		 */
+		public int previousIndex() {
+			throw new UnsupportedOperationException("KeyBlock: previousIndex() not supported");
+		}
+
+		/**
+		 * @see ListIterator#add()
+		 */
+		public void add(Object o) {
+			throw new UnsupportedOperationException("KeyBlock: add() not supported");
+		}
+
+		/**
+		 * @see ListIterator#remove()
+		 */
+		public void remove() {
+			throw new UnsupportedOperationException("KeyBlock: remove() not supported");
+		}
+
+		/**
+		 * @see ListIterator#set(Object)
+		 */
+		public void set(Object arg0) {
+		}
+	}
+}
\ No newline at end of file

Added: geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/Log.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/Log.java?rev=290479&view=auto
==============================================================================
--- geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/Log.java (added)
+++ geronimo/trunk/sandbox/daytrader/modules/ejb/src/java/org/apache/geronimo/samples/daytrader/util/Log.java Tue Sep 20 09:07:08 2005
@@ -0,0 +1,215 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation or its licensors, as applicable 
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.geronimo.samples.daytrader.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+import org.apache.geronimo.samples.daytrader.*;
+
+public class Log {
+	
+//A general purpose, high performance logging, tracing, statistic service
+    private static void logInternal(String message)
+    {
+    	System.out.println(message);
+    }
+    
+	public static void log(String message)
+	{
+		logInternal("TradeLog:" + new java.util.Date() + "------\n\t ");
+		logInternal(message);
+	}
+	public static void log(String msg1, String msg2)
+	{
+		log(msg1+msg2);
+	}
+	public static void log(String msg1, String msg2, String msg3)
+	{
+		log(msg1+msg2+msg3);
+	}
+	
+	public static void error(String message)
+	{
+		message = "Error: " + message;
+		log(message);
+	}
+	public static void error(String message, Throwable e)
+	{
+		error(message+"\n\t"+e.toString());
+		e.printStackTrace(System.out);
+	}
+	public static void error(String msg1, String msg2, Throwable e)
+	{
+		error(msg1+"\n"+msg2+"\n\t", e);
+	}
+	public static void error(String msg1, String msg2, String msg3, Throwable e)
+	{
+		error(msg1+"\n"+msg2+"\n"+msg3+"\n\t", e);
+	}
+	public static void error(Throwable e, String message)
+	{
+		error(message+"\n\t",e);
+		e.printStackTrace(System.out);
+	}
+	public static void error(Throwable e, String msg1, String msg2)
+	{
+		error(msg1+"\n"+msg2+"\n\t",e);
+	}
+	public static void error(Throwable e, String msg1, String msg2, String msg3)
+	{
+		error(msg1+"\n"+msg2+"\n"+msg3+"\n\t", e);
+	}
+	
+	
+	public static void trace(String message)
+	{
+		log(message + " threadID="+ Thread.currentThread());
+	}
+
+	public static void trace(String message, Object parm1)
+	{
+		trace(message+"("+parm1+")");
+	}
+
+	public static void trace(String message, Object parm1, Object parm2)
+	{
+		trace(message+"("+parm1+", "+parm2+")");
+	}
+
+	public static void trace(String message, Object parm1, Object parm2, Object parm3)
+	{
+		trace(message+"("+parm1+", "+parm2+", "+parm3+")");
+	}
+	public static void trace(String message, Object parm1, Object parm2, Object parm3, Object parm4)
+	{
+		trace(message+"("+parm1+", "+parm2+", "+parm3+")"+", "+parm4);
+	}
+	public static void trace(String message, Object parm1, Object parm2, Object parm3, Object parm4, Object parm5)
+	{
+		trace(message+"("+parm1+", "+parm2+", "+parm3+")"+", "+parm4+", "+parm5);
+	}
+	public static void trace(String message, Object parm1, Object parm2, Object parm3, Object parm4, 
+								Object parm5, Object parm6)
+	{
+		trace(message+"("+parm1+", "+parm2+", "+parm3+")"+", "+parm4+", "+parm5+", "+parm6);
+	}
+	public static void trace(String message, Object parm1, Object parm2, Object parm3, Object parm4, 
+	          					Object parm5, Object parm6, Object parm7)
+	{
+		trace(message+"("+parm1+", "+parm2+", "+parm3+")"+", "+parm4+", "+parm5+", "+parm6+", "+parm7);
+	}
+	public static void traceEnter(String message)
+	{
+		log("Method enter --" + message);
+	}
+	public static void traceExit(String message)
+	{
+		log("Method exit  --" + message);
+	}
+	
+	
+	public static void stat(String message)
+	{
+		log(message);
+	}
+
+	public static void debug(String message)
+	{
+		log(message);
+	}
+
+	public static void print(String message)
+	{
+		logInternal(message);
+	}
+	
+	public static void printObject(Object o)
+	{
+		log("\t"+o.toString());
+	}
+		
+	public static void printCollection(Collection c)
+	{
+		log("\t---Log.printCollection -- collection size=" + c.size());
+		Iterator it = c.iterator();
+		while ( it.hasNext() )
+		{
+			logInternal("\t\t"+it.next().toString());
+		}
+		log("\t---Log.printCollection -- complete");		
+	}
+	
+	public static void printCollection(String message, Collection c)
+	{
+		log(message);
+		printCollection(c);
+	}
+	
+	public static boolean doActionTrace()
+	{
+		return getTrace() || getActionTrace();
+	}
+
+	public static boolean doTrace()
+	{
+		return getTrace();
+	}
+	
+	public static boolean doDebug()
+	{
+		return true;
+	}
+	
+	public static boolean doStat()
+	{
+		return true;
+	}		
+	
+	/**
+	 * Gets the trace
+	 * @return Returns a boolean
+	 */
+	public static boolean getTrace() {
+		return TradeConfig.getTrace();
+	}
+	/**
+	 * Gets the trace value for Trade actions only
+	 * @return Returns a boolean
+	 */
+	public static boolean getActionTrace() {
+		return TradeConfig.getActionTrace();
+	}
+		
+	/**
+	 * Sets the trace
+	 * @param trace The trace to set
+	 */
+	public static void setTrace(boolean traceValue)
+	{
+		TradeConfig.setTrace(traceValue);
+	}
+	/**
+	 * Sets the trace value for Trade actions only
+	 * @param trace The trace to set
+	 */
+	public static void setActionTrace(boolean traceValue)
+	{
+		TradeConfig.setActionTrace(traceValue);
+	}
+}
+



Mime
View raw message