001    package org.apache.ojb.broker.util.sequence;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import org.apache.ojb.broker.PersistenceBroker;
019    import org.apache.ojb.broker.metadata.FieldDescriptor;
020    
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    /**
025     * <p>
026     * Very fast in memory sequence manager implementation, only the first
027     * time an id was requested for a class, the manager query the database
028     * for the max id in requested column - all following request were
029     * performed in memory.
030     * </p>
031     * <b>Limitations:</b>
032     * <ul type="disc">
033     *      <li>do not use in clustered environments</li>
034     *      <li>do not use if other applications generate id's for objects</li>
035     * </ul>
036     *
037     *
038     *
039     * <p>
040     * Implementation configuration properties:
041     * </p>
042     *
043     * <table cellspacing="2" cellpadding="2" border="3" frame="box">
044     * <tr>
045     *     <td><strong>Property Key</strong></td>
046     *     <td><strong>Property Values</strong></td>
047     * </tr>
048     * <tr>
049     *     <td>seq.start</td>
050     *     <td>
051     *         Set the start index of used sequences (e.g. set 100000, id generation starts with 100001).
052     *         Default start index is <em>1</em>.
053     *    </td>
054     * </tr>
055     * <tr>
056     *     <td>autoNaming</td>
057     *     <td>
058     *          Default was 'true'. If set 'true' OJB try to build a
059     *          sequence name automatic if none found in field-descriptor
060     *          and set this generated name as <code>sequence-name</code>
061     *          in field-descriptor. If set 'false' OJB throws an exception
062     *          if none sequence name was found in field-descriptor.
063     *    </td>
064     * </tr>
065     * <tr>
066     *     <td>sequenceStart</td>
067     *     <td>
068     *          <em>Deprecated, use property 'seq.start'.</em> Set the start index
069     *          of used sequences (e.g. set 100000, id generation starts with 100001).
070     *          Default start index is <em>1</em>.
071     *    </td>
072     * </tr>
073     * </table>
074     *
075     * <br/>
076     * <br/>
077     * <br/>
078     *
079     * @see org.apache.ojb.broker.util.sequence.SequenceManager
080     * @see org.apache.ojb.broker.util.sequence.SequenceManagerFactory
081     * @see org.apache.ojb.broker.util.sequence.SequenceManagerHelper
082     *
083     *
084     * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
085     * @version $Id: SequenceManagerInMemoryImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
086     */
087    public class SequenceManagerInMemoryImpl extends AbstractSequenceManager
088    {
089        protected static Map sequencesDBMap = new HashMap();
090        private long sequenceStart;
091    
092        public SequenceManagerInMemoryImpl(PersistenceBroker broker)
093        {
094            super(broker);
095            Long start = SequenceManagerHelper.getSeqStart(getConfigurationProperties());
096            sequenceStart = start != null ? start.longValue() : 1;
097        }
098    
099        protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
100        {
101            String seqName = calculateSequenceName(field);
102            // we have to be threadsafe
103            synchronized (SequenceManagerInMemoryImpl.class)
104            {
105                // get id for given seq name
106                Long currentId = getSequence(seqName);
107                // check - first time we search for sequence name
108                if (currentId == null)
109                {
110                    long maxKey = SequenceManagerHelper.getMaxForExtent(getBrokerForClass(), field);
111                    maxKey = sequenceStart > maxKey ? sequenceStart : maxKey;
112                    currentId = new Long(maxKey);
113                }
114                currentId = new Long(currentId.longValue() + 1);
115                // put new id back to map
116                addSequence(seqName, currentId);
117                return currentId.intValue();
118            }
119        }
120    
121        /**
122         * Returns last used sequence value or <code>null</code> if no sequence
123         * was add for given sequence name.
124         *
125         * @param sequenceName Name of the sequence.
126         * @return Last used sequence value or <code>null</code>
127         */
128        private Long getSequence(String sequenceName)
129        {
130            Long result = null;
131            // now lookup the sequence map for calling DB
132            Map mapForDB = (Map) sequencesDBMap.get(getBrokerForClass()
133                    .serviceConnectionManager().getConnectionDescriptor().getJcdAlias());
134            if(mapForDB != null)
135            {
136                result = (Long) mapForDB.get(sequenceName);
137            }
138            return result;
139        }
140    
141        /**
142         * Add new sequence value for sequence name.
143         * @param sequenceName Name of the sequence.
144         * @param seq The sequence value to add.
145         */
146        private void addSequence(String sequenceName, Long seq)
147        {
148            // lookup the sequence map for calling DB
149            String jcdAlias = getBrokerForClass()
150                    .serviceConnectionManager().getConnectionDescriptor().getJcdAlias();
151            Map mapForDB = (Map) sequencesDBMap.get(jcdAlias);
152            if(mapForDB == null)
153            {
154                mapForDB = new HashMap();
155            }
156            mapForDB.put(sequenceName, seq);
157            sequencesDBMap.put(jcdAlias, mapForDB);
158        }
159    
160        /**
161         * Remove the sequence for given sequence name.
162         *
163         * @param sequenceName Name of the sequence to remove.
164         */
165        protected void removeSequence(String sequenceName)
166        {
167            // lookup the sequence map for calling DB
168            Map mapForDB = (Map) sequencesDBMap.get(getBrokerForClass()
169                    .serviceConnectionManager().getConnectionDescriptor().getJcdAlias());
170            if(mapForDB != null)
171            {
172                synchronized(SequenceManagerInMemoryImpl.class)
173                {
174                    mapForDB.remove(sequenceName);
175                }
176            }
177        }
178    }