
org.apache.ojb.broker.util.sequence.SequenceManagerHighLowImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of db-ojb Show documentation
Show all versions of db-ojb Show documentation
ObJectRelationalBridge (OJB) is an Object/Relational mapping tool that
allows transparent persistence for Java Objects against relational
databases.
The newest version!
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.
*
*
*
* Implementation configuration properties:
*
*
*
*
* Property Key
* Property Values
*
*
* seq.start
*
* Set the start index of used sequences (e.g. set 100000, id generation starts with 100001).
* Default start index is 1.
*
*
*
* grabSize
*
* Integer entry determines the
* number of IDs allocated within the
* H/L sequence manager implementation.
* Default was '20'.
*
*
*
* autoNaming
*
* 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 sequence-name
* in field-descriptor. If set 'false' OJB throws an exception
* if none sequence name was found in field-descriptor.
*
*
*
* globalSequenceId
*
* Deprecated! If set 'true' implementation use global unique
* id's for all fields. Default was 'false'.
*
*
*
* globalSequenceStart
*
* Deprecated, use property 'seq.start'. Set the start index of used global id
* generation (e.g. set 100000, id generation starts with 100001)
*
*
*
* sequenceStart
*
* Deprecated, use property 'seq.start'. Set the start index of used
* sequences (e.g. set 100000, id generation starts with 100001). Default start index is 1.
*
*
*
*
*
*
* Limitations:
*
* - Do NOT use this implementation in managed environment or
* any comparable system where any connection was associated
* with the running transaction.
*
*
*
*
*
*
*
*
* @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 Armin Waibel
* @version $Id: SequenceManagerHighLowImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal 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 null
if no sequence
* was add for given sequence name.
*
* @param sequenceName Name of the sequence.
* @return Sequence object or null
*/
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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy