KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > util > sequence > SequenceManagerHighLowImpl


1 package org.apache.ojb.broker.util.sequence;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.util.HashMap JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import org.apache.commons.lang.SystemUtils;
22 import org.apache.ojb.broker.Identity;
23 import org.apache.ojb.broker.OptimisticLockException;
24 import org.apache.ojb.broker.PersistenceBroker;
25 import org.apache.ojb.broker.PersistenceBrokerFactory;
26 import org.apache.ojb.broker.metadata.FieldDescriptor;
27 import org.apache.ojb.broker.util.ObjectModification;
28 import org.apache.ojb.broker.util.logging.Logger;
29 import org.apache.ojb.broker.util.logging.LoggerFactory;
30
31 /**
32  * High/Low sequence manager implementation generates unique and continuous
33  * id's (during runtime) by using sequences to avoid database access.
34  * <br/>
35  *
36  * <p>
37  * Implementation configuration properties:
38  * </p>
39  *
40  * <table cellspacing="2" cellpadding="2" border="3" frame="box">
41  * <tr>
42  * <td><strong>Property Key</strong></td>
43  * <td><strong>Property Values</strong></td>
44  * </tr>
45  * <tr>
46  * <td>seq.start</td>
47  * <td>
48  * Set the start index of used sequences (e.g. set 100000, id generation starts with 100001).
49  * Default start index is <em>1</em>.
50  * </td>
51  * </tr>
52  * <tr>
53  * <td>grabSize</td>
54  * <td>
55  * Integer entry determines the
56  * number of IDs allocated within the
57  * H/L sequence manager implementation.
58  * Default was '20'.
59  * </td>
60  * </tr>
61  * <tr>
62  * <td>autoNaming</td>
63  * <td>
64  * Default was 'true'. If set 'true' OJB try to build a
65  * sequence name automatic if none found in field-descriptor
66  * and set this generated name as <code>sequence-name</code>
67  * in field-descriptor. If set 'false' OJB throws an exception
68  * if none sequence name was found in field-descriptor.
69  * </td>
70  * </tr>
71  * <tr>
72  * <td>globalSequenceId</td>
73  * <td>
74  * Deprecated! If set 'true' implementation use global unique
75  * id's for all fields. Default was 'false'.
76  * </td>
77  * </tr>
78  * <tr>
79  * <td>globalSequenceStart</td>
80  * <td>
81  * <em>Deprecated, use property 'seq.start'.</em> Set the start index of used global id
82  * generation (e.g. set 100000, id generation starts with 100001)
83  * </td>
84  * </tr>
85  * <tr>
86  * <td>sequenceStart</td>
87  * <td>
88  * <em>Deprecated, use property 'seq.start'.</em> Set the start index of used
89  * sequences (e.g. set 100000, id generation starts with 100001). Default start index is <em>1</em>.
90  * </td>
91  * </tr>
92  * </table>
93  *
94  * <br/>
95  * <p>
96  * <b>Limitations:</b>
97  * <ul>
98  * <li>Do NOT use this implementation in managed environment or
99  * any comparable system where any connection was associated
100  * with the running transaction.</li>
101  * </ul>
102  * </p>
103  *
104  *
105  * <br/>
106  * <br/>
107  *
108  *
109  * @see org.apache.ojb.broker.util.sequence.SequenceManager
110  * @see org.apache.ojb.broker.util.sequence.SequenceManagerFactory
111  * @see org.apache.ojb.broker.util.sequence.SequenceManagerHelper
112  *
113  * @author <a HREF="mailto:armin@codeAuLait.de">Armin Waibel</a>
114  * @version $Id: SequenceManagerHighLowImpl.java,v 1.29.2.5 2005/12/21 22:28:41 tomdz Exp $
115  */

116 public class SequenceManagerHighLowImpl extends AbstractSequenceManager
117 {
118     private static Logger log = LoggerFactory.getLogger(SequenceManagerHighLowImpl.class);
119     /**
120      * sequence name used for global id generation.
121      */

122     private static final String JavaDoc GLOBAL_SEQUENCE_NAME = "global - default sequence name";
123     public static final String JavaDoc PROPERTY_GRAB_SIZE = "grabSize";
124     public static final String JavaDoc PROPERTY_GLOBAL_SEQUENCE_ID = "globalSequenceId";
125     public static final String JavaDoc PROPERTY_GLOBAL_SEQUENCE_START = "globalSequenceStart";
126
127     protected static Map JavaDoc sequencesDBMap = new HashMap JavaDoc();
128
129     protected boolean useGlobalSequenceIdentities;
130     protected int grabSize;
131     protected long sequenceStart;
132     protected int attempts;
133
134     public SequenceManagerHighLowImpl(PersistenceBroker broker)
135     {
136         super(broker);
137         Long JavaDoc start = SequenceManagerHelper.getSeqStart(getConfigurationProperties());
138         sequenceStart = start != null ? start.longValue() : 1;
139         grabSize = Integer.parseInt(getConfigurationProperty(PROPERTY_GRAB_SIZE, "20"));
140         useGlobalSequenceIdentities = Boolean.getBoolean(getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_ID, "false"));
141         // support for deprecated properties
142
long globalSequenceStart = Long.parseLong(getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_START, "1"));
143         if(useGlobalSequenceIdentities && globalSequenceStart > sequenceStart)
144         {
145             sequenceStart = globalSequenceStart;
146         }
147     }
148
149     protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
150     {
151         HighLowSequence seq;
152         String JavaDoc sequenceName = buildSequenceName(field);
153         synchronized (SequenceManagerHighLowImpl.class)
154         {
155             // try to find sequence
156
seq = getSequence(sequenceName);
157
158             if (seq == null)
159             {
160                 // not found, get sequence from database or create new
161
seq = getSequence(getBrokerForClass(), field, sequenceName);
162                 addSequence(sequenceName, seq);
163             }
164
165             // now we have a sequence
166
long id = seq.getNextId();
167             // seq does not have reserved IDs => catch new block of keys
168
if (id == 0)
169             {
170                 seq = getSequence(getBrokerForClass(), field, sequenceName);
171                 // replace old sequence!!
172
addSequence(sequenceName, seq);
173                 id = seq.getNextId();
174                 if (id == 0)
175                 {
176                     // something going wrong
177
removeSequence(sequenceName);
178                     throw new SequenceManagerException("Sequence generation failed: " +
179                             SystemUtils.LINE_SEPARATOR + "Sequence: " + seq +
180                             ". Unable to build new ID, id was always 0." +
181                             SystemUtils.LINE_SEPARATOR + "Thread: " + Thread.currentThread() +
182                             SystemUtils.LINE_SEPARATOR + "PB: " + getBrokerForClass());
183                 }
184             }
185             return id;
186         }
187     }
188
189     /**
190      * Returns last used sequence object or <code>null</code> if no sequence
191      * was add for given sequence name.
192      *
193      * @param sequenceName Name of the sequence.
194      * @return Sequence object or <code>null</code>
195      */

196     private HighLowSequence getSequence(String JavaDoc sequenceName)
197     {
198         HighLowSequence result = null;
199         // now lookup the sequence map for calling DB
200
Map JavaDoc mapForDB = (Map JavaDoc) sequencesDBMap.get(getBrokerForClass()
201                 .serviceConnectionManager().getConnectionDescriptor().getJcdAlias());
202         if(mapForDB != null)
203         {
204             result = (HighLowSequence) mapForDB.get(sequenceName);
205         }
206         return result;
207     }
208
209     /**
210      * Put new sequence object for given sequence name.
211      * @param sequenceName Name of the sequence.
212      * @param seq The sequence object to add.
213      */

214     private void addSequence(String JavaDoc sequenceName, HighLowSequence seq)
215     {
216         // lookup the sequence map for calling DB
217
String JavaDoc jcdAlias = getBrokerForClass()
218                 .serviceConnectionManager().getConnectionDescriptor().getJcdAlias();
219         Map JavaDoc mapForDB = (Map JavaDoc) sequencesDBMap.get(jcdAlias);
220         if(mapForDB == null)
221         {
222             mapForDB = new HashMap JavaDoc();
223         }
224         mapForDB.put(sequenceName, seq);
225         sequencesDBMap.put(jcdAlias, mapForDB);
226     }
227
228     /**
229      * Remove the sequence for given sequence name.
230      *
231      * @param sequenceName Name of the sequence to remove.
232      */

233     protected void removeSequence(String JavaDoc sequenceName)
234     {
235         // lookup the sequence map for calling DB
236
Map JavaDoc mapForDB = (Map JavaDoc) sequencesDBMap.get(getBrokerForClass()
237                 .serviceConnectionManager().getConnectionDescriptor().getJcdAlias());
238         if(mapForDB != null)
239         {
240             synchronized(SequenceManagerHighLowImpl.class)
241             {
242                 mapForDB.remove(sequenceName);
243             }
244         }
245     }
246
247     protected HighLowSequence getSequence(PersistenceBroker brokerForSequence,
248                                         FieldDescriptor field,
249                                         String JavaDoc sequenceName) throws SequenceManagerException
250     {
251         HighLowSequence newSequence = null;
252         PersistenceBroker internBroker = null;
253         try
254         {
255             /*
256             arminw:
257             we use a new internBroker instance, because we run into problems
258             when current internBroker was rollback, then we have new sequence
259             in memory, but not in database and a concurrent thread will
260             get the same sequence.
261             Thus we use a new internBroker instance (with new connection) to
262             avoid this problem.
263             */

264             internBroker = PersistenceBrokerFactory.createPersistenceBroker(brokerForSequence.getPBKey());
265             internBroker.beginTransaction();
266
267             newSequence = lookupStoreSequence(internBroker, field, sequenceName);
268
269             internBroker.commitTransaction();
270
271             if (log.isDebugEnabled()) log.debug("new sequence was " + newSequence);
272         }
273         catch(Exception JavaDoc e)
274         {
275             log.error("Can't lookup new HighLowSequence for field "
276                     + (field != null ? field.getAttributeName() : null)
277                     + " using sequence name " + sequenceName, e);
278             if(internBroker != null && internBroker.isInTransaction()) internBroker.abortTransaction();
279             throw new SequenceManagerException("Can't build new sequence", e);
280         }
281         finally
282         {
283             attempts = 0;
284             if (internBroker != null) internBroker.close();
285         }
286         return newSequence;
287     }
288
289     protected HighLowSequence lookupStoreSequence(PersistenceBroker broker, FieldDescriptor field, String JavaDoc seqName)
290     {
291         HighLowSequence newSequence;
292         boolean needsInsert = false;
293
294         Identity oid = broker.serviceIdentity().buildIdentity(HighLowSequence.class, seqName);
295         // first we lookup sequence object in database
296
newSequence = (HighLowSequence) broker.getObjectByIdentity(oid);
297
298         //not in db --> we have to store a new sequence
299
if (newSequence == null)
300         {
301             if (log.isDebugEnabled())
302             {
303                 log.debug("sequence for field " + field + " not found in db, store new HighLowSequence");
304             }
305             /*
306             here we lookup the max key for the given field in system
307             */

308             // !!! here we use current broker instance to avoid deadlock !!!
309
long maxKey = getMaxKeyForSequence(getBrokerForClass(), field);
310
311             newSequence = newSequenceObject(seqName, field);
312             newSequence.setMaxKey(maxKey);
313             needsInsert = true;
314         }
315         // maybe property 'sequenceStart' was changed, so we check maxKey against
316
// current set sequence start index
317
if(newSequence.getMaxKey() < sequenceStart)
318         {
319             newSequence.setMaxKey(sequenceStart);
320         }
321
322         // set current grab size
323
newSequence.setGrabSize(grabSize);
324
325         //grab the next key scope
326
newSequence.grabNextKeySet();
327
328         //store the sequence to db
329
try
330         {
331             if(needsInsert) broker.store(newSequence, ObjectModification.INSERT);
332             else broker.store(newSequence, ObjectModification.UPDATE);
333         }
334         catch (OptimisticLockException e)
335         {
336             // we try five times to get a new sequence
337
if(attempts < 5)
338             {
339                 log.info("OptimisticLockException was thrown, will try again to store sequence. Sequence was "+newSequence);
340                 attempts++;
341                 newSequence = lookupStoreSequence(broker, field, seqName);
342             }
343             else throw e;
344         }
345         return newSequence;
346     }
347
348     protected HighLowSequence newSequenceObject(String JavaDoc sequenceName,
349                                               FieldDescriptor field)
350     {
351         HighLowSequence seq = new HighLowSequence();
352         seq.setName(sequenceName);
353         seq.setGrabSize(grabSize);
354         return seq;
355     }
356
357     protected long getMaxKeyForSequence(PersistenceBroker broker,
358                                         FieldDescriptor field)
359     {
360         long maxKey;
361         if (useGlobalSequenceIdentities)
362         {
363             maxKey = sequenceStart;
364         }
365         else
366         {
367             /*
368             here we lookup the max key for the given field in system
369             */

370             maxKey = SequenceManagerHelper.getMaxForExtent(broker, field);
371             // check against start index
372
maxKey = sequenceStart > maxKey ? sequenceStart : maxKey;
373         }
374         return maxKey;
375     }
376
377     private String JavaDoc buildSequenceName(FieldDescriptor field) throws SequenceManagerException
378     {
379         String JavaDoc seqName;
380         if (useGlobalSequenceIdentities)
381         {
382             seqName = GLOBAL_SEQUENCE_NAME;
383         }
384         else
385         {
386             seqName = calculateSequenceName(field);
387         }
388         return seqName;
389     }
390 }
391
Popular Tags