001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.samples.daytrader;
018    
019    import java.math.BigDecimal;
020    import java.rmi.RemoteException;
021    import java.util.Collection;
022    
023    import javax.naming.InitialContext;
024    
025    import org.apache.geronimo.samples.daytrader.direct.TradeDirect;
026    import org.apache.geronimo.samples.daytrader.ejb.TradeHome;
027    import org.apache.geronimo.samples.daytrader.session.TradeJDBCHome;
028    import org.apache.geronimo.samples.daytrader.util.FinancialUtils;
029    import org.apache.geronimo.samples.daytrader.util.Log;
030    
031    /**
032     * The TradeAction class provides the generic client side access to each of the
033     * Trade brokerage user operations. These include login, logout, buy, sell,
034     * getQuote, etc. The TradeAction class does not handle user interface
035     * processing and should be used by a class that is UI specific. For example,
036     * {trade_client.TradeServletAction}manages a web interface to Trade,
037     * making calls to TradeAction methods to actually performance each operation.
038     */
039    public class TradeAction implements TradeServices {
040        private TradeServices trade = null;
041        private static TradeHome tradeHome = null;
042        private static TradeJDBCHome tradeJDBCHome = null;
043        private static TradeHome tradeHomeJPA = null;
044    
045    
046        public TradeAction() {
047            if (Log.doTrace())
048                Log.trace("TradeAction:TradeAction()");
049            createTrade();
050        }
051    
052        public TradeAction(TradeServices trade) {
053            if (Log.doActionTrace())
054                Log.trace("TradeAction:TradeAction(trade)");
055            this.trade = trade;
056        }
057    
058        private void createTrade() {
059            if (TradeConfig.runTimeMode == TradeConfig.EJB) {
060                try {
061                    if (tradeHome == null) {
062                        InitialContext ic = new InitialContext();
063                        try {
064                            tradeHome = (TradeHome) (javax.rmi.PortableRemoteObject.narrow(ic.lookup("java:comp/env/ejb/Trade"), TradeHome.class));
065                        }
066                        catch (Exception e) {
067                            Log.log("TradeAction:createTrade lookup of java:comp/env/ejb/Trade failed. Reverting to JNDI lookup of Trade");
068                            tradeHome = (TradeHome) (javax.rmi.PortableRemoteObject.narrow(ic.lookup("Trade"), TradeHome.class));
069                        }
070                    }
071                    trade = tradeHome.create();
072                }
073                catch (Exception e) {
074                    Log.error("TradeAction:TradeAction() Creation of Trade EJB failed\n" + e);
075                    e.printStackTrace();
076                }
077            } else        if (TradeConfig.runTimeMode == TradeConfig.JPA) {
078                try {
079                    if (tradeHomeJPA == null) {
080                        InitialContext ic = new InitialContext();
081                        try {
082                            tradeHomeJPA = (TradeHome) (javax.rmi.PortableRemoteObject.narrow(ic.lookup("java:comp/env/ejb/TradeJPA"), TradeHome.class));
083                        }
084                        catch (Exception e) {
085                            Log.log("TradeAction:createTrade lookup of java:comp/env/ejb/TradeJPA failed. Reverting to JNDI lookup of Trade");
086                            tradeHomeJPA = (TradeHome) (javax.rmi.PortableRemoteObject.narrow(ic.lookup("TradeJPA"), TradeHome.class));
087                        }
088                    }
089                    trade = tradeHomeJPA.create();
090                }
091                catch (Exception e) {
092                    Log.error("TradeAction:TradeAction() Creation of Trade JPA failed\n" + e);
093                    e.printStackTrace();
094                }
095            } else if (TradeConfig.runTimeMode == TradeConfig.DIRECT) {
096                try {
097                    trade = new TradeDirect();
098                }
099                catch (Exception e) {
100                    Log.error("TradeAction:TradeAction() Creation of Trade Direct failed\n" + e);
101                }
102            } else if (TradeConfig.runTimeMode == TradeConfig.SESSION) {
103                try {
104                    if (tradeJDBCHome == null) {
105                        InitialContext ic = new InitialContext();
106                        try {
107                            tradeJDBCHome = (TradeJDBCHome) (javax.rmi.PortableRemoteObject.narrow(ic.lookup("java:comp/env/ejb/TradeJDBC"), TradeJDBCHome.class));
108                        }
109                        catch (Exception e) {
110                            Log.log("TradeAction:createTrade lookup of java:comp/env/ejb/TradeJDBC failed. Reverting to JNDI lookup of Trade");
111                            tradeJDBCHome = (TradeJDBCHome) (javax.rmi.PortableRemoteObject.narrow(ic.lookup("TradeJDBC"), TradeJDBCHome.class));
112                        }
113                    }
114                    trade = tradeJDBCHome.create();
115                }
116                catch (Exception e) {
117                    Log.error("TradeAction:TradeAction() Creation of Trade JDBC failed\n" + e);
118                    e.printStackTrace();
119                }
120            }
121        }
122    
123        /**
124         * Compute and return a snapshot of the current market conditions This
125         * includes the TSIA - an index of the price of the top 100 Trade stock
126         * quotes The openTSIA ( the index at the open) The volume of shares traded,
127         * Top Stocks gain and loss
128         *
129         * @return A snapshot of the current market summary
130         */
131        public MarketSummaryDataBean getMarketSummary() throws Exception {
132            if (Log.doActionTrace()) {
133                Log.trace("TradeAction:getMarketSummary()");
134            }
135            MarketSummaryDataBean marketSummaryData = null;
136            marketSummaryData = trade.getMarketSummary();
137            return marketSummaryData;
138        }
139    
140        /**
141         * Purchase a stock and create a new holding for the given user. Given a
142         * stock symbol and quantity to purchase, retrieve the current quote price,
143         * debit the user's account balance, and add holdings to user's portfolio.
144         *
145         * @param userID   the customer requesting the stock purchase
146         * @param symbol   the symbol of the stock being purchased
147         * @param quantity the quantity of shares to purchase
148         * @return OrderDataBean providing the status of the newly created buy order
149         */
150        public OrderDataBean buy(String userID, String symbol, double quantity, int orderProcessingMode) throws Exception {
151            if (Log.doActionTrace())
152                Log.trace("TradeAction:buy", userID, symbol, new Double(quantity), new Integer(orderProcessingMode));
153            OrderDataBean orderData;
154            orderData = trade.buy(userID, symbol, quantity, orderProcessingMode);
155            //after the purchase or sell of a stock, update the stocks volume and
156            // price
157            updateQuotePriceVolume(symbol, TradeConfig.getRandomPriceChangeFactor(), quantity);
158            return orderData;
159        }
160    
161        /**
162         * Sell(SOAP 2.2 Wrapper converting int to Integer) a stock holding and
163         * removed the holding for the given user. Given a Holding, retrieve current
164         * quote, credit user's account, and reduce holdings in user's portfolio.
165         *
166         * @param userID    the customer requesting the sell
167         * @param holdingID the users holding to be sold
168         * @return OrderDataBean providing the status of the newly created sell
169         *         order
170         */
171        public OrderDataBean sell(String userID, int holdingID, int orderProcessingMode) throws Exception {
172            return sell(userID, new Integer(holdingID), orderProcessingMode);
173        }
174    
175        /**
176         * Sell a stock holding and removed the holding for the given user. Given a
177         * Holding, retrieve current quote, credit user's account, and reduce
178         * holdings in user's portfolio.
179         *
180         * @param userID    the customer requesting the sell
181         * @param holdingID the users holding to be sold
182         * @return OrderDataBean providing the status of the newly created sell
183         *         order
184         */
185        public OrderDataBean sell(String userID, Integer holdingID, int orderProcessingMode) throws Exception {
186            if (Log.doActionTrace())
187                Log.trace("TradeAction:sell", userID, holdingID, new Integer(orderProcessingMode));
188            OrderDataBean orderData;
189            orderData = trade.sell(userID, holdingID, orderProcessingMode);
190            if (!(orderData.getOrderStatus().equalsIgnoreCase("cancelled")))
191                //after the purchase or sell of a stock, update the stocks volume
192                // and price
193                updateQuotePriceVolume(orderData.getSymbol(), TradeConfig.getRandomPriceChangeFactor(), orderData.getQuantity());
194            return orderData;
195        }
196    
197        /**
198         * Queue the Order identified by orderID to be processed
199         * <p/>
200         * Orders are submitted through JMS to a Trading Broker and completed
201         * asynchronously. This method queues the order for processing
202         * <p/>
203         * The boolean twoPhase specifies to the server implementation whether or
204         * not the method is to participate in a global transaction
205         *
206         * @param orderID the Order being queued for processing
207         */
208        public void queueOrder(Integer orderID, boolean twoPhase) {
209            throw new UnsupportedOperationException("TradeAction: queueOrder method not supported");
210        }
211    
212        /**
213         * Complete the Order identefied by orderID Orders are submitted through JMS
214         * to a Trading agent and completed asynchronously. This method completes
215         * the order For a buy, the stock is purchased creating a holding and the
216         * users account is debited For a sell, the stock holding is removed and the
217         * users account is credited with the proceeds
218         * <p/>
219         * The boolean twoPhase specifies to the server implementation whether or
220         * not the method is to participate in a global transaction
221         *
222         * @param orderID the Order to complete
223         * @return OrderDataBean providing the status of the completed order
224         */
225        public OrderDataBean completeOrder(Integer orderID, boolean twoPhase) {
226            throw new UnsupportedOperationException("TradeAction: completeOrder method not supported");
227        }
228    
229        /**
230         * Cancel the Order identified by orderID
231         * <p/>
232         * Orders are submitted through JMS to a Trading Broker and completed
233         * asynchronously. This method queues the order for processing
234         * <p/>
235         * The boolean twoPhase specifies to the server implementation whether or
236         * not the method is to participate in a global transaction
237         *
238         * @param orderID the Order being queued for processing
239         */
240        public void cancelOrder(Integer orderID, boolean twoPhase) {
241            throw new UnsupportedOperationException("TradeAction: cancelOrder method not supported");
242        }
243    
244        public void orderCompleted(String userID, Integer orderID) throws Exception {
245            if (Log.doActionTrace())
246                Log.trace("TradeAction:orderCompleted", userID, orderID);
247            if (Log.doTrace())
248                Log.trace("OrderCompleted", userID, orderID);
249        }
250    
251        /**
252         * Get the collection of all orders for a given account
253         *
254         * @param userID the customer account to retrieve orders for
255         * @return Collection OrderDataBeans providing detailed order information
256         */
257        public Collection getOrders(String userID) throws Exception {
258            if (Log.doActionTrace())
259                Log.trace("TradeAction:getOrders", userID);
260            Collection orderDataBeans;
261            orderDataBeans = trade.getOrders(userID);
262            return orderDataBeans;
263        }
264    
265        /**
266         * Get the collection of completed orders for a given account that need to
267         * be alerted to the user
268         *
269         * @param userID the customer account to retrieve orders for
270         * @return Collection OrderDataBeans providing detailed order information
271         */
272        public Collection getClosedOrders(String userID) throws Exception {
273            if (Log.doActionTrace())
274                Log.trace("TradeAction:getClosedOrders", userID);
275            Collection orderDataBeans;
276            orderDataBeans = trade.getClosedOrders(userID);
277            return orderDataBeans;
278        }
279    
280        /**
281         * Given a market symbol, price, and details, create and return a new
282         * {@link QuoteDataBean}
283         *
284         * @param symbol  the symbol of the stock
285         * @param price   the current stock price
286         * @return a new QuoteDataBean or null if Quote could not be created
287         */
288        public QuoteDataBean createQuote(String symbol, String companyName, BigDecimal price) throws Exception {
289            if (Log.doActionTrace())
290                Log.trace("TradeAction:createQuote", symbol, companyName, price);
291            QuoteDataBean quoteData;
292            quoteData = trade.createQuote(symbol, companyName, price);
293            return quoteData;
294        }
295    
296        /**
297         * Return a collection of {@link QuoteDataBean}describing all current
298         * quotes
299         *
300         * @return the collection of QuoteDataBean
301         */
302        public Collection getAllQuotes() throws Exception {
303            if (Log.doActionTrace()) {
304                Log.trace("TradeAction:getAllQuotes");
305            }
306            Collection quotes;
307            quotes = trade.getAllQuotes();
308            return quotes;
309        }
310    
311        /**
312         * Return a {@link QuoteDataBean}describing a current quote for the given
313         * stock symbol
314         *
315         * @param symbol the stock symbol to retrieve the current Quote
316         * @return the QuoteDataBean
317         */
318        public QuoteDataBean getQuote(String symbol) throws Exception {
319            if (Log.doActionTrace())
320                Log.trace("TradeAction:getQuote", symbol);
321            if ((symbol == null) || (symbol.length() == 0) || (symbol.length() > 10)) {
322                if (Log.doActionTrace()) {
323                    Log.trace("TradeAction:getQuote   ---  primitive workload");
324                }
325                return new QuoteDataBean("Invalid symbol", "", 0.0, FinancialUtils.ZERO, FinancialUtils.ZERO, FinancialUtils.ZERO, FinancialUtils.ZERO, 0.0);
326            }
327            QuoteDataBean quoteData;
328            quoteData = trade.getQuote(symbol);
329            return quoteData;
330        }
331    
332        /**
333         * Update the stock quote price for the specified stock symbol
334         *
335         * @param symbol for stock quote to update
336         * @return the QuoteDataBean describing the stock
337         */
338        /* avoid data collision with synch */
339        public QuoteDataBean updateQuotePriceVolume(String symbol, BigDecimal changeFactor, double sharesTraded) throws Exception {
340            if (Log.doActionTrace())
341                Log.trace("TradeAction:updateQuotePriceVolume", symbol, changeFactor, new Double(sharesTraded));
342            QuoteDataBean quoteData = null;
343            try {
344                quoteData = trade.updateQuotePriceVolume(symbol, changeFactor, sharesTraded);
345            }
346            catch (Exception e) {
347                Log.error("TradeAction:updateQuotePrice -- ", e);
348            }
349            return quoteData;
350        }
351    
352        /**
353         * Return the portfolio of stock holdings for the specified customer as a
354         * collection of HoldingDataBeans
355         *
356         * @param userID the customer requesting the portfolio
357         * @return Collection of the users portfolio of stock holdings
358         */
359        public Collection getHoldings(String userID) throws Exception {
360            if (Log.doActionTrace())
361                Log.trace("TradeAction:getHoldings", userID);
362            Collection holdingDataBeans;
363            holdingDataBeans = trade.getHoldings(userID);
364            return holdingDataBeans;
365        }
366    
367        /**
368         * Return a specific user stock holding identifed by the holdingID
369         *
370         * @param holdingID the holdingID to return
371         * @return a HoldingDataBean describing the holding
372         */
373        public HoldingDataBean getHolding(Integer holdingID) throws Exception {
374            if (Log.doActionTrace())
375                Log.trace("TradeAction:getHolding", holdingID);
376            HoldingDataBean holdingData;
377            holdingData = trade.getHolding(holdingID);
378            return holdingData;
379        }
380    
381        /**
382         * Return an AccountDataBean object for userID describing the account
383         *
384         * @param userID the account userID to lookup
385         * @return User account data in AccountDataBean
386         */
387        public AccountDataBean getAccountData(String userID) throws javax.ejb.FinderException, RemoteException {
388            if (Log.doActionTrace())
389                Log.trace("TradeAction:getAccountData", userID);
390            AccountDataBean accountData;
391            accountData = trade.getAccountData(userID);
392            return accountData;
393        }
394    
395        /**
396         * Return an AccountProfileDataBean for userID providing the users profile
397         *
398         * @param userID the account userID to lookup
399         */
400        public AccountProfileDataBean getAccountProfileData(String userID) throws Exception {
401            if (Log.doActionTrace())
402                Log.trace("TradeAction:getAccountProfileData", userID);
403            AccountProfileDataBean accountProfileData;
404            accountProfileData = trade.getAccountProfileData(userID);
405            return accountProfileData;
406        }
407    
408        /**
409         * Update userID's account profile information using the provided
410         * AccountProfileDataBean object
411         *
412         * @param accountProfileData   account profile data in AccountProfileDataBean
413         */
414        public AccountProfileDataBean updateAccountProfile(AccountProfileDataBean accountProfileData) throws Exception {
415            if (Log.doActionTrace())
416                Log.trace("TradeAction:updateAccountProfile", accountProfileData);
417            accountProfileData = trade.updateAccountProfile(accountProfileData);
418            return accountProfileData;
419        }
420    
421        /**
422         * Attempt to authenticate and login a user with the given password
423         *
424         * @param userID   the customer to login
425         * @param password the password entered by the customer for authentication
426         * @return User account data in AccountDataBean
427         */
428        public AccountDataBean login(String userID, String password) throws Exception {
429            if (Log.doActionTrace())
430                Log.trace("TradeAction:login", userID, password);
431            AccountDataBean accountData;
432            accountData = trade.login(userID, password);
433            return accountData;
434        }
435    
436        /**
437         * Logout the given user
438         *
439         * @param userID the customer to logout
440         */
441        public void logout(String userID) throws Exception {
442            if (Log.doActionTrace())
443                Log.trace("TradeAction:logout", userID);
444            trade.logout(userID);
445        }
446    
447        /**
448         * Register a new Trade customer. Create a new user profile, user registry
449         * entry, account with initial balance, and empty portfolio.
450         *
451         * @param userID         the new customer to register
452         * @param password       the customers password
453         * @param fullname       the customers fullname
454         * @param address        the customers street address
455         * @param email          the customers email address
456         * @param creditCard     the customers creditcard number
457         * @param openBalance the amount to charge to the customers credit to open the
458         *                       account and set the initial balance
459         * @return the userID if successful, null otherwise
460         */
461        public AccountDataBean register(String userID, String password, String fullname, String address, String email, String creditCard, BigDecimal openBalance) throws Exception {
462            if (Log.doActionTrace())
463                Log.trace("TradeAction:register", userID, password, fullname, address, email, creditCard, openBalance);
464            AccountDataBean accountData;
465            accountData = trade.register(userID, password, fullname, address, email, creditCard, openBalance);
466            return accountData;
467        }
468    
469        public AccountDataBean register(String userID, String password, String fullname, String address, String email, String creditCard, String openBalanceString) throws Exception {
470            BigDecimal openBalance = new BigDecimal(openBalanceString);
471            return register(userID, password, fullname, address, email, creditCard, openBalance);
472        }
473    
474        /**
475         * Reset the TradeData by - removing all newly registered users by scenario
476         * servlet (i.e. users with userID's beginning with "ru:") * - removing all
477         * buy/sell order pairs - setting logoutCount = loginCount
478         *
479         * return statistics for this benchmark run
480         */
481        public RunStatsDataBean resetTrade(boolean deleteAll) throws Exception {
482            RunStatsDataBean runStatsData;
483            runStatsData = trade.resetTrade(deleteAll);
484                    return runStatsData;
485            }
486    }