001: /*
002: * Danet GmbH
003: *
004: * $Id: KeyGenEJB.java,v 1.6 2006/10/11 09:06:57 drmlipp Exp $
005: *
006: * $Log: KeyGenEJB.java,v $
007: * Revision 1.6 2006/10/11 09:06:57 drmlipp
008: * Accessing KeyGen locally now.
009: *
010: * Revision 1.5 2006/10/07 20:41:34 mlipp
011: * Merged J2EE 1.4 adaptions from test branch.
012: *
013: * Revision 1.4 2005/04/08 11:28:02 drmlipp
014: * Merged changes from 1.3 branch up to 1.3p6.
015: *
016: * Revision 1.3.4.1 2005/04/04 20:08:54 drmlipp
017: * Changed WLS transaction isolation.
018: *
019: * Revision 1.3 2004/09/17 14:37:04 drmlipp
020: * More properties for J2EESDK port.
021: *
022: * Revision 1.2 2004/09/10 12:44:28 drmlipp
023: * Enabled call by reference for weblogic by default.
024: *
025: * Revision 1.1.1.6 2004/08/18 15:17:34 drmlipp
026: * Update to 1.2
027: *
028: * Revision 1.21 2004/07/06 14:07:38 lipp
029: * Circumvent JBoss idiosyncrasy.
030: *
031: * Revision 1.20 2004/06/23 18:50:29 lipp
032: * Added jndi-names for jonas.
033: *
034: * Revision 1.19 2004/06/23 15:06:38 lipp
035: * Started JOnAS port.
036: *
037: * Revision 1.18 2004/01/14 07:59:44 lipp
038: * Added transaction isolation attribute for WLS.
039: *
040: * Revision 1.17 2003/10/29 16:22:54 lipp
041: * Corrected exception handling.
042: *
043: * Revision 1.16 2003/10/27 13:33:25 lipp
044: * Added debug statements.
045: *
046: * Revision 1.15 2003/10/22 15:58:45 lipp
047: * Fixed synchronization problem.
048: *
049: * Revision 1.14 2003/10/20 15:10:54 lipp
050: * Fixed reference to lock EJB.
051: *
052: * Revision 1.13 2003/10/20 14:01:48 lipp
053: * Better support for data bases without SELECT FOR UPDATE.
054: *
055: * Revision 1.12 2003/10/01 14:20:22 lipp
056: * Adjusted weblogic datasource name.
057: *
058: * Revision 1.11 2003/09/28 19:21:09 lipp
059: * More improvements concerning security handling.
060: *
061: * Revision 1.10 2003/09/11 13:25:49 lipp
062: * Improved naming scheme for symbolic role names.
063: *
064: * Revision 1.9 2003/08/22 13:05:04 lipp
065: * Better data source name.
066: *
067: * Revision 1.8 2003/06/29 19:50:07 lipp
068: * Moved primary key generator fropm JDBCUtil to EJBUtil and made some
069: * fixes.
070: *
071: * Revision 1.7 2003/06/04 13:15:50 lipp
072: * Optimized resource allocation/caching.
073: *
074: * Revision 1.6 2003/05/23 15:42:41 lipp
075: * Fixed deployment unit dependencies.
076: *
077: * Revision 1.5 2003/05/22 09:20:53 lipp
078: * Improved KeyGen referencing.
079: *
080: * Revision 1.4 2003/04/03 10:35:23 ott
081: * Changes for Weblogic deployment
082: *
083: * Revision 1.3 2003/03/31 16:50:27 huaiyang
084: * Logging using common-logging.
085: *
086: * Revision 1.2 2003/02/14 21:57:12 lipp
087: * Fixed problem with reserved identifier.
088: *
089: * Revision 1.1 2003/02/14 16:48:07 lipp
090: * New primary key generator.
091: *
092: */
093: package de.danet.an.util;
094:
095: import java.rmi.RemoteException;
096: import java.sql.Connection;
097: import java.sql.PreparedStatement;
098: import java.sql.ResultSet;
099: import java.sql.SQLException;
100:
101: import javax.ejb.CreateException;
102: import javax.ejb.EJBException;
103: import javax.ejb.FinderException;
104: import javax.ejb.SessionBean;
105: import javax.ejb.SessionContext;
106: import javax.naming.NamingException;
107: import javax.sql.DataSource;
108:
109: /**
110: * This stateless session bean is part of the primary key generator in
111: * {@link JDBCUtil <code>JDBCUtil</code>}.
112: *
113: * @ejb.bean name="KeyGen" display-name="KeyGen"
114: * jndi-name="ejb/@@@_Utility-EJBs_KeyGenEJB_JNDI_Name_@@@"
115: * local-jndi-name="ejb/@@@_Utility-EJBs_KeyGenEJB_JNDI_Name_@@@Local"
116: * type="Stateless" transaction-type="Container" view-type="both"
117: * @jonas.bean ejb-name="KeyGen"
118: * jndi-name="@@@_Utility-EJBs_KeyGenEJB_JNDI_Name_@@@"
119: * @ejb.home remote-class="de.danet.an.util.KeyGenHome"
120: * @ejb.interface remote-class="de.danet.an.util.KeyGen"
121: * @ejb.ejb-ref ejb-name="KeyGenLock" view-type="remote"
122: * @ejb.resource-ref res-ref-name="jdbc/KeyGen" res-type="javax.sql.DataSource"
123: * res-auth="Container" jndi-name="DefaultDS"
124: * @jonas.resource res-ref-name="jdbc/KeyGen" jndi-name="jdbc_1"
125: * @weblogic.enable-call-by-reference True
126: * @weblogic.resource-description res-ref-name="jdbc/KeyGen"
127: * jndi-name="DefaultDS"
128: * @weblogic.transaction-isolation TRANSACTION_READ_COMMITTED
129: */
130: public class KeyGenEJB implements SessionBean {
131:
132: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
133: .getLog(KeyGenEJB.class);
134:
135: /** Database name. */
136: private static final String DB_NAME = "java:comp/env/jdbc/KeyGen";
137:
138: /** The SessionContext interface of the instance. */
139: private SessionContext ctx;
140:
141: /**
142: * The data source of the database.
143: * @see javax.sql.DataSource
144: */
145: private DataSource ds = null;
146:
147: /** Indicates if the database supports select for update. */
148: private boolean haveSelForUp = false;
149:
150: /** Has warning about "for update" been issued? */
151: private static boolean warningIssued = false;
152:
153: /** The key gen lock home cache. */
154: private KeyGenLockHome keyGenLockHomeCache = null;
155:
156: /**
157: * The home interface of the key gen lock EJB.
158: * @return home interface
159: */
160: private KeyGenLockHome keyGenLockHome()
161: throws ResourceNotAvailableException {
162: if (keyGenLockHomeCache == null) {
163: keyGenLockHomeCache = (KeyGenLockHome) EJBUtil
164: .retrieveEJBHome(KeyGenLockHome.class,
165: "java:comp/env/ejb/KeyGenLock");
166: }
167: return keyGenLockHomeCache;
168: }
169:
170: /**
171: * Set the associated session context. The container calls this method
172: * after the instance creation.
173: * @see javax.ejb.SessionBean
174: * @param context a SessionContext interface for the instance
175: */
176: public void setSessionContext(SessionContext context) {
177: ctx = context;
178: try {
179: ds = JDBCUtil.refreshDS(null, DB_NAME);
180: } catch (NamingException e) {
181: throw new EJBException(e);
182: }
183: }
184:
185: /**
186: * Not called for stateless session beans.
187: * @see javax.ejb.SessionBean
188: */
189: public void ejbActivate() {
190: // nothing to do
191: }
192:
193: /**
194: * Not called for stateless session beans.
195: * @see javax.ejb.SessionBean
196: */
197: public void ejbPassivate() {
198: // nothing to do
199: }
200:
201: /**
202: * Create an new instance of StaffDirectoryEJB.
203: * @throws CreateException if the StaffDirectoryEJB can not
204: * be create.
205: */
206: public void ejbCreate() throws CreateException {
207: try {
208: Connection con = ds.getConnection();
209: haveSelForUp = con.getMetaData().supportsSelectForUpdate();
210: if (!haveSelForUp && !warningIssued) {
211: logger
212: .warn("Database does not support SELECT FOR UPDATE, "
213: + "cannot be used for cluster configuration.");
214: warningIssued = true;
215: }
216: con.close();
217: } catch (SQLException e) {
218: logger.error(e.getMessage(), e);
219: throw new EJBException(e);
220: }
221: }
222:
223: /**
224: * A container invokes this method before it ends the life of the session
225: * object. This happens as a result of a client's invoking a remove
226: * operation, or when a container decides to terminate the session object
227: * after a timeout.
228: * @see javax.ejb.SessionBean
229: */
230: public void ejbRemove() {
231: ds = null;
232: ctx = null;
233: }
234:
235: /**
236: * Returns a new high value for the high/low key generator.
237: * @param keyTable the table used for storing the high keys.
238: * @param tabName the table for which a new key is requested.
239: * @param min minimum value. Note that this value is only significant
240: * when the first key for a table is generated.
241: * @return a new high value.
242: * @ejb.interface-method view-type="both"
243: * @ejb.transaction type="RequiresNew"
244: */
245: public long newHigh(String keyTable, String tabName, long min) {
246: KeyGenLock lock = null;
247: Connection con = null;
248: PreparedStatement prepStmt = null;
249: ResultSet rs = null;
250: try {
251: if (!haveSelForUp) {
252: // do application server wide locking by including an
253: // appropriate entity bean in the transaction.
254: try {
255: lock = keyGenLockHome().findByPrimaryKey(tabName);
256: lock.key();
257: } catch (FinderException e) {
258: logger.error(e.getMessage(), e);
259: }
260: }
261: if (logger.isDebugEnabled()) {
262: logger.debug("Generating new high for " + tabName);
263: }
264: String selectStatement = "SELECT NextKey FROM " + keyTable
265: + " WHERE TabName = ?"
266: + (haveSelForUp ? " FOR UPDATE" : "");
267: con = ds.getConnection();
268: prepStmt = con.prepareStatement(selectStatement);
269: prepStmt.setString(1, tabName);
270: rs = prepStmt.executeQuery();
271: if (!rs.next()) {
272: rs.close();
273: prepStmt.close();
274: prepStmt = con.prepareStatement("INSERT INTO "
275: + keyTable
276: + " (TabName, NextKey) VALUES (?, ?)");
277: prepStmt.setString(1, tabName);
278: prepStmt.setLong(2, min);
279: prepStmt.executeUpdate();
280: prepStmt.close();
281: prepStmt = con.prepareStatement(selectStatement);
282: prepStmt.setString(1, tabName);
283: rs = prepStmt.executeQuery();
284: if (!rs.next()) {
285: throw new EJBException(
286: "Newly created row not found?!");
287: }
288: }
289: long max = rs.getLong(1);
290: rs.close();
291: rs = null;
292: prepStmt.close();
293: prepStmt = con.prepareStatement("UPDATE " + keyTable
294: + " SET NextKey = ? WHERE TabName = ?");
295: prepStmt.setLong(1, max + 1);
296: prepStmt.setString(2, tabName);
297: prepStmt.executeUpdate();
298: if (logger.isDebugEnabled()) {
299: logger.debug("New high for " + tabName + " is " + max);
300: }
301: return max;
302: } catch (SQLException e) {
303: throw new EJBException(e);
304: } catch (RemoteException e) {
305: throw new EJBException(e);
306: } finally {
307: try {
308: JDBCUtil.closeAll(rs, prepStmt, con);
309: } catch (SQLException se) {
310: logger.error("Error releasing database resources: "
311: + se.getMessage(), se);
312: }
313: }
314: }
315:
316: }
|