Java tutorial
package org.apache.ojb.broker.util.sequence; /* Copyright 2002-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.SystemUtils; import org.apache.ojb.broker.Identity; import org.apache.ojb.broker.OptimisticLockException; import org.apache.ojb.broker.PersistenceBroker; import org.apache.ojb.broker.PersistenceBrokerFactory; import org.apache.ojb.broker.metadata.FieldDescriptor; import org.apache.ojb.broker.util.ObjectModification; import org.apache.ojb.broker.util.logging.Logger; import org.apache.ojb.broker.util.logging.LoggerFactory; /** * High/Low sequence manager implementation generates unique and continuous * id's (during runtime) by using sequences to avoid database access. * <br/> * * <p> * Implementation configuration properties: * </p> * * <table cellspacing="2" cellpadding="2" border="3" frame="box"> * <tr> * <td><strong>Property Key</strong></td> * <td><strong>Property Values</strong></td> * </tr> * <tr> * <td>seq.start</td> * <td> * Set the start index of used sequences (e.g. set 100000, id generation starts with 100001). * Default start index is <em>1</em>. * </td> * </tr> * <tr> * <td>grabSize</td> * <td> * Integer entry determines the * number of IDs allocated within the * H/L sequence manager implementation. * Default was '20'. * </td> * </tr> * <tr> * <td>autoNaming</td> * <td> * Default was 'true'. If set 'true' OJB try to build a * sequence name automatic if none found in field-descriptor * and set this generated name as <code>sequence-name</code> * in field-descriptor. If set 'false' OJB throws an exception * if none sequence name was found in field-descriptor. * </td> * </tr> * <tr> * <td>globalSequenceId</td> * <td> * Deprecated! If set 'true' implementation use global unique * id's for all fields. Default was 'false'. * </td> * </tr> * <tr> * <td>globalSequenceStart</td> * <td> * <em>Deprecated, use property 'seq.start'.</em> Set the start index of used global id * generation (e.g. set 100000, id generation starts with 100001) * </td> * </tr> * <tr> * <td>sequenceStart</td> * <td> * <em>Deprecated, use property 'seq.start'.</em> Set the start index of used * sequences (e.g. set 100000, id generation starts with 100001). Default start index is <em>1</em>. * </td> * </tr> * </table> * * <br/> * <p> * <b>Limitations:</b> * <ul> * <li>Do NOT use this implementation in managed environment or * any comparable system where any connection was associated * with the running transaction.</li> * </ul> * </p> * * * <br/> * <br/> * * * @see org.apache.ojb.broker.util.sequence.SequenceManager * @see org.apache.ojb.broker.util.sequence.SequenceManagerFactory * @see org.apache.ojb.broker.util.sequence.SequenceManagerHelper * * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a> * @version $Id: SequenceManagerHighLowImpl.java,v 1.29.2.5 2005/12/21 22:28:41 tomdz Exp $ */ public class SequenceManagerHighLowImpl extends AbstractSequenceManager { private static Logger log = LoggerFactory.getLogger(SequenceManagerHighLowImpl.class); /** * sequence name used for global id generation. */ private static final String GLOBAL_SEQUENCE_NAME = "global - default sequence name"; public static final String PROPERTY_GRAB_SIZE = "grabSize"; public static final String PROPERTY_GLOBAL_SEQUENCE_ID = "globalSequenceId"; public static final String PROPERTY_GLOBAL_SEQUENCE_START = "globalSequenceStart"; protected static Map sequencesDBMap = new HashMap(); protected boolean useGlobalSequenceIdentities; protected int grabSize; protected long sequenceStart; protected int attempts; public SequenceManagerHighLowImpl(PersistenceBroker broker) { super(broker); Long start = SequenceManagerHelper.getSeqStart(getConfigurationProperties()); sequenceStart = start != null ? start.longValue() : 1; grabSize = Integer.parseInt(getConfigurationProperty(PROPERTY_GRAB_SIZE, "20")); useGlobalSequenceIdentities = Boolean .getBoolean(getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_ID, "false")); // support for deprecated properties long globalSequenceStart = Long.parseLong(getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_START, "1")); if (useGlobalSequenceIdentities && globalSequenceStart > sequenceStart) { sequenceStart = globalSequenceStart; } } protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException { HighLowSequence seq; String sequenceName = buildSequenceName(field); synchronized (SequenceManagerHighLowImpl.class) { // try to find sequence seq = getSequence(sequenceName); if (seq == null) { // not found, get sequence from database or create new seq = getSequence(getBrokerForClass(), field, sequenceName); addSequence(sequenceName, seq); } // now we have a sequence long id = seq.getNextId(); // seq does not have reserved IDs => catch new block of keys if (id == 0) { seq = getSequence(getBrokerForClass(), field, sequenceName); // replace old sequence!! addSequence(sequenceName, seq); id = seq.getNextId(); if (id == 0) { // something going wrong removeSequence(sequenceName); throw new SequenceManagerException("Sequence generation failed: " + SystemUtils.LINE_SEPARATOR + "Sequence: " + seq + ". Unable to build new ID, id was always 0." + SystemUtils.LINE_SEPARATOR + "Thread: " + Thread.currentThread() + SystemUtils.LINE_SEPARATOR + "PB: " + getBrokerForClass()); } } return id; } } /** * Returns last used sequence object or <code>null</code> if no sequence * was add for given sequence name. * * @param sequenceName Name of the sequence. * @return Sequence object or <code>null</code> */ private HighLowSequence getSequence(String sequenceName) { HighLowSequence result = null; // now lookup the sequence map for calling DB Map mapForDB = (Map) sequencesDBMap .get(getBrokerForClass().serviceConnectionManager().getConnectionDescriptor().getJcdAlias()); if (mapForDB != null) { result = (HighLowSequence) mapForDB.get(sequenceName); } return result; } /** * Put new sequence object for given sequence name. * @param sequenceName Name of the sequence. * @param seq The sequence object to add. */ private void addSequence(String sequenceName, HighLowSequence seq) { // lookup the sequence map for calling DB String jcdAlias = getBrokerForClass().serviceConnectionManager().getConnectionDescriptor().getJcdAlias(); Map mapForDB = (Map) sequencesDBMap.get(jcdAlias); if (mapForDB == null) { mapForDB = new HashMap(); } mapForDB.put(sequenceName, seq); sequencesDBMap.put(jcdAlias, mapForDB); } /** * Remove the sequence for given sequence name. * * @param sequenceName Name of the sequence to remove. */ protected void removeSequence(String sequenceName) { // lookup the sequence map for calling DB Map mapForDB = (Map) sequencesDBMap .get(getBrokerForClass().serviceConnectionManager().getConnectionDescriptor().getJcdAlias()); if (mapForDB != null) { synchronized (SequenceManagerHighLowImpl.class) { mapForDB.remove(sequenceName); } } } protected HighLowSequence getSequence(PersistenceBroker brokerForSequence, FieldDescriptor field, String sequenceName) throws SequenceManagerException { HighLowSequence newSequence = null; PersistenceBroker internBroker = null; try { /* arminw: we use a new internBroker instance, because we run into problems when current internBroker was rollback, then we have new sequence in memory, but not in database and a concurrent thread will get the same sequence. Thus we use a new internBroker instance (with new connection) to avoid this problem. */ internBroker = PersistenceBrokerFactory.createPersistenceBroker(brokerForSequence.getPBKey()); internBroker.beginTransaction(); newSequence = lookupStoreSequence(internBroker, field, sequenceName); internBroker.commitTransaction(); if (log.isDebugEnabled()) log.debug("new sequence was " + newSequence); } catch (Exception e) { log.error("Can't lookup new HighLowSequence for field " + (field != null ? field.getAttributeName() : null) + " using sequence name " + sequenceName, e); if (internBroker != null && internBroker.isInTransaction()) internBroker.abortTransaction(); throw new SequenceManagerException("Can't build new sequence", e); } finally { attempts = 0; if (internBroker != null) internBroker.close(); } return newSequence; } protected HighLowSequence lookupStoreSequence(PersistenceBroker broker, FieldDescriptor field, String seqName) { HighLowSequence newSequence; boolean needsInsert = false; Identity oid = broker.serviceIdentity().buildIdentity(HighLowSequence.class, seqName); // first we lookup sequence object in database newSequence = (HighLowSequence) broker.getObjectByIdentity(oid); //not in db --> we have to store a new sequence if (newSequence == null) { if (log.isDebugEnabled()) { log.debug("sequence for field " + field + " not found in db, store new HighLowSequence"); } /* here we lookup the max key for the given field in system */ // !!! here we use current broker instance to avoid deadlock !!! long maxKey = getMaxKeyForSequence(getBrokerForClass(), field); newSequence = newSequenceObject(seqName, field); newSequence.setMaxKey(maxKey); needsInsert = true; } // maybe property 'sequenceStart' was changed, so we check maxKey against // current set sequence start index if (newSequence.getMaxKey() < sequenceStart) { newSequence.setMaxKey(sequenceStart); } // set current grab size newSequence.setGrabSize(grabSize); //grab the next key scope newSequence.grabNextKeySet(); //store the sequence to db try { if (needsInsert) broker.store(newSequence, ObjectModification.INSERT); else broker.store(newSequence, ObjectModification.UPDATE); } catch (OptimisticLockException e) { // we try five times to get a new sequence if (attempts < 5) { log.info("OptimisticLockException was thrown, will try again to store sequence. Sequence was " + newSequence); attempts++; newSequence = lookupStoreSequence(broker, field, seqName); } else throw e; } return newSequence; } protected HighLowSequence newSequenceObject(String sequenceName, FieldDescriptor field) { HighLowSequence seq = new HighLowSequence(); seq.setName(sequenceName); seq.setGrabSize(grabSize); return seq; } protected long getMaxKeyForSequence(PersistenceBroker broker, FieldDescriptor field) { long maxKey; if (useGlobalSequenceIdentities) { maxKey = sequenceStart; } else { /* here we lookup the max key for the given field in system */ maxKey = SequenceManagerHelper.getMaxForExtent(broker, field); // check against start index maxKey = sequenceStart > maxKey ? sequenceStart : maxKey; } return maxKey; } private String buildSequenceName(FieldDescriptor field) throws SequenceManagerException { String seqName; if (useGlobalSequenceIdentities) { seqName = GLOBAL_SEQUENCE_NAME; } else { seqName = calculateSequenceName(field); } return seqName; } }