001: /*
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: ManagerSF.java 8378 2006-05-22 14:48:47Z durieuxp $
023: * --------------------------------------------------------------------------
024: */
025:
026: package org.objectweb.jonas.stests.manac;
027:
028: import java.rmi.RemoteException;
029: import java.sql.Connection;
030: import java.sql.SQLException;
031: import java.sql.Statement;
032: import java.util.Enumeration;
033: import javax.ejb.CreateException;
034: import javax.ejb.EJBException;
035: import javax.ejb.FinderException;
036: import javax.ejb.RemoveException;
037: import javax.ejb.SessionBean;
038: import javax.ejb.SessionContext;
039: import javax.ejb.SessionSynchronization;
040: import javax.ejb.TransactionRolledbackLocalException;
041: import javax.naming.Context;
042: import javax.naming.InitialContext;
043: import javax.naming.NamingException;
044: import javax.sql.DataSource;
045:
046: import org.objectweb.jonas.common.Log;
047: import org.objectweb.util.monolog.api.Logger;
048: import org.objectweb.util.monolog.api.BasicLevel;
049:
050: /**
051: * Manager Implementation
052: * @author Philippe Durieux
053: */
054: public class ManagerSF implements SessionBean, SessionSynchronization {
055:
056: protected static Logger logger = null;
057: protected static Logger history = null;
058: SessionContext ejbContext;
059: ManacLocalHome manacLocalHome = null;
060: int delay = 0;
061: int c1 = 0;
062: int c2 = 0;
063: int d1 = 0;
064: int d2 = 0;
065: ManacLocal cred1 = null;
066: ManacLocal cred2 = null;
067: ManacLocal deb1 = null;
068: ManacLocal deb2 = null;
069: int initialValue = 1000;
070: int value = 10;
071:
072: private void log(String message) {
073: history.log(BasicLevel.INFO, "a_" + d1 + "\ta_" + d2 + "\ta_"
074: + c1 + "\ta_" + c2 + "\tv_" + value + "\t" + message);
075: }
076:
077: // ------------------------------------------------------------------
078: // init DataBase for Manac beans
079: // ------------------------------------------------------------------
080: private void initDB() {
081: logger.log(BasicLevel.DEBUG, "");
082:
083: // Get my DataSource from JNDI
084: DataSource ds = null;
085: InitialContext ictx = null;
086: String myTable = null;
087: try {
088: ictx = new InitialContext();
089: } catch (Exception e) {
090: logger.log(BasicLevel.ERROR, "new InitialContext() : " + e);
091: throw new EJBException("Cannot get JNDI InitialContext");
092: }
093: try {
094: ds = (DataSource) ictx.lookup("java:comp/env/jdbc/mydb");
095: } catch (Exception e) {
096: logger.log(BasicLevel.ERROR, "cannot lookup datasource "
097: + e);
098: throw new EJBException("cannot lookup datasource");
099: }
100: try {
101: // Create table if specified in dd (in fact, only if CMP1.x)
102: myTable = (String) ictx.lookup("java:comp/env/tablename");
103: createTable(ds, myTable);
104: } catch (Exception e) {
105: logger.log(BasicLevel.WARN, "No init necessary in CMP2");
106: }
107: }
108:
109: private void createTable(DataSource ds, String myTable) {
110: // Drop table
111: Connection conn = null;
112: Statement stmt = null;
113: try {
114: conn = ds.getConnection();
115: stmt = conn.createStatement();
116: stmt.execute("drop table " + myTable);
117: stmt.close();
118: } catch (SQLException e) {
119: // The first time, table will not exist.
120: logger
121: .log(BasicLevel.INFO, "Exception in dropTable : "
122: + e);
123: }
124:
125: // Create table.
126: try {
127: stmt = conn.createStatement();
128: stmt
129: .execute("create table "
130: + myTable
131: + "(name varchar(30) not null primary key, num integer, balance integer)");
132: stmt.close();
133: conn.close();
134: } catch (SQLException e) {
135: logger.log(BasicLevel.ERROR, "Exception in createTable : "
136: + e);
137: throw new EJBException("Exception in createTable");
138: }
139: }
140:
141: // ------------------------------------------------------------------
142: // SessionBean implementation
143: // ------------------------------------------------------------------
144:
145: /**
146: * Set the associated session context. The container calls this method
147: * after the instance creation.
148: * The enterprise Bean instance should store the reference to the context
149: * object in an instance variable.
150: * This method is called with no transaction context.
151: *
152: * @param ctx A SessionContext interface for the instance.
153: * @throws EJBException Thrown by the method to indicate a failure caused by
154: * a system-level error.
155: */
156: public void setSessionContext(SessionContext ctx) {
157: if (logger == null) {
158: logger = Log.getLogger(Log.JONAS_TESTS_PREFIX);
159: }
160: if (history == null) {
161: history = Log
162: .getLogger("org.objectweb.jonas_tests.history");
163: }
164: logger.log(BasicLevel.DEBUG, "");
165: ejbContext = ctx;
166: }
167:
168: /**
169: * A container invokes this method before it ends the life of the session object.
170: * This happens as a result of a client's invoking a remove operation, or when a
171: * container decides to terminate the session object after a timeout.
172: * This method is called with no transaction context.
173: *
174: * @throws EJBException Thrown by the method to indicate a failure caused by
175: * a system-level error.
176: */
177: public void ejbRemove() {
178: logger.log(BasicLevel.DEBUG, "");
179: }
180:
181: /**
182: * Create a session.
183: * @param ival initial balance value for new accounts.
184: * @throws CreateException Failure to create a session EJB object.
185: */
186: public void ejbCreate(int ival) throws CreateException {
187: logger.log(BasicLevel.DEBUG, "");
188:
189: // lookup ManacLocalHome
190: try {
191: Context ictx = new InitialContext();
192: manacLocalHome = (ManacLocalHome) ictx
193: .lookup("java:comp/env/ejb/manac");
194: } catch (NamingException e) {
195: logger.log(BasicLevel.ERROR, "Cannot get ManacLocalHome:"
196: + e);
197: throw new CreateException("Cannot get ManacLocalHome");
198: }
199:
200: initialValue = ival;
201: }
202:
203: /**
204: * A container invokes this method on an instance before the instance
205: * becomes disassociated with a specific EJB object.
206: */
207: public void ejbPassivate() {
208: logger.log(BasicLevel.DEBUG, "");
209: }
210:
211: /**
212: * A container invokes this method when the instance is taken out of
213: * the pool of available instances to become associated with a specific
214: * EJB object.
215: */
216: public void ejbActivate() {
217: logger.log(BasicLevel.DEBUG, "");
218: }
219:
220: // ------------------------------------------------------------------
221: // SessionSynchronization implementation
222: // ------------------------------------------------------------------
223:
224: public void afterBegin() {
225: log("TX after_begin");
226: }
227:
228: public void beforeCompletion() {
229: log("TX before_completion");
230: }
231:
232: public void afterCompletion(boolean committed) {
233: if (committed) {
234: log("TX committed");
235: } else {
236: log("TX rolled back");
237: }
238: }
239:
240: // ------------------------------------------------------------------
241: // Manager implementation
242: // ------------------------------------------------------------------
243:
244: /**
245: * Create a new Account. The account may exist (for example, when running
246: * tests twice without restarting the Container, or if created by another
247: * session meanwhile)
248: * @param i account number (its PK)
249: */
250: private ManacLocal newAccount(int i) throws RemoteException {
251: ManacLocal ml = null;
252: try {
253: ml = manacLocalHome.create(i, initialValue);
254: logger.log(BasicLevel.INFO, "New Account created : " + i);
255: history.log(BasicLevel.INFO, "New Account\t" + i);
256: } catch (CreateException e) {
257: // Sometimes another one has just created it
258: try {
259: ml = manacLocalHome.findByNum(i);
260: logger.log(BasicLevel.INFO,
261: "Account was created already : " + i);
262: } catch (FinderException e2) {
263: logger.log(BasicLevel.ERROR, "newAccount:" + e);
264: history.log(BasicLevel.INFO, "CANNOT CREATE ACCOUNT\t"
265: + i);
266: throw new RemoteException("Cannot create Manac", e);
267: }
268: }
269: return ml;
270: }
271:
272: /**
273: * Initializes the database by creating a set of accounts.
274: * @param nb number of account created.
275: */
276: public void createAll(int nb) throws RemoteException {
277: logger.log(BasicLevel.DEBUG, "");
278:
279: // init database for Manac bean
280: initDB();
281:
282: // Check if accounts are already created.
283: try {
284: manacLocalHome.findByNum(nb - 1);
285: } catch (Exception e) {
286: // create accounts
287: for (int i = 0; i < nb; i++) {
288: newAccount(i);
289: }
290: }
291: }
292:
293: /**
294: * Reinit the balances for each created account to its initial value.
295: */
296: public void reinitAll() throws RemoteException {
297: logger.log(BasicLevel.DEBUG, "");
298: try {
299: Enumeration enumer = manacLocalHome.findAll();
300: while (enumer.hasMoreElements()) {
301: ManacLocal a = (ManacLocal) enumer.nextElement();
302: a.setBalance(initialValue);
303: }
304: } catch (Exception e) {
305: logger.log(BasicLevel.ERROR, "reinitAll:" + e);
306: }
307: }
308:
309: /**
310: * Set a delay value, in seconds, for the transaction.
311: */
312: public void setDelay(int d) throws RemoteException {
313: logger.log(BasicLevel.DEBUG, "delay (sec) = " + d);
314: delay = d;
315: }
316:
317: /**
318: * Each operation debits 2 accounts and credits 2 accounts.
319: * Lookup all accounts and create them if they not exist yet
320: * @param d1 first account to credit
321: * @param d2 second account to credit
322: * @param c1 first account to debit
323: * @param c2 second account to debit
324: * @throws RemoteException
325: */
326: public void setAccounts(int d1, int d2, int c1, int c2)
327: throws RemoteException {
328: logger.log(BasicLevel.DEBUG, "d1=" + d1 + " d2=" + d2 + " c1="
329: + c1 + " c2=" + c2);
330: log("SA beg");
331:
332: // debit account d1
333: this .d1 = d1;
334: try {
335: deb1 = manacLocalHome.findByNum(d1);
336: } catch (FinderException e) {
337: deb1 = newAccount(d1);
338: }
339:
340: // debit account d2
341: this .d2 = d2;
342: try {
343: deb2 = manacLocalHome.findByNum(d2);
344: } catch (FinderException e) {
345: deb2 = newAccount(d2);
346: }
347:
348: // credit account c1
349: this .c1 = c1;
350: try {
351: cred1 = manacLocalHome.findByNum(c1);
352: } catch (FinderException e) {
353: cred1 = newAccount(c1);
354: }
355:
356: // credit account c2
357: this .c2 = c2;
358: try {
359: cred2 = manacLocalHome.findByNum(c2);
360: } catch (FinderException e) {
361: cred2 = newAccount(c2);
362: }
363: log("SA end");
364: }
365:
366: /**
367: * Find an account and create it if not found.
368: * @param c1 account number
369: */
370: public void getAccount(int c1) throws RemoteException {
371: logger.log(BasicLevel.DEBUG, " c1=" + c1);
372: this .c1 = c1;
373: try {
374: cred1 = manacLocalHome.findByNum(c1);
375: } catch (FinderException e) {
376: cred1 = newAccount(c1);
377: }
378: }
379:
380: /**
381: * Find an account and create it if not found.
382: * Then, remove it.
383: * @param d1 account number
384: */
385: public void delAccount(int d1) throws RemoteException,
386: RemoveException {
387: logger.log(BasicLevel.DEBUG, " d1=" + d1);
388: this .d1 = d1;
389: try {
390: deb1 = manacLocalHome.findByNum(d1);
391: } catch (FinderException e) {
392: deb1 = newAccount(d1);
393: }
394: deb1.remove();
395: history.log(BasicLevel.INFO, d1 + "\tREMOVED");
396: }
397:
398: /**
399: * Set the value to withdraw.
400: * @param v value to withdraw.
401: */
402: public void setValue(int v) throws RemoteException {
403: logger.log(BasicLevel.DEBUG, "");
404: this .value = v;
405: }
406:
407: /**
408: * Do the movement.
409: */
410: public void movement() throws RemoteException {
411: logger.log(BasicLevel.DEBUG, "move " + value + " from " + d1
412: + "," + d2 + " to " + c1 + "," + c2);
413: log("MV start");
414:
415: // credit accounts first because we don't want a rollback if
416: // same account is debited and credited in the same operation.
417: try {
418: cred1.credit(value);
419: cred2.credit(value);
420: } catch (TransactionRolledbackLocalException e) {
421: logger.log(BasicLevel.WARN,
422: "Credit -> Rollback transaction");
423: log("MV rbc1");
424: return;
425: } catch (EJBException e) {
426: logger.log(BasicLevel.ERROR, "Cannot credit account:" + e);
427: log("MV rbc2");
428: return;
429: }
430:
431: // wait a little if delay
432: if (delay > 0) {
433: sleep(delay);
434: }
435:
436: // debit accounts
437: try {
438: deb1.debit(value);
439: deb2.debit(value);
440: } catch (TransactionRolledbackLocalException e) {
441: logger
442: .log(BasicLevel.WARN,
443: "Debit -> Rollback transaction");
444: log("MV rbd1");
445: return;
446: } catch (EJBException e) {
447: logger.log(BasicLevel.ERROR, "debit:" + e);
448: log("MV rbd2");
449: return;
450: }
451:
452: log("MV commit");
453: }
454:
455: /**
456: * Simple withdraw operation, on existing accounts only.
457: */
458: public void withdraw(int wd, int wc, int wv) throws RemoteException {
459: logger.log(BasicLevel.DEBUG, "move " + wv + " from " + wd
460: + " to " + wc);
461: d1 = wd;
462: c1 = wc;
463: log("W move start: ");
464:
465: // credit accounts first because we don't want a rollback if
466: // same account is debited and credited in the same operation.
467: // credit account wc
468: try {
469: manacLocalHome.findByNum(wc).credit(wv);
470: } catch (FinderException e) {
471: logger.log(BasicLevel.WARN, "Credit -> Unknown account");
472: log("W cred0");
473: return;
474: } catch (TransactionRolledbackLocalException e) {
475: logger.log(BasicLevel.WARN,
476: "Credit -> Rollback transaction");
477: log("W cred1");
478: return;
479: } catch (EJBException e) {
480: logger.log(BasicLevel.ERROR, "Cannot credit account:" + e);
481: log("W cred2");
482: return;
483: }
484: // debit account
485: try {
486: manacLocalHome.findByNum(wd).debit(wv);
487: } catch (FinderException e) {
488: logger.log(BasicLevel.WARN, "Debit -> Unknown account");
489: log("W ded0");
490: return;
491: } catch (TransactionRolledbackLocalException e) {
492: logger
493: .log(BasicLevel.WARN,
494: "Debit -> Rollback transaction");
495: log("W deb1");
496: return;
497: } catch (EJBException e) {
498: logger.log(BasicLevel.ERROR, "Cannot debit account:" + e);
499: log("W deb2");
500: return;
501: }
502: log("W move end: ");
503: }
504:
505: public void sleep(int seconds) {
506: try {
507: Thread.sleep(1000 * seconds);
508: } catch (InterruptedException e) {
509: }
510: }
511:
512: /**
513: * This is a read operation, executed outside transaction.
514: */
515: public int readBalances() throws RemoteException {
516: logger.log(BasicLevel.DEBUG, "");
517: int ret = 1000000;
518: int bal;
519: try {
520: bal = deb1.getBalance();
521: if (bal < ret) {
522: ret = bal;
523: }
524: bal = deb2.getBalance();
525: if (bal < ret) {
526: ret = bal;
527: }
528: bal = cred1.getBalance();
529: if (bal < ret) {
530: ret = bal;
531: }
532: bal = cred2.getBalance();
533: if (bal < ret) {
534: ret = bal;
535: }
536: } catch (Exception e) {
537: logger.log(BasicLevel.ERROR, "cannot read account" + e);
538: throw new RemoteException("cannot read account" + e);
539: }
540: return ret;
541: }
542:
543: public boolean checkAccount(int a) throws RemoteException {
544: logger.log(BasicLevel.DEBUG, "");
545:
546: boolean ret = false;
547: ManacLocal m = null;
548:
549: // retry several times, because this operation may be rolledback
550: // in case of deadlock.
551: Exception exc = null;
552: int retry;
553: for (retry = 0; retry < 20; retry++) {
554: try {
555: history.log(BasicLevel.INFO, "\ta_" + a
556: + "\tCHECKED try #" + retry);
557: m = manacLocalHome.findByNum(a);
558: int b = m.getBalance();
559: if (b >= 0) {
560: ret = true;
561: } else {
562: logger.log(BasicLevel.WARN, "bad balance=" + b);
563: }
564: return ret;
565: } catch (Exception e) {
566: exc = e;
567: logger.log(BasicLevel.INFO, "retrying " + retry);
568: sleep(retry + 1);
569: }
570: }
571: logger.log(BasicLevel.WARN, "cannot check account: " + exc);
572: return ret;
573: }
574:
575: public boolean checkAll() throws RemoteException {
576: logger.log(BasicLevel.DEBUG, "");
577:
578: int count = 0;
579: int total = 0;
580: try {
581: Enumeration enumer = manacLocalHome.findAll();
582: while (enumer.hasMoreElements()) {
583: count++;
584: ManacLocal a = (ManacLocal) enumer.nextElement();
585: int balance = a.getBalance();
586: if (balance < 0) {
587: logger
588: .log(BasicLevel.ERROR,
589: "checkAllAccounts: bad balance: "
590: + balance);
591: return false;
592: }
593: String name = a.getName();
594: logger.log(BasicLevel.DEBUG, name + " : FINAL BALANCE="
595: + balance);
596: history.log(BasicLevel.INFO, name + "\t" + balance);
597: total += balance;
598: }
599: } catch (Exception e) {
600: logger.log(BasicLevel.ERROR, "checkAllAccounts:" + e);
601: return false;
602: }
603: int exp = initialValue * count;
604: if (total != exp) {
605: logger.log(BasicLevel.ERROR,
606: "checkAllAccounts: bad total: " + total
607: + " (expected: " + exp + ")");
608: history.log(BasicLevel.INFO, "CheckAll ERROR: " + total
609: + "\t" + exp);
610: return false;
611: }
612: logger.log(BasicLevel.DEBUG, "total=" + total);
613: history.log(BasicLevel.INFO, "CheckAll OK");
614: return true;
615: }
616:
617: }
|