001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.key.impl;
010:
011: import com.completex.objective.components.OdalRuntimeException;
012: import com.completex.objective.components.persistency.OdalPersistencyException;
013: import com.completex.objective.components.persistency.Persistency;
014: import com.completex.objective.components.persistency.PersistentEntry;
015: import com.completex.objective.components.persistency.core.DatabasePolicy;
016: import com.completex.objective.components.persistency.key.ComplexSequenceKeyGenerator;
017: import com.completex.objective.components.persistency.key.OdalKeyPolicy;
018: import com.completex.objective.components.persistency.transact.Transaction;
019: import com.completex.objective.util.PropertyMap;
020:
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.util.Arrays;
025: import java.util.Map;
026:
027: /**
028: * Sequence Key Generator based on emulated sequence. It is table based with default table name
029: * odal_sequence.
030: * <p/>
031: * <p/>
032: * Example (staticAttributes section describes possible attributes to set):
033: * <br>
034: * <PRE>
035: * // keyGenerator = {
036: * // class = com.completex.objective.components.persistency.key.impl.SimpleSequenceKeyGeneratorImpl
037: * // staticAttributes = {
038: * // name = CONTACT_SEQ # Sequence name - either in the "table" (see the next attribute) or name of a
039: * // # database sequence when it is appropriate with accordance with the
040: * // # policy used (see "policy" attribute below).
041: * // table = # Optional attribute specifying the table used for storing ODAL sequences.
042: * // # If not set the default - "odal_sequence" - is used
043: * // sameTransaction = # Optional attribute specifying if the sequence has to be generated in the same
044: * // # (current) transaction. If false - a separate transaction is open to generate
045: * // # a sequence from the "table" when it is appropriate with accordance with the
046: * // # policy used (see "policy" attribute below).
047: * // # Default is false.
048: * // policy = # Optional attribute - policy class name
049: * // # (com.completex.objective.components.persistency.key.OdalKeyPolicy instance - assumed to have no arguments constructor) -
050: * // # if not set - com.completex.objective.components.persistency.key.impl.OdalKeyPolicyImpl
051: * // # is used
052: * // }
053: * // }
054: * </PRE>
055: * <p/>
056: * This class will generate the next sequence number according to the policy
057: * (com.completex.objective.components.persistency.key.OdalKeyPolicy) used.
058: * There are 3 possible way to generate sequence using this class -
059: * 1. Odal key table.
060: * 2. Database sequence
061: * 3. Database identity (or auto-increment) field.
062: * <p/>
063: * Odal Key Policy determines priorities give to each of the above mentioned methods.
064: * <br>
065: * <br>
066: * If all sequence generation method priorities are either disabled or not supported -
067: * exception is thrown.
068: * <br>
069: * Otherwise: the highest priority is calculated and the sequence generation method associated with it is used.
070: * NOTE: if the database does not support record level locks - Odal key table based sequence generation method
071: * is given the lowest priority though it is NOT considered disabled, meaning even not multi-process safe it
072: * is still may be used as the last resort.
073: * <br>
074: * With default Odal Key Policy, the priorities in descending order are ODAL KEY PRIORITY,
075: * SEQUENCE PRIORITY, IDENTITY PRIORITY. If the database does not support
076: * record level locks ODAL KEY PRIORITY moves to the 3rd (the lowest) position.
077: *
078: * @author Gennady Krizhevsky
079: */
080: public abstract class AbstractOdalSequenceKeyGenerator extends
081: AbstractKeyGenerator implements ComplexSequenceKeyGenerator {
082:
083: public static final OdalKeyPolicy DEFAULT_ODAL_KEY_POLICY = new OdalKeyPolicyImpl();
084:
085: public static final String DEFAULT_SEQ_TABLE = "odal_sequence";
086: public static final String COL_SEQ_NAME = "name";
087: public static final String COL_SEQ_VALUE = "value";
088: public static final String DISABLED_FOR_IDENTITY_KEY = "disabledForIdentity";
089:
090: protected String tableName = DEFAULT_SEQ_TABLE;
091: protected boolean sameTransaction;
092: protected OdalKeyPolicy odalKeyPolicy = DEFAULT_ODAL_KEY_POLICY;
093:
094: /**
095: * For databases that do not have explicit record level locks (like HSQLDB)
096: * but support sequences the supporting key generator can be used
097: * instead of the table one.
098: */
099: protected AbstractKeyGenerator supportingKeyGenerator = new SimpleSequenceKeyGeneratorImpl();
100:
101: protected AbstractOdalSequenceKeyGenerator(String tableName,
102: boolean sameTransaction) {
103: if (tableName != null) {
104: this .tableName = tableName;
105: }
106: this .sameTransaction = sameTransaction;
107: }
108:
109: protected AbstractOdalSequenceKeyGenerator() {
110: }
111:
112: /**
113: * @return tableName
114: */
115: public String getTableName() {
116: return tableName;
117: }
118:
119: /**
120: * @param tableName
121: */
122: public void setTableName(String tableName) {
123: this .tableName = tableName;
124: }
125:
126: /**
127: * @return true if the sequence is taken in current transaction
128: */
129: public boolean isSameTransaction() {
130: return sameTransaction;
131: }
132:
133: /**
134: * Set flag that indicates if the sequence is to be taken in current transaction
135: *
136: * @param sameTransaction static attributes
137: */
138: public void setSameTransaction(boolean sameTransaction) {
139: this .sameTransaction = sameTransaction;
140: }
141:
142: /* Example (staticAttributes section describes possible attributes to set):
143: * <br>
144: * <p/>
145: * Example (staticAttributes section describes possible attributes to set):
146: * <br>
147: * <PRE>
148: * // keyGenerator = {
149: * // class = com.completex.objective.components.persistency.key.impl.SimpleSequenceKeyGeneratorImpl
150: * // staticAttributes = {
151: * // name = CONTACT_SEQ # Sequence name - either in the "table" (see the next attribute) or name of a
152: * // # database sequence when it is appropriate with accordance with the
153: * // # policy used (see "policy" attribute below).
154: * // table = # Optional attribute specifying the table used for storing ODAL sequences.
155: * // # If not set the default - "odal_sequence" - is used
156: * // sameTransaction = # Optional attribute specifying if the sequence has to be generated in the same
157: * // # (current) transaction. If false - a separate transaction is open to generate
158: * // # a sequence from the "table" when it is appropriate with accordance with the
159: * // # policy used (see "policy" attribute below).
160: * // # Default is false.
161: * // policy = # Optional attribute - policy class name
162: * // # (com.completex.objective.components.persistency.key.OdalKeyPolicy instance - assumed to have no arguments constructor) -
163: * // # if not set - com.completex.objective.components.persistency.key.impl.OdalKeyPolicyImpl
164: * // # is used
165: * // }
166: * // }
167: * </PRE>
168: *
169: * This class will generate the next sequence number according to the policy
170: * (com.completex.objective.components.persistency.key.OdalKeyPolicy) used.
171: * There are 3 possible way to generate sequence using this class -
172: * 1. Odal key table.
173: * 2. Database sequence
174: * 3. Database identity (or auto-increment) field.
175: *
176: * Odal Key Policy determines priorities give to each of the above mentioned methods.
177: * If all sequence generation method priorities are either disabled or not supported -
178: * throw exception
179: *
180: * Otherwise: the highest priority is calculated and the sequence generation method associated with it is used.
181: * NOTE: if the database does not support record level locks - Odal key table based sequence generation method
182: * is given the lowest priority though it is NOT considered disabled, meaning even not multi-process safe it
183: * is still may be used as the last resort.
184: *
185: *
186: * @param staticParameters
187: */
188: public void setStaticParameters(Object staticParameters) {
189: super .setStaticParameters(staticParameters);
190: if (staticParameters == null) {
191: throw new NullPointerException(
192: "AbstractOdalSequenceKeyGenerator::setStaticParameters: parameters == null");
193: }
194: PropertyMap parameters = PropertyMap
195: .toPropertyMap(((Map) staticParameters));
196: String seqName = parameters.getProperty(SEQ_KEY);
197: if (seqName == null) {
198: throw new IllegalArgumentException(
199: "Cannot find required parameter by key [" + SEQ_KEY
200: + "]");
201: }
202: setSeqName(seqName);
203: String tableName = parameters.getProperty(SEQ_TABLE_KEY,
204: DEFAULT_SEQ_TABLE);
205: boolean sameTransaction = parameters
206: .getBoolean(SEQ_SAME_TRANSACTION_KEY);
207: this .tableName = tableName;
208: this .sameTransaction = sameTransaction;
209:
210: supportingKeyGenerator.setStaticParameters(parameters);
211: }
212:
213: /**
214: * @see AbstractKeyGenerator#getNextKeyPlain(com.completex.objective.components.persistency.transact.Transaction, com.completex.objective.components.persistency.Persistency, String)
215: */
216: public Object getNextKeyPlain(final Transaction transaction,
217: final Persistency persistency, final String objectName)
218: throws OdalPersistencyException {
219:
220: Object id = null;
221: if (odalKeyPolicy.isDisabled()) {
222: throw new OdalRuntimeException(
223: "odalKeyPolicy has no active priorities");
224: }
225:
226: // If all sequence generation methods priorities are either disabled or not supported -
227: // throw exception
228: //
229: // Else: Calculate the highest priority and use the sequence generation method associated with it.
230: // NOTE: if this database does not support record level locks - Odal Key Table based sequence generation method
231: // is given the lowest priority though is not considered disabled
232: //
233: //
234:
235: DatabasePolicy databasePolicy = persistency.getDatabasePolicy();
236: ExecPriority[] priotities = extractPriorities(databasePolicy);
237:
238: validatePrioritiesEnabled(priotities);
239: sortPriorities(priotities);
240: for (int i = 0; i < priotities.length; i++) {
241: ExecPriority priotity = priotities[i];
242: if (priotity.getPriority() != OdalKeyPolicy.DISABLED_PRIORITY) {
243: id = priotity.execute(transaction, persistency,
244: objectName);
245: postExecPriority(id, priotity);
246: break;
247: }
248: }
249:
250: return id;
251: }
252:
253: protected void postExecPriority(Object id, ExecPriority priotity) {
254: }
255:
256: protected void sortPriorities(ExecPriority[] priotities) {
257: Arrays.sort(priotities);
258: }
259:
260: protected ExecPriority[] extractPriorities(
261: DatabasePolicy databasePolicy) {
262: ExecPriority odalKeyPriority = getOdalKeyPriority(databasePolicy);
263: ExecPriority sequencePriority = getSequencePriority(databasePolicy);
264: ExecPriority identityPriority = getIdentityPriority(databasePolicy);
265:
266: return new ExecPriority[] { odalKeyPriority, sequencePriority,
267: identityPriority };
268: }
269:
270: protected void validatePrioritiesEnabled(ExecPriority[] priotities)
271: throws OdalPersistencyException {
272: boolean foundEnabled = false;
273: for (int i = 0; i < priotities.length; i++) {
274: if (priotities[i].getPriority() != OdalKeyPolicy.DISABLED_PRIORITY) {
275: foundEnabled = true;
276: break;
277: }
278: }
279: if (!foundEnabled) {
280: throw new OdalPersistencyException(
281: "All the key generation priorities either disabled or not supported by database");
282: }
283: }
284:
285: protected ExecPriority getOdalKeyPriority(
286: DatabasePolicy databasePolicy) {
287: OdalKeyExecPriority priority = new OdalKeyExecPriority();
288: if (odalKeyPolicy.isOdalKeyPriorityDisabled()) {
289: priority.setPriority(OdalKeyPolicy.DISABLED_PRIORITY);
290: } else if (databasePolicy.supportsExplicitRecordLevelLocks()) {
291: priority.setPriority(odalKeyPolicy.getOdalKeyPriority());
292: } else {
293: priority.setPriority(Integer.MIN_VALUE);
294: }
295: return priority;
296: }
297:
298: protected ExecPriority getSequencePriority(
299: DatabasePolicy databasePolicy) {
300: SeqExecPriority priority = new SeqExecPriority();
301: if (odalKeyPolicy.isSequencePriorityDisabled()) {
302: priority.setPriority(OdalKeyPolicy.DISABLED_PRIORITY);
303: } else if (databasePolicy.supportsSequences()) {
304: priority.setPriority(odalKeyPolicy.getSequencePriority());
305: } else {
306: priority.setPriority(OdalKeyPolicy.DISABLED_PRIORITY);
307: }
308: return priority;
309: }
310:
311: protected ExecPriority getIdentityPriority(
312: DatabasePolicy databasePolicy) {
313: IdentityExecPriority priority = new IdentityExecPriority();
314: if (odalKeyPolicy.isIdentityPriorityDisabled()) {
315: priority.setPriority(OdalKeyPolicy.DISABLED_PRIORITY);
316: } else if (databasePolicy.supportsSequences()) {
317: priority.setPriority(odalKeyPolicy.getIdentityPriority());
318: } else {
319: priority.setPriority(OdalKeyPolicy.DISABLED_PRIORITY);
320: }
321: return priority;
322: }
323:
324: protected Object generateSequenceFromSupportingKeyGenerator(
325: Transaction transaction, Persistency persistency,
326: String objectName) throws OdalPersistencyException {
327: return supportingKeyGenerator.getNextKeyPlain(transaction,
328: persistency, objectName);
329: }
330:
331: protected Object generateSequenceFromOdalTable(
332: Transaction transaction, Persistency persistency,
333: String objectName) throws OdalPersistencyException {
334: ResultSet rs = null;
335: Long id = null;
336: PreparedStatement pstmt = null;
337: String sql = null;
338: Transaction localTransaction = transaction;
339: try {
340: //
341: // Process select:
342: //
343: String selectSql = new StringBuffer().append("select ")
344: .append(COL_SEQ_VALUE).append(" from ").append(
345: persistency.getDatabasePolicy()
346: .getLockHint(true, tableName))
347: .append(" where ").append(COL_SEQ_NAME).append(
348: " = ? ").append(
349: persistency.getDatabasePolicy()
350: .getLockString()).toString();
351:
352: sql = selectSql;
353: if (!sameTransaction) {
354: localTransaction = persistency.getTransactionManager()
355: .begin();
356: }
357: pstmt = prepareStatement(localTransaction, selectSql);
358: pstmt.setString(1, objectName);
359: rs = pstmt.executeQuery();
360: rs.next();
361: long oldValue = rs.getLong(1);
362: long value = oldValue + 1;
363: id = new Long(value);
364: if (getLogger() != null && getLogger().isTraceEnabled()) {
365: getLogger().trace(
366: "getNextKey: objectName=" + objectName);
367: }
368: rs.close();
369: rs = null;
370: pstmt.close();
371: pstmt = null;
372:
373: //
374: // Process update:
375: //
376: String updateSql = new StringBuffer().append("update ")
377: .append(tableName).append(" set ").append(
378: COL_SEQ_VALUE).append(" = ? where ")
379: .append(COL_SEQ_NAME).append(" = ? ").append(
380: " and ").append(COL_SEQ_VALUE).append(
381: " = ? ").toString();
382: sql = updateSql;
383: pstmt = prepareStatement(localTransaction, updateSql);
384: pstmt.setLong(1, value);
385: pstmt.setString(2, objectName);
386: pstmt.setLong(3, oldValue);
387:
388: int rc = pstmt.executeUpdate();
389: if (rc == 0) {
390: getLogger().error(updateSql + " updated nothing");
391: }
392: localTransaction.commit();
393:
394: pstmt.close();
395: pstmt = null;
396:
397: } catch (OdalPersistencyException e) {
398: logSqlException(sql, e);
399: throw e;
400: } catch (SQLException e) {
401: logSqlException(sql, e);
402: throw new OdalPersistencyException(e);
403: } finally {
404: closeAll(localTransaction, rs, pstmt);
405: if (localTransaction != null
406: && localTransaction != transaction) {
407: try {
408: persistency.getTransactionManager().release(
409: localTransaction);
410: } catch (SQLException e) {
411: e.printStackTrace();
412: }
413: }
414: }
415: return id;
416: }
417:
418: private void logSqlException(String sql, SQLException e) {
419: String sqlMsg = sql == null ? "" : ": " + sql;
420: getLogger().error("Cannot getNextKeyPlain" + sqlMsg, e);
421: }
422:
423: /**
424: * @see AbstractKeyGenerator#insertValue(com.completex.objective.components.persistency.transact.Transaction, com.completex.objective.components.persistency.Persistency, com.completex.objective.components.persistency.PersistentEntry)
425: */
426: public void insertValue(Transaction transaction,
427: Persistency persistency, PersistentEntry persistentEntry)
428: throws OdalPersistencyException {
429: if (isInsertConditionSatisfied0(persistentEntry)) {
430: Object nextKey = getNextKey(transaction, persistency,
431: persistentEntry.getRecord());
432: persistentEntry.setValue(toConventional(persistentEntry,
433: nextKey));
434: }
435: }
436:
437: protected boolean isInsertConditionSatisfied0(
438: PersistentEntry persistentEntry) {
439: return AbstractSequenceKeyGenerator
440: .isInsertConditionSatisfied(persistentEntry);
441: }
442:
443: /**
444: * @see AbstractKeyGenerator#updateValue(com.completex.objective.components.persistency.transact.Transaction, com.completex.objective.components.persistency.Persistency, com.completex.objective.components.persistency.PersistentEntry, boolean)
445: */
446: public void updateValue(Transaction transaction,
447: Persistency persistency, PersistentEntry persistentEntry,
448: boolean complexDirty) throws OdalPersistencyException {
449: }
450:
451: public OdalKeyPolicy getOdalKeyPolicy() {
452: return odalKeyPolicy;
453: }
454:
455: /**
456: * Sets Odal Key Policy
457: *
458: * @param odalKeyPolicy
459: * @see OdalKeyPolicy
460: */
461: public void setOdalKeyPolicy(OdalKeyPolicy odalKeyPolicy) {
462: this .odalKeyPolicy = odalKeyPolicy;
463: }
464:
465: /**
466: *
467: */
468: static interface ExecPriority extends Comparable {
469: Object execute(Transaction transaction,
470: Persistency persistency, String objectName)
471: throws OdalPersistencyException;
472:
473: int getPriority();
474: }
475:
476: protected abstract class AbstractExecPriority implements
477: ExecPriority {
478: private int priority = OdalKeyPolicy.DISABLED_PRIORITY;
479:
480: public int getPriority() {
481: return priority;
482: }
483:
484: public void setPriority(int priority) {
485: this .priority = priority;
486: }
487:
488: /**
489: * Sort descending
490: */
491: public int compareTo(Object o) {
492: ExecPriority thatPriority = (ExecPriority) o;
493: if (priority < thatPriority.getPriority()) {
494: return 1;
495: } else if (priority == thatPriority.getPriority()) {
496: return 0;
497: } else {
498: return -1;
499: }
500: }
501:
502: public String toString() {
503: return super .toString() + "; priority = " + priority;
504: }
505: }
506:
507: protected class OdalKeyExecPriority extends AbstractExecPriority {
508: public Object execute(Transaction transaction,
509: Persistency persistency, String objectName)
510: throws OdalPersistencyException {
511: return generateSequenceFromOdalTable(transaction,
512: persistency, objectName);
513: }
514: }
515:
516: protected class SeqExecPriority extends AbstractExecPriority {
517: public Object execute(Transaction transaction,
518: Persistency persistency, String objectName)
519: throws OdalPersistencyException {
520: return generateSequenceFromSupportingKeyGenerator(
521: transaction, persistency, objectName);
522: }
523: }
524:
525: protected class IdentityExecPriority extends AbstractExecPriority {
526: public Object execute(Transaction transaction,
527: Persistency persistency, String objectName)
528: throws OdalPersistencyException {
529: return null;
530: }
531: }
532: }
|