1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.finalist.util.sequencegenerator;
19
20 import com.finalist.util.sequencegenerator.tablecreator.TableCreator;
21
22 import javax.ejb.CreateException;
23 import javax.ejb.EJBException;
24 import javax.ejb.SessionBean;
25 import javax.ejb.SessionContext;
26 import javax.naming.InitialContext;
27 import javax.naming.NamingException;
28 import javax.sql.DataSource;
29 import java.rmi.RemoteException;
30 import java.sql.Connection;
31 import java.sql.PreparedStatement;
32 import java.sql.ResultSet;
33 import java.sql.SQLException;
34 import java.util.HashMap;
35 import java.util.Properties;
36
37 /*** Generates unique sequences.<p>
38 * This EJB can use any datasource and table. If the table and/or required<br>
39 * columns doesn't exist, the EJB will create them. By using a factory<br>
40 * it can manage several types of databases.<p>
41 * If the EJB is instantiated the datasource and table must be supplied.<br>
42 * It uses an internal HashMap for sequenceblock.<p>
43 * The EJB doesn't garantees a continous sequence! So the sequence can contain<br>
44 * so called gaps.<p>
45 * Since version 2.0 all settings are delivered by a Properties objects. This object<br>
46 * must be provided by the Facade in the application using this (shared) EJB.<br>
47 * In this Properties objects the keys are provided by the SequenceGeneratorConstants and<br>
48 * are mapped as follows:
49 * <ul><li>SequenceGeneratorConstants.DATASOURCENAME: Datasource name of the datasource holding the sequence table (mandatory, no default)</li>
50 * <li>SequenceGeneratorConstants.BLOCKSIZE: Default blocksize for new sequences stored as String (default: 10)</li>
51 * <li>SequenceGeneratorConstants.TABLENAME: Name of the sequence table (default: SEQUENCETABLE)</li>
52 * <li>SequenceGeneratorConstants.NAMECOLUMN: Name of the column where the sequence names are stored (default: SEQUENCE_NAME)</li>
53 * <li>SequenceGeneratorConstants.VALUECOLUMN: Name of the column where the sequence values are stored (default: SEQUENCE_VALUE)</li>
54 * <li>SequenceGeneratorConstants.BLOCKSIZECOLUMN: Name of the column where the sequence blocksizes are stored (default: SEQUENCE_BLOCKSIZE)</li>
55 * <li>SequenceGeneratorConstants.USEPUBLICSYNONYMS: Boolean value (stored as String) for indication to check for synonyms first (default: false)</li>
56 * <li>SequenceGeneratorConstants.CUSTOMTABLENAME: Name of a alternate table for the sequences. Has higher preference to TABLENAME (default: none)</li>
57 * </ul>
58 * Since this version the CustomSequenceGeneratorSettings object is no longer supported!<p>
59 *
60 * <i><b>NOTE:</b> The generator supports SYNONYMS in the TableCreator, however it <br>
61 * cannot alter the original table of the synonym. When using SYNONYMS make sure<br>
62 * the table is correct before using the generator!</i><p>
63 * Usage:<br>
64 * Create a FacadeBean that uses this Session EJB. This Facade knows about the <br>
65 * used datasource and table name. Another advantage is that it is loose coupled<br>
66 * So changing the SequenceGenerator is easy.<br>
67 * Another approach is creating an Object that holds all configuration information.<br>
68 * See SequenceGeneratorCustomizable interface on how to do this.
69 * @author Vincent van Wichen (based on code of Erik Jan de Wit)
70 * @version $Revision: 1.7 $, $Date: 2004/04/03 13:40:36 $
71 * @ejb:bean type="Stateless"
72 * name="com.finalist.util.sequencegenerator.SequenceGenerator"
73 * local-jndi-name="local/com/finalist/util/sequencegenerator/SequenceGenerator"
74 * jndi-name="com/finalist/util/sequencegenerator/SequenceGenerator"
75 * view-type="both"
76 * @ejb:transaction type="NotSupported"
77 * @ejb:transaction-type type="Container"
78 * @ejb:util generate="physical"
79 * @see SequenceGeneratorCustomizable
80 * @see SequenceGeneratorConstants
81 */
82 public class SequenceGeneratorEJB implements SessionBean {
83
84 /*** Log handler */
85 private transient org.apache.log4j.Category log = org.apache.log4j.Category.getInstance(SequenceGeneratorEJB.class);
86
87 /*** Start of every new sequence */
88 private static final long DEFAULT_START_OF_SEQUENCE = 1l;
89 /*** Stores all sequence blocks. Static so all EJB's will use the same instance. */
90 private static HashMap sequenceBlocks;
91
92 /*** Default blocksize */
93 private static int blockSize;
94
95 /*** Datasource to work with */
96 private String datasourceName;
97
98 /*** Stores the last used datasource. Used for caching */
99 private String lastDatasourceNameUsed = "";
100
101 /*** Reference to the datasource. */
102 private DataSource datasource = null;
103
104 /*** Name of the sequence table in the database. */
105 private String sequenceTableName;
106
107 private SessionContext ctx;
108
109 /*** Selects the next value in the sequencetable. */
110 private String SELECTSEQUENCE;
111
112 /*** Selects the next value in the sequencetable. */
113 private String SELECTSEQUENCE_NONLOCKING;
114
115 /*** Updates the new value in the database. */
116 private String UPDATESEQUENCE;
117
118 /*** Updates the blocksize in the database. */
119 private String UPDATEBLOCKSIZE;
120
121 /*** Inserts a new sequence in the database. */
122 private String INSERTSEQUENCE;
123
124 /*** Deletes a sequence from the database. */
125 private String DELETESEQUENCE;
126
127 /*** Holder for default setting Object */
128 private static SequenceGeneratorDefaults defaults = null;
129 /*** Holder for user settings object (if any) */
130
131
132 /*** Private holder for the column name for the sequence name */
133 private String sequenceNameCol = null;
134 /*** Private holder for the column name for the value of the sequence */
135 private String sequenceValueCol = null;
136 /*** Private holder for the column name for the block size */
137 private String sequenceBlocksizeCol = null;
138
139 private boolean useLocking = true;
140
141
142 public void setSessionContext(SessionContext context) throws RemoteException, EJBException {
143 ctx = context;
144 }
145
146
147 public void ejbActivate() throws RemoteException, EJBException {
148 }
149
150
151 public void ejbPassivate() throws RemoteException, EJBException {
152 }
153
154
155 public void ejbRemove() throws RemoteException, EJBException {
156 }
157
158
159 /*** Instantiates the EJB
160 * @ejb:create-method
161 * @throws CreateException
162 */
163 public void ejbCreate() throws CreateException {
164 if (sequenceBlocks == null) {
165 log.debug("Creating new SEQUENCEBLOCKS");
166 sequenceBlocks = new HashMap();
167 }
168 }
169
170
171 /*** Constructs a Property Object using the old settings method. Using a CustomSequenceGeneratorSettings Object
172 * @param datasourceName Name of the Datasource
173 * @param customSequenceTableName Name of a alternate tablename. <CODE>null</CODE> when using the table set in the settings.
174 * @return Properties Object reflecting the old settings
175 * @BUSINESSMETHOD
176 * @ejb:interface-mathod
177 */
178
179 public Properties getPropertiesByOldMethod(String datasourceName, String customSequenceTableName) {
180 Properties props = new Properties();
181 SequenceGeneratorCustomizable settings = null;
182 try {
183 Class foundClass = Class.forName("com.finalist.util.sequencegenerator.CustomSequenceGeneratorSettings");
184 log.debug("Custom settings object found!");
185 settings = (com.finalist.util.sequencegenerator.SequenceGeneratorCustomizable) foundClass.newInstance();
186 }
187 catch (ClassNotFoundException cnfe) {
188 log.debug("Custom settings object *not* found! All defaults will be used");
189 }
190 catch (InstantiationException ie) {
191 log.error("Custom settings object could not be instantiated. ABORT");
192 throw new EJBException(ie);
193 }
194 catch (IllegalAccessException iae) {
195 log.error("Custom settings object could not be instantiated. ABORT");
196 throw new EJBException(iae);
197 }
198
199
200 if (settings != null) {
201 props.setProperty(SequenceGeneratorConstants.DATASOURCENAME, settings.getDataSourceName());
202 }
203
204 if (customSequenceTableName == null) {
205 if (settings != null) {
206 props.setProperty(SequenceGeneratorConstants.TABLENAME, settings.getSequenceTableName());
207 }
208 }
209 else {
210 props.setProperty(SequenceGeneratorConstants.CUSTOMTABLENAME, customSequenceTableName);
211 }
212 if (settings != null) {
213 props.setProperty(SequenceGeneratorConstants.NAMECOLUMN, settings.getSequenceNameColumnName());
214 props.setProperty(SequenceGeneratorConstants.VALUECOLUMN, settings.getSequenceValueColumnName());
215 props.setProperty(SequenceGeneratorConstants.BLOCKSIZECOLUMN, settings.getSequenceBlockSizeColumnName());
216 props.setProperty(SequenceGeneratorConstants.BLOCKSIZE, "" + settings.getBlockSize());
217 props.setProperty(SequenceGeneratorConstants.USEPUBLICSYNONYMS, "" + settings.usePublicSynonyms());
218 }
219
220 return props;
221 }
222
223
224 /*** Initializes the attributes for its action.<p>
225 * This method is must always be called when handling a business method.
226 * @param datasourceName The datasource name for this sequence.
227 * @param customSequenceTableName The table name for this sequence in mentioned datasource.
228 * @deprecated Only <CODE>init(Properties)</CODE> is used.
229 */
230 private void init(String datasourceName, String customSequenceTableName) {
231 Properties props = this.getPropertiesByOldMethod(datasourceName, customSequenceTableName);
232 this.init(props);
233 }
234
235
236 /*** Initializes the attributes for its action.<p>
237 * This method is must always be called when handling a business method.
238 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
239 * See the SequenceGeneratorConstants for the settings.
240 */
241 private void init(Properties props) {
242 if (defaults == null) {
243 defaults = new SequenceGeneratorDefaults();
244 }
245
246 this.sequenceTableName = props.getProperty(SequenceGeneratorConstants.CUSTOMTABLENAME);
247 this.datasourceName = props.getProperty(SequenceGeneratorConstants.DATASOURCENAME);
248 this.blockSize = Integer.parseInt(props.getProperty(SequenceGeneratorConstants.BLOCKSIZE, "" + defaults.getBlockSize()));
249 this.sequenceNameCol = props.getProperty(SequenceGeneratorConstants.NAMECOLUMN, defaults.getSequenceNameColumnName());
250 this.sequenceValueCol = props.getProperty(SequenceGeneratorConstants.VALUECOLUMN, defaults.getSequenceValueColumnName());
251 this.sequenceBlocksizeCol = props.getProperty(SequenceGeneratorConstants.BLOCKSIZECOLUMN, defaults.getSequenceBlockSizeColumnName());
252
253 if (this.datasourceName == null) {
254 throw new EJBException("Datasource not specified");
255 }
256 if (this.sequenceTableName == null) {
257 this.sequenceTableName = props.getProperty(SequenceGeneratorConstants.TABLENAME, defaults.getSequenceTableName());
258 if (this.sequenceTableName == null) {
259 throw new EJBException("Sequence table is not specified");
260 }
261 }
262 if (this.blockSize == -1 || this.sequenceNameCol == null || this.sequenceValueCol == null || this.sequenceBlocksizeCol == null) {
263 throw new EJBException("One or more of the required attributes is/are not specified");
264 }
265
266
267 this.SELECTSEQUENCE_NONLOCKING =
268 "SELECT " + this.sequenceNameCol + "," + this.sequenceValueCol + "," + this.sequenceBlocksizeCol +
269 " FROM " + sequenceTableName + " WHERE " + this.sequenceNameCol + " = ?";
270 this.SELECTSEQUENCE = SELECTSEQUENCE_NONLOCKING + " FOR UPDATE";
271
272 this.UPDATESEQUENCE =
273 "UPDATE " + this.sequenceTableName + " SET " + this.sequenceValueCol + " = ?, " + this.sequenceBlocksizeCol + " = ? " +
274 "WHERE " + this.sequenceNameCol + " = ? AND " + this.sequenceValueCol + " <= ?";
275 this.UPDATEBLOCKSIZE =
276 "UPDATE " + this.sequenceTableName + " SET " + this.sequenceBlocksizeCol + " = ? " +
277 "WHERE " + this.sequenceNameCol + " = ?";
278 this.INSERTSEQUENCE =
279 "INSERT INTO " + this.sequenceTableName + " (" + this.sequenceNameCol + ", " +
280 this.sequenceValueCol + ", " + this.sequenceBlocksizeCol + ") VALUES (?,?,?)";
281 this.DELETESEQUENCE =
282 "DELETE FROM " + this.sequenceTableName + " WHERE " + this.sequenceNameCol + " = ?";
283 }
284
285
286 /*** Returns the next sequence number.<p>
287 * Uses the datasource and table specified in the configuration settings.<br>
288 * Same as <CODE>getNextNumberInSequence(name, null, null)</CODE>
289 * @BUSINESSMETHOD
290 * @ejb:interface-method
291 * @return Next sequence.
292 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
293 * @deprecated Use {@link getNextNumberInSequence(String, Properties)}
294 */
295 public long getNextNumberInSequence(String name) {
296 return this.getNextNumberInSequence(name, null, null);
297 }
298
299
300 /*** Returns the next sequence number.<p>
301 * Uses the table specified in the configuration settings.<br>
302 * Same as <CODE>getNextNumberInSequence(name, datasourceName, null)</CODE>
303 * @BUSINESSMETHOD
304 * @ejb:interface-method
305 * @return Next sequence.
306 * @param datasourceName Datasource name for the database
307 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
308 * @deprecated Use {@link getNextNumberInSequence(String, Properties)}
309 */
310 public long getNextNumberInSequence(String name, String datasourceName) {
311 return this.getNextNumberInSequence(name, datasourceName, null);
312 }
313
314
315 /*** Returns the next sequence number.<p>
316 * @BUSINESSMETHOD
317 * @ejb:interface-method
318 * @return Next sequence.
319 * @param datasourceName Datasource name for the database
320 * @param sequenceTableName Table name for this sequence in mentioned datasource
321 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
322 * @deprecated Use {@link getNextNumberInSequence(String, Properties)}
323 */
324 public long getNextNumberInSequence(String name, String datasourceName, String sequenceTableName) {
325 return this.getNextNumberInSequence(name, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
326 }
327
328
329 /*** Returns the next sequence number.<p>
330 * @BUSINESSMETHOD
331 * @ejb:interface-method
332 * @return Next sequence.
333 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
334 * See the SequenceGeneratorConstants for the settings.
335 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
336 */
337 public long getNextNumberInSequence(String name, Properties props) {
338 init(props);
339 SequenceBlock sequenceBlock = this.getSequenceBlock(name, props);
340
341 synchronized (sequenceBlock) {
342 if (sequenceBlock.current == sequenceBlock.last) {
343 this.refreshBlockForSequence(name, props);
344 }
345 return sequenceBlock.current++;
346 }
347 }
348
349
350 /*** Returns the next sequence number as String.<p>
351 * @BUSINESSMETHOD
352 * @ejb:interface-method
353 * @return Next sequence.
354 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
355 * @deprecated use {@link getNextNumberInSequenceAsString(String, Properties)}
356 */
357 public String getNextNumberInSequenceAsString(String name) {
358 return this.getNextNumberInSequenceAsString(name, null, null);
359 }
360
361
362 /*** Returns the next sequence number as String.<p>
363 * @BUSINESSMETHOD
364 * @ejb:interface-method
365 * @return Next sequence.
366 * @param datasourceName Datasource name for the database
367 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
368 * @deprecated use {@link getNextNumberInSequenceAsString(String, Properties)}
369 */
370 public String getNextNumberInSequenceAsString(String name, String datasourceName) {
371 return this.getNextNumberInSequenceAsString(name, datasourceName, null);
372 }
373
374
375 /*** Returns the next sequence number as String.<p>
376 * @BUSINESSMETHOD
377 * @ejb:interface-method
378 * @return Next sequence.
379 * @param datasourceName Datasource name for the database
380 * @param sequenceTableName Table name for this sequence in mentioned datasource
381 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
382 * @deprecated use {@link getNextNumberInSequenceAsString(String, Properties)}
383 */
384 public String getNextNumberInSequenceAsString(String name, String datasourceName, String sequenceTableName) {
385 return "" + this.getNextNumberInSequence(name, datasourceName, sequenceTableName);
386 }
387
388
389 /*** Returns the next sequence number as String.<p>
390 * @BUSINESSMETHOD
391 * @ejb:interface-method
392 * @return Next sequence.
393 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
394 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
395 * See the SequenceGeneratorConstants for the settings.
396 */
397 public String getNextNumberInSequenceAsString(String name, Properties props) {
398 return "" + this.getNextNumberInSequence(name, props);
399 }
400
401
402 /*** Returns the next sequence number as fixed length zero-filled String.<p>
403 * @BUSINESSMETHOD
404 * @ejb:interface-method
405 * @return Next sequence.
406 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
407 * @param length of the desired String
408 * @deprecated Use {@link getNextNumberInSequenceAsString(String, int, Properties)}
409 */
410 public String getNextNumberInSequenceAsString(String name, int length) {
411 return this.getNextNumberInSequenceAsString(name, null, null, length);
412 }
413
414
415 /*** Returns the next sequence number as fixed length zero-filled String.<p>
416 * @BUSINESSMETHOD
417 * @ejb:interface-method
418 * @return Next sequence.
419 * @param datasourceName Datasource name for the database
420 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
421 * @param length of the desired String
422 * @deprecated Use {@link getNextNumberInSequenceAsString(String, int, Properties)}
423 */
424 public String getNextNumberInSequenceAsString(String name, String datasourceName, int length) {
425 return this.getNextNumberInSequenceAsString(name, datasourceName, null, length);
426 }
427
428
429 /*** Returns the next sequence number as fixed length zero-filled String.<p>
430 * @BUSINESSMETHOD
431 * @ejb:interface-method
432 * @return Next sequence.
433 * @param datasourceName Datasource name for the database
434 * @param sequenceTableName Table name for this sequence in mentioned datasource
435 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
436 * @param length of the desired String
437 * @deprecated Use {@link getNextNumberInSequenceAsString(String, int, Properties)}
438 */
439 public String getNextNumberInSequenceAsString(String name, String datasourceName, String sequenceTableName, int length) {
440 return this.getNextNumberInSequenceAsString(name, length, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
441 }
442
443
444 /*** Returns the next sequence number as fixed length zero-filled String.<p>
445 * @BUSINESSMETHOD
446 * @ejb:interface-method
447 * @return Next sequence.
448 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
449 * See the SequenceGeneratorConstants for the settings.
450 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
451 * @param length of the desired String
452 */
453 public String getNextNumberInSequenceAsString(String name, int length, Properties props) {
454 StringBuffer sb = new StringBuffer(this.getNextNumberInSequenceAsString(name, props));
455 while (sb.length() < length) {
456 sb.insert(0, '0');
457 }
458 return sb.toString();
459 }
460
461
462 /*** Sets the new number in the sequence.<p>
463 * The new number must greater than or equal to the current key<br>
464 * and if the falls outside the block, it must be greater than or equal to the current value in the<br>
465 * database. If not then it will throw an exception.
466 * Uses the datasource and table specified in the configuration settings.<br>
467 * Same as <CODE>setNumberInSequence(name, newNumber, null, null)</CODE>
468 * @BUSINESSMETHOD
469 * @ejb:interface-method
470 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
471 * @param newNumber New number in the sequence.
472 * @deprecated Use {@link setNumberInSequence(String, long, Properties)}
473 */
474 public void setNumberInSequence(String name, long newNumber) {
475 this.setNumberInSequence(name, newNumber, null, null);
476 }
477
478
479 /*** Sets the new number in the sequence.<p>
480 * The new number must greater than or equal to the current key<br>
481 * and if the falls outside the block, it must be greater than or equal to the current value in the<br>
482 * database. If not then it will throw an exception.
483 * Uses the table specified in the configuration settings.<br>
484 * Same as <CODE>setNumberInSequence(name, newNumber, datasourceName, null)</CODE>
485 * @BUSINESSMETHOD
486 * @ejb:interface-method
487 * @param datasourceName Datasource name for the database
488 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
489 * @param newNumber New number in the sequence.
490 * @deprecated Use {@link setNumberInSequence(String, long, Properties)}
491 */
492 public void setNumberInSequence(String name, long newNumber, String datasourceName) {
493 this.setNumberInSequence(name, newNumber, datasourceName, null);
494 }
495
496
497 /*** Sets the new number in the sequence.<p>
498 * The new number must greater than or equal to the current key<br>
499 * and if the falls outside the block, it must be greater than or equal to the current value in the<br>
500 * database. If not then it will throw an exception.
501 * @BUSINESSMETHOD
502 * @ejb:interface-method
503 * @param datasourceName Datasource name for the database
504 * @param sequenceTableName Table name for this sequence in mentioned datasource
505 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
506 * @param newNumber New number in the sequence.
507 * @deprecated Use {@link setNumberInSequence(String, long, Properties)}
508 */
509 public void setNumberInSequence(String name, long newNumber, String datasourceName, String sequenceTableName) {
510 this.setNumberInSequence(name, newNumber, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
511 }
512
513
514 /*** Sets the new number in the sequence.<p>
515 * The new number must greater than or equal to the current key<br>
516 * and if the falls outside the block, it must be greater than or equal to the current value in the<br>
517 * database. If not then it will throw an exception.
518 * @BUSINESSMETHOD
519 * @ejb:interface-method
520 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
521 * See the SequenceGeneratorConstants for the settings.
522 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
523 * @param newNumber New number in the sequence.
524 */
525 public void setNumberInSequence(String name, long newNumber, Properties props) {
526 log.debug("+ setNumberInSequence for " + name + " with value " + newNumber);
527 init(props);
528 SequenceBlock sequenceBlock = this.getSequenceBlock(name, props);
529 synchronized (sequenceBlock) {
530 if (newNumber < sequenceBlock.current) {
531 throw new EJBException("New key must be greater than or equal to current key! Sequence not updated");
532 }
533 if (newNumber <= sequenceBlock.last) {
534 sequenceBlock.current = newNumber;
535 log.info("Updating current key succeeded in memory! Update done in Memory.");
536 return;
537 }
538 Connection connection = null;
539 PreparedStatement ps = null;
540 try {
541 connection = getDatabaseConnection();
542 ps = connection.prepareStatement(this.UPDATESEQUENCE);
543 ps.setLong(1, newNumber);
544 ps.setInt(2, sequenceBlock.blockSize);
545 ps.setString(3, name);
546 ps.setLong(4, newNumber);
547 if (ps.executeUpdate() > 0) {
548 sequenceBlock.last = sequenceBlock.current + sequenceBlock.blockSize;
549 log.info("Updating current key succeeded! Update done in database & memory.");
550 }
551 else {
552 log.warn("Current key not updated in database & memory! Check if new key is >= than old key!");
553 return;
554 }
555 }
556 catch (SQLException sqle) {
557 throw new EJBException(sqle);
558 }
559 finally {
560 try {
561 if (ps != null) {
562 ps.close();
563 ps = null;
564 }
565 if (connection != null) {
566 connection.close();
567 connection = null;
568 }
569 }
570 catch (SQLException slqe1) {
571
572 }
573 }
574 }
575 }
576
577
578 /*** Sets a new block size of a sequence.<p>
579 * This new blocksize will be used when the block will be refreshed.<br>
580 * @BUSINESSMETHOD
581 * @ejb:interface-method
582 * @param datasourceName Datasource name for the database
583 * @param sequenceTableName Table name for this sequence in mentioned datasource
584 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
585 * @param blockSize New block size. Must be greater than 0
586 * @deprecated Use {@link setBlockSizeForSequence(String, int, boolean, Properties)}
587 */
588 public void setBlockSizeForSequence(String name, int blockSize, String datasourceName, String sequenceTableName) {
589 this.setBlockSizeForSequence(name, blockSize, false, datasourceName, sequenceTableName);
590 }
591
592
593 /*** Sets a new block size of a sequence.<p>
594 * This new blocksize will be used when the block will be refreshed. This can be<br>
595 * done by setting refreshBlock true as 3rd parameter.
596 * Uses the datasource and table specified in the configuration settings.<br>
597 * Same as <CODE>setBlockSizeForSequence(name, blockSize, refreshBlock, null, null)</CODE>
598 * @BUSINESSMETHOD
599 * @ejb:interface-method
600 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
601 * @param blockSize New block size. Must be greater than 0
602 * @param refreshBlock <CODE>True</CODE> if a refresh is forced.
603 * @deprecated Use {@link setBlockSizeForSequence(String, int, boolean, Properties)}
604 */
605 public void setBlockSizeForSequence(String name, int blockSize, boolean refreshBlock) {
606 this.setBlockSizeForSequence(name, blockSize, refreshBlock, null, null);
607 }
608
609
610 /*** Sets a new block size of a sequence.<p>
611 * This new blocksize will be used when the block will be refreshed. This can be<br>
612 * done by setting refreshBlock true as 3rd parameter.
613 * Uses the table specified in the configuration settings.<br>
614 * Same as <CODE>setBlockSizeForSequence(name, blockSize, refreshBlock, datasourceName, null)</CODE>
615 * @BUSINESSMETHOD
616 * @ejb:interface-method
617 * @param datasourceName Datasource name for the database
618 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
619 * @param blockSize New block size. Must be greater than 0
620 * @param refreshBlock True if a refresh is forced.
621 * @deprecated Use {@link setBlockSizeForSequence(String, int, boolean, Properties)}
622 */
623 public void setBlockSizeForSequence(String name, int blockSize, boolean refreshBlock, String datasourceName) {
624 this.setBlockSizeForSequence(name, blockSize, refreshBlock, datasourceName, null);
625 }
626
627
628 /*** Sets a new block size of a sequence.<p>
629 * This new blocksize will be used when the block will be refreshed. This can be<br>
630 * done by setting refreshBlock true as 3rd parameter.
631 * @BUSINESSMETHOD
632 * @ejb:interface-method
633 * @param datasourceName Datasource name for the database
634 * @param sequenceTableName Table name for this sequence in mentioned datasource
635 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
636 * @param blockSize New block size. Must be greater than 0
637 * @param refreshBlock True if a refresh is forced.
638 * @deprecated Use {@link setBlockSizeForSequence(String, int, boolean, Properties)}
639 */
640 public void setBlockSizeForSequence(String name, int blockSize, boolean refreshBlock, String datasourceName, String sequenceTableName) {
641 this.setBlockSizeForSequence(name, blockSize, refreshBlock, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
642 }
643
644
645 /*** Sets a new block size of a sequence.<p>
646 * This new blocksize will be used when the block will be refreshed. This can be<br>
647 * done by setting refreshBlock true as 3rd parameter.
648 * @BUSINESSMETHOD
649 * @ejb:interface-method
650 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
651 * See the SequenceGeneratorConstants for the settings.
652 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
653 * @param blockSize New block size. Must be greater than 0
654 * @param refreshBlock True if a refresh is forced.
655 */
656 public void setBlockSizeForSequence(String name, int blockSize, boolean refreshBlock, Properties props) {
657 log.debug("+ setBlockSizeForSequence changing Blocksize for " + name + " with value " + blockSize + " (refresh forced " + refreshBlock + ")");
658 init(props);
659 SequenceBlock sequenceBlock = this.getSequenceBlock(name, props);
660 synchronized (sequenceBlock) {
661 Connection connection = null;
662 PreparedStatement ps = null;
663 try {
664 connection = this.getDatabaseConnection();
665 ps = connection.prepareStatement(this.UPDATEBLOCKSIZE);
666 ps.setInt(1, blockSize);
667 ps.setString(2, name);
668 if (ps.executeUpdate() > 0) {
669 log.info("Blocksize for " + name + " changed to " + blockSize);
670 }
671 else {
672 log.error("Blocksize change failed for " + name);
673 return;
674 }
675 }
676 catch (SQLException sqle) {
677 throw new EJBException(sqle);
678 }
679 finally {
680 try {
681 if (ps != null) {
682 ps.close();
683 ps = null;
684 }
685 if (connection != null) {
686 connection.close();
687 connection = null;
688 }
689 }
690 catch (SQLException sqle1) {
691
692 }
693 }
694 sequenceBlock.blockSize = blockSize;
695 }
696 if (refreshBlock) {
697 this.refreshBlockForSequence(name, props);
698 }
699 }
700
701
702 /*** Refreshes the sequence block with a new sequence out of the database and updates the database.<p>
703 * Uses the datasource and table specified in the configuration settings.<br>
704 * Same as <CODE>refreshBlockForSequence(name, null, null)</CODE>
705 * @BUSINESSMETHOD
706 * @ejb:interface-method
707 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
708 * @deprecated Use {@link refreshBlockForSequence(String, Properties)}
709 */
710 public void refreshBlockForSequence(String name) {
711 this.refreshBlockForSequence(name, null, null);
712 }
713
714
715 /*** Refreshes the sequence block with a new sequence out of the database and updates the database.<p>
716 * Uses the table specified in the configuration settings.<br>
717 * Same as <CODE>refreshBlockForSequence(name, datasourceName, null)</CODE>
718 * @BUSINESSMETHOD
719 * @ejb:interface-method
720 * @param datasourceName Datasource name for the database
721 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
722 * @deprecated Use {@link refreshBlockForSequence(String, Properties)}
723 */
724 public void refreshBlockForSequence(String name, String datasourceName) {
725 this.refreshBlockForSequence(name, datasourceName, null);
726 }
727
728
729 /*** Refreshes the sequence block with a new sequence out of the database and updates the database.<p>
730 * @BUSINESSMETHOD
731 * @ejb:interface-method
732 * @param datasourceName Datasource name for the database
733 * @param sequenceTableName Table name for this sequence in mentioned datasource
734 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
735 * @deprecated Use {@link refreshBlockForSequence(String, Properties)}
736 */
737 public void refreshBlockForSequence(String name, String datasourceName, String sequenceTableName) {
738 this.refreshBlockForSequence(name, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
739 }
740
741
742 /*** Refreshes the sequence block with a new sequence out of the database and updates the database.<p>
743 * @BUSINESSMETHOD
744 * @ejb:interface-method
745 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
746 * See the SequenceGeneratorConstants for the settings.
747 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
748 */
749 public void refreshBlockForSequence(String name, Properties props) {
750 log.debug("+ refreshBlockForSequence for " + name);
751 init(props);
752 SequenceBlock sequenceBlock = this.getSequenceBlock(name, props);
753 synchronized (sequenceBlock) {
754 SequenceBlock newBlock = this.getNextKeyAfterIncrementingBy(name);
755 sequenceBlock.last = newBlock.last;
756 sequenceBlock.current = newBlock.current;
757 sequenceBlock.blockSize = newBlock.blockSize;
758 log.debug("Sequence Block updated: " + sequenceBlock.toString());
759 }
760 }
761
762
763 /*** Returns the low watermark in the sequence block.<p>
764 * This is the same value as getNextNumerInSequence, however it will not increase<br>
765 * the sequence.<br>
766 * Uses the datasource and table specified in the configuration settings.<br>
767 * Same as <CODE>getLowWaterMarkForSequence(name, null, null)</CODE>
768 * @BUSINESSMETHOD
769 * @ejb:interface-method
770 * @return The low watermark in the sequence.
771 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
772 * @deprecated Use {@link getLowWaterMarkForSequence(name, Properties)}
773 */
774 public long getLowWaterMarkForSequence(String name) {
775 return this.getLowWaterMarkForSequence(name, null, null);
776 }
777
778
779 /*** Returns the low watermark in the sequence block.<p>
780 * This is the same value as getNextNumerInSequence, however it will not increase<br>
781 * the sequence.
782 * Uses the table specified in the configuration settings.<br>
783 * Same as <CODE>getLowWaterMarkForSequence(name, datasourceName, null)</CODE>
784 * @BUSINESSMETHOD
785 * @ejb:interface-method
786 * @return The low watermark in the sequence.
787 * @param datasourceName Datasource name for the database
788 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
789 * @deprecated Use {@link getLowWaterMarkForSequence(name, Properties)}
790 */
791 public long getLowWaterMarkForSequence(String name, String datasourceName) {
792 return this.getLowWaterMarkForSequence(name, datasourceName, null);
793 }
794
795
796 /*** Returns the low watermark in the sequence block.<p>
797 * This is the same value as getNextNumerInSequence, however it will not increase<br>
798 * the sequence.
799 * @BUSINESSMETHOD
800 * @ejb:interface-method
801 * @return The low watermark in the sequence.
802 * @param datasourceName Datasource name for the database
803 * @param sequenceTableName Table name for this sequence in mentioned datasource
804 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
805 * @deprecated Use {@link getLowWaterMarkForSequence(name, Properties)}
806 */
807 public long getLowWaterMarkForSequence(String name, String datasourceName, String sequenceTableName) {
808 return this.getLowWaterMarkForSequence(name, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
809 }
810
811
812 /*** Returns the low watermark in the sequence block.<p>
813 * This is the same value as getNextNumerInSequence, however it will not increase<br>
814 * the sequence.
815 * @BUSINESSMETHOD
816 * @ejb:interface-method
817 * @return The low watermark in the sequence.
818 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
819 * See the SequenceGeneratorConstants for the settings.
820 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
821 */
822 public long getLowWaterMarkForSequence(String name, Properties props) {
823 init(props);
824 return getSequenceBlock(name, props).current;
825 }
826
827
828 /*** Gets the high watermark in the sequence.<p>
829 * This is the last number in the current sequence block of this sequence in memory. If the low watermark and<br>
830 * the high watermark is equal than a refresh is called.<br>
831 * Uses the datasource and table specified in the configuration settings.<br>
832 * Same as <CODE>getHighWaterMarkForSequence(name, null, null)</CODE>
833 * @BUSINESSMETHOD
834 * @ejb:interface-method
835 * @return The high watermark for this sequence.
836 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
837 * @deprecated Use {@link getHighWaterMarkForSequence(String, Properties)}
838 */
839 public long getHighWaterMarkForSequence(String name) {
840 return this.getHighWaterMarkForSequence(name, null, null);
841 }
842
843
844 /*** Gets the high watermark in the sequence. <p>
845 * This is the last number in the<br>
846 * current sequence block of this sequence in memory. If the low watermark and<br>
847 * the high watermark is equal than a refresh is called.<br>
848 * Uses the table specified in the configuration settings.<br>
849 * Same as <CODE>getHighWaterMarkForSequence(name, datasourceName, null)</CODE>
850 * @BUSINESSMETHOD
851 * @ejb:interface-method
852 * @return The high watermark for this sequence.
853 * @param datasourceName Datasource name for the database
854 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
855 * @deprecated Use {@link getHighWaterMarkForSequence(String, Properties)}
856 */
857 public long getHighWaterMarkForSequence(String name, String datasourceName) {
858 return this.getHighWaterMarkForSequence(name, datasourceName, null);
859 }
860
861
862 /*** Gets the high watermark in the sequence. <p>
863 * This is the last number in the<br>
864 * current sequence block of this sequence in memory. If the low watermark and<br>
865 * the high watermark is equal than a refresh is called.
866 * @BUSINESSMETHOD
867 * @ejb:interface-method
868 * @return The high watermark for this sequence.
869 * @param datasourceName Datasource name for the database
870 * @param sequenceTableName Table name for this sequence in mentioned datasource
871 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
872 * @deprecated Use {@link getHighWaterMarkForSequence(String, Properties)}
873 */
874 public long getHighWaterMarkForSequence(String name, String datasourceName, String sequenceTableName) {
875 return this.getHighWaterMarkForSequence(name, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
876 }
877
878
879 /*** Gets the high watermark in the sequence. <p>
880 * This is the last number in the<br>
881 * current sequence block of this sequence in memory. If the low watermark and<br>
882 * the high watermark is equal than a refresh is called.
883 * @BUSINESSMETHOD
884 * @ejb:interface-method
885 * @return The high watermark for this sequence.
886 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
887 * See the SequenceGeneratorConstants for the settings.
888 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
889 */
890 public long getHighWaterMarkForSequence(String name, Properties props) {
891 init(props);
892 return getSequenceBlock(name, props).last;
893 }
894
895
896 /*** Removes a sequence from the database.<p>
897 * If the sequence doesn't exist or several sequences matches the name<br>
898 * (nearly impossible) an exception is thrown.<br>
899 * Uses the datasource and table specified in the configuration settings.<br>
900 * Same as <CODE>removeSequence(name, null, null)</CODE>
901 * @BUSINESSMETHOD
902 * @ejb:interface-method
903 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
904 * @deprecated Use {@link removeSequence(String, Properties)}
905 */
906 public void removeSequence(String name) {
907 this.removeSequence(name, null, null);
908 }
909
910
911 /*** Removes a sequence from the database.<p>
912 * If the sequence doesn't exist or several sequences matches the name<br>
913 * (nearly impossible) an exception is thrown.<br>
914 * Uses the table specified in the configuration settings.<br>
915 * Same as <CODE>removeSequence(name, datasourceName, null)</CODE>
916 * @BUSINESSMETHOD
917 * @ejb:interface-method
918 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
919 * @param datasourceName Datasource name for the database
920 * @deprecated Use {@link removeSequence(String, Properties)}
921 */
922 public void removeSequence(String name, String datasourceName) {
923 this.removeSequence(name, datasourceName, null);
924 }
925
926
927 /*** Removes a sequence from the database.<p>
928 * If the sequence doesn't exist or several sequences matches the name<br>
929 * (nearly impossible) an exception is thrown.
930 * @BUSINESSMETHOD
931 * @ejb:interface-method
932 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
933 * @param datasourceName Datasource name for the database
934 * @param sequenceTableName Table name for this sequence in mentioned datasource
935 * @deprecated Use {@link removeSequence(String, Properties)}
936 */
937 public void removeSequence(String name, String datasourceName, String sequenceTableName) {
938 this.removeSequence(name, this.getPropertiesByOldMethod(datasourceName, sequenceTableName));
939
940 }
941
942
943 /*** Removes a sequence from the database.<p>
944 * If the sequence doesn't exist or several sequences matches the name<br>
945 * (nearly impossible) an exception is thrown.
946 * @BUSINESSMETHOD
947 * @ejb:interface-method
948 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
949 * See the SequenceGeneratorConstants for the settings.
950 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
951 */
952 public void removeSequence(String name, Properties props) {
953 log.debug("+ removeSequence for " + name);
954 init(props);
955 Connection connection = null;
956 PreparedStatement ps = null;
957 try {
958 connection = this.getDatabaseConnection();
959 connection.setAutoCommit(false);
960 ps = connection.prepareStatement(this.DELETESEQUENCE);
961 ps.setString(1, name);
962 if (ps.executeUpdate() != 1) {
963 connection.rollback();
964 log.error("Removal of " + name + " failed. No or several items exist");
965 }
966 else {
967 connection.commit();
968 log.info("Removal of " + name + " succeeded");
969 }
970 }
971 catch (SQLException sqle) {
972 log.error("Removal of " + name + " failed. Exception occurred: " + sqle.toString());
973 throw new EJBException(sqle);
974 }
975 finally {
976 try {
977 if (ps != null) {
978 ps.close();
979 ps = null;
980 }
981 if (connection != null) {
982 connection.close();
983 connection = null;
984 }
985 }
986 catch (SQLException sqle) {
987
988 }
989 }
990 this.removeSequenceBlock(name);
991 }
992
993
994
995
996
997
998
999
1000 /*** Gets the requested sequence stored in memory.<p> If not existing then a new one<br>
1001 * will be created. Before creation the database will be checked if it is valid.
1002 * @return A SequenceBlock
1003 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
1004 * See the SequenceGeneratorConstants for the settings.
1005 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
1006 */
1007 private synchronized SequenceBlock getSequenceBlock(String name, Properties props) {
1008 SequenceBlockId sequenceBlockId = new SequenceBlockId(this.datasourceName, this.sequenceTableName, name);
1009 SequenceBlock sequenceBlock = (SequenceBlock) sequenceBlocks.get(sequenceBlockId);
1010 if (sequenceBlock == null) {
1011
1012
1013 log.info("New sequence found! Checking database!");
1014 this.checkTable(props);
1015
1016 sequenceBlock = this.getNextKeyAfterIncrementingBy(name);
1017 log.info("New sequenceBlock (" + name + ") created: " + sequenceBlock.toString());
1018 sequenceBlocks.put(sequenceBlockId, sequenceBlock);
1019 }
1020 log.debug("" + sequenceBlocks.size() + " SequenceBlocks in memory");
1021 if (sequenceBlock == null) {
1022 throw new EJBException("SequenceBlock not found and created!");
1023 }
1024 return sequenceBlock;
1025 }
1026
1027
1028 /*** Gets the requested sequence stored in memory.<p> If not existing then a new one<br>
1029 * will be created. Before creation the database will be checked if it is valid.
1030 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
1031 */
1032 private synchronized void removeSequenceBlock(String name) {
1033 SequenceBlockId sequenceBlockId = new SequenceBlockId(this.datasourceName, this.sequenceTableName, name);
1034 sequenceBlocks.remove(sequenceBlockId);
1035 }
1036
1037
1038 /*** Checks if the database is valid and if it contains a valid table.<p>
1039 * If the table is not present one will be created. If the table exists<br>
1040 * it is checked and changed accordingly.<p>
1041 * It uses the TableCreator for this task.
1042 * @param props Properties Object with key/value as settings for the SequenceGenerator.<br>
1043 * See the SequenceGeneratorConstants for the settings.
1044 */
1045 private synchronized void checkTable(Properties props) {
1046 log.debug("+ checkTable");
1047 try {
1048 Connection con = this.getDatabaseConnection();
1049 try {
1050 TableCreator tableCreator = TableCreator.getInstance(con, props);
1051 if (tableCreator.checkDatabase()) {
1052 log.info("Table checked");
1053 }
1054 else {
1055 log.warn("Table check returned false!");
1056 }
1057 }
1058 finally {
1059 con.close();
1060 }
1061 }
1062 catch (SQLException sqle) {
1063 log.error(sqle);
1064 throw new EJBException(sqle);
1065 }
1066 }
1067
1068
1069 /*** Gets a database connection out of the datasource.<p>
1070 * @throws SQLException Thrown if something goes wrong in JDBC
1071 * @return A jdbc Connection
1072 */
1073 private Connection getDatabaseConnection() throws SQLException {
1074
1075 if (this.datasource == null || !this.datasourceName.equals(this.lastDatasourceNameUsed)) {
1076 this.lastDatasourceNameUsed = this.datasourceName;
1077 String tryDatasourceName = null;
1078 int maxTypes = 3;
1079 int retryType = 0;
1080 boolean found = false;
1081 while (!found) {
1082 try {
1083 tryDatasourceName = this.getDatabaseLookupName(retryType);
1084 log.debug("Going to try datasource: " + tryDatasourceName);
1085 if (tryDatasourceName != null) {
1086 if (tryDatasourceName.equals("ABORT")) {
1087 log.fatal("No suitable datasource found for SequenceGenerator! ABORT!!");
1088 throw new EJBException("No suitable datasource name found! Abort");
1089 }
1090 else {
1091 this.datasource = (DataSource) (new InitialContext().lookup(tryDatasourceName));
1092 found = true;
1093 log.info("Found a suitable name and datasource!");
1094 }
1095 }
1096 }
1097 catch (NamingException ne) {
1098 String errorMessage = "JDBC Datasource " + tryDatasourceName + " not found. Next try!";
1099 log.warn(errorMessage);
1100 }
1101 retryType++;
1102 }
1103 }
1104 return datasource.getConnection();
1105 }
1106
1107
1108 /*** Strips the given datasource name.<p>
1109 * Strips the given datasource name to overcome the differences between<br>
1110 * several application server handling the JNDI lookups and the datasources.<p>
1111 * @param type Type of the fixed name:
1112 * <ul><li>0 - The original name</li>
1113 * <li>1 - The original name without comp/env/ (if any)</li>
1114 * <li>2 - The original name without java: (if any)</li>
1115 * <li>3 - 1 and 2 combined</li></UL><ul>
1116 * <li>Any - return <CODE>"ABORT"</CODE></li>
1117 * </ul>
1118 * @return The stripped name will be returned or...<br>
1119 * If a type doesn't result in a valid name it will return <CODE>null</CODE>.<br>
1120 * If a invalid type is given <CODE>"ABORT"</CODE> will be returned.
1121 */
1122 private String getDatabaseLookupName(int type) {
1123
1124 String compenv = "comp/env/";
1125 String javaColon = "java:";
1126 switch (type) {
1127 case 0:
1128 {
1129 return this.datasourceName;
1130 }
1131 case 1:
1132 {
1133 if (this.datasourceName.indexOf(compenv) > -1) {
1134 return this.datasourceName.substring(0, this.datasourceName.indexOf(compenv)) + this.datasourceName.substring(this.datasourceName.indexOf(compenv) + compenv.length());
1135 }
1136 else {
1137 return null;
1138 }
1139 }
1140 case 2:
1141 {
1142 if (this.datasourceName.indexOf(javaColon) > -1) {
1143 return this.datasourceName.substring(javaColon.length());
1144 }
1145 else {
1146 return null;
1147 }
1148 }
1149 case 3:
1150 {
1151 String case3 = this.getDatabaseLookupName(1);
1152 if (case3 != null && case3.indexOf(javaColon) > -1) {
1153 return this.getDatabaseLookupName(1).substring(javaColon.length());
1154 }
1155 else {
1156 return null;
1157 }
1158 }
1159 default:
1160 {
1161 return "ABORT";
1162 }
1163 }
1164 }
1165
1166
1167 /*** Gets a new SequenceBlock out of the database.<p> Same as refresh and get new value.
1168 * @param name Name of the sequence. In combination with the datasource and table it can identify itself
1169 * @return A new sequenceBlock
1170 */
1171 private SequenceBlock getNextKeyAfterIncrementingBy(String name) {
1172 log.debug("+ GetNextKeyAfterIncrementingBy");
1173 Connection con = null;
1174 PreparedStatement psSelect = null;
1175 PreparedStatement psUpdate = null;
1176 boolean retry = false;
1177 int retryCounter = 0;
1178 int maxRetry = 2;
1179 SequenceBlock result = new SequenceBlock();
1180 result.current = -1;
1181
1182 try {
1183 con = this.getDatabaseConnection();
1184 con.setAutoCommit(false);
1185
1186 psSelect = useLocking ?
1187 con.prepareStatement(SELECTSEQUENCE) : con.prepareStatement(SELECTSEQUENCE_NONLOCKING);
1188 psUpdate = con.prepareStatement(UPDATESEQUENCE);
1189
1190 do {
1191 ResultSet rs = null;
1192
1193 try {
1194 retry = false;
1195 psSelect.setString(1, name);
1196 try {
1197 rs = psSelect.executeQuery();
1198 }
1199 catch (SQLException se) {
1200 psSelect = con.prepareStatement(SELECTSEQUENCE_NONLOCKING);
1201 psSelect.setString(1, name);
1202 rs = psSelect.executeQuery();
1203 log.warn("=========== SEQUENCE GENERATOR: !!WARNING!! ===========");
1204 log.warn("Your database appears to not support locking: SequenceGenerator will " +
1205 "continue without locking, but CAN NOT GUARANTEE that the generated primary " +
1206 "keys will be unique.");
1207 useLocking = false;
1208 }
1209
1210 if (!rs.next()) {
1211 retry = true;
1212 PreparedStatement psInsert = null;
1213 try {
1214 psInsert = con.prepareStatement(INSERTSEQUENCE);
1215 psInsert.setString(1, name);
1216 psInsert.setLong(2, DEFAULT_START_OF_SEQUENCE);
1217 psInsert.setInt(3, this.blockSize);
1218 if (psInsert.executeUpdate() != 1) {
1219 con.rollback();
1220 throw new EJBException("CANNOT CREATE SEQUENCE");
1221 }
1222 con.commit();
1223 }
1224 catch (SQLException sqle) {
1225 con.rollback();
1226 this.ctx.setRollbackOnly();
1227 throw new EJBException(sqle);
1228 }
1229 finally {
1230 try {
1231 if (psInsert != null) {
1232 psInsert.close();
1233 psInsert = null;
1234 }
1235 if (rs != null) {
1236 rs.close();
1237 rs = null;
1238 }
1239 }
1240 catch (SQLException eee) {
1241 log.error("Exception while closing connections for insert", eee);
1242 }
1243 }
1244 }
1245 else {
1246 int blockSize = rs.getInt(this.sequenceBlocksizeCol);
1247 if (blockSize == 0) {
1248 blockSize = this.blockSize;
1249 }
1250 long oldValue = rs.getLong(this.sequenceValueCol);
1251 long newValue = oldValue + blockSize;
1252 psUpdate.setLong(1, newValue);
1253 psUpdate.setInt(2, blockSize);
1254 psUpdate.setString(3, name);
1255 psUpdate.setLong(4, newValue);
1256 if (psUpdate.executeUpdate() != 1) {
1257 con.rollback();
1258 this.ctx.setRollbackOnly();
1259 throw new EJBException("CANNOT UPDATE SEQUENCE");
1260 }
1261 else {
1262 con.commit();
1263 result.current = oldValue;
1264 result.last = newValue;
1265 result.blockSize = blockSize;
1266 }
1267 }
1268 }
1269 catch (SQLException ee) {
1270 log.error("Exception while doing update: " + ee.toString());
1271 ee.printStackTrace();
1272 retry = retryCounter++ < maxRetry;
1273 }
1274 finally {
1275 try {
1276 if (rs != null) {
1277 rs.close();
1278 rs = null;
1279 }
1280 }
1281 catch (SQLException eee) {
1282 log.error("Error while closing a resultSet: ", eee);
1283 }
1284 }
1285 } while (retry);
1286 }
1287 catch (SQLException e) {
1288 throw new EJBException(e);
1289 }
1290 finally {
1291 try {
1292 if (psUpdate != null) {
1293 psUpdate.close();
1294 psUpdate = null;
1295 }
1296 if (psSelect != null) {
1297 psSelect.close();
1298 psSelect = null;
1299 }
1300 if (con != null) {
1301 con.close();
1302 con = null;
1303 }
1304 }
1305 catch (SQLException se) {
1306 log.error("Exception while closing: ", se);
1307 }
1308 }
1309 log.debug("- GetNextKeyAfterIncrementBy");
1310 if (result.current != -1) {
1311 return result;
1312 }
1313 else {
1314 throw new EJBException("UNKNOWN SEQUENCE ERROR OCCURED");
1315 }
1316 }
1317 }
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398