SequenceManagerHighLowImpl.java :  » Database-ORM » db-ojb » org » apache » ojb » broker » util » sequence » Java Open Source

Java Open Source » Database ORM » db ojb 
db ojb » org » apache » ojb » broker » util » sequence » SequenceManagerHighLowImpl.java
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;
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.