KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > dbobj > NextNumberImpl


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.dbobj;
66
67 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
68 import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
69 import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData;
70 import com.jcorporate.expresso.core.db.DBException;
71 import com.jcorporate.expresso.core.misc.ConfigManager;
72 import com.jcorporate.expresso.core.misc.ReusableLong;
73
74 import java.util.Enumeration JavaDoc;
75 import java.util.Iterator JavaDoc;
76
77
78 /**
79  * this class is a SOLITAIRE, used in NextNumber
80  *
81  * @author Original by Michael Nash, rewritten by Michael Rimov, Larry Hamel
82  * <p/>
83  * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
84  * @see com.jcorporate.expresso.core.dbobj.NextNumber
85  * NextNumber - Manages in-memory, database independent autoincrement values. This
86  * version is not cluster safe. See Expresso Enterprise for a cluster-safe version.
87  * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
88  */

89 public class NextNumberImpl
90         extends NextNumber {
91
92     /**
93      * Top level hashMap - This level separates the db contexts. The object
94      * returned by a get() is another Map() containing lists of dbobject
95      * fields. The key is DBObject.getTargetTable() + &quot;.&quot; + DBField.getFieldName()
96      * this will return the Integer object that will be the number is internally
97      * incremented,
98      */

99     protected volatile ConcurrentReaderHashMap contextMap = new ConcurrentReaderHashMap();
100
101     private Object JavaDoc mInitLock = new Object JavaDoc();
102
103     /**
104      * Create the base level hashes for each db key.
105      */

106     public NextNumberImpl() {
107         for (Enumeration JavaDoc e = ConfigManager.getAllConfigKeys(); e.hasMoreElements();) {
108             String JavaDoc configKey = (String JavaDoc) e.nextElement();
109             contextMap.put(configKey, new ConcurrentReaderHashMap(40));
110         }
111     }
112
113     /**
114      * Initializes hash entries for all auto-inc fields in a particular DBOBject. This is
115      * called the first time that a particular DBObject has a nextnumber called for any field.
116      * this method should be synchronized externally.
117      *
118      * @param db The db to hook this nextnumber object to.
119      * @param callingObject the object to initialize from.
120      */

121     public void initialize(String JavaDoc db, DBObject callingObject)
122             throws DBException {
123         callingObject.setDataContext(db);
124
125         Iterator JavaDoc i = callingObject.getMetaData().getAllFieldsMap().values().iterator();
126         while (i.hasNext()) {
127             DBField dbf = (DBField) i.next();
128
129             if (dbf.isAutoIncremented()) {
130                 String JavaDoc fieldName = dbf.getName();
131                 // registerField has proper synchronization
132
registerField(db, callingObject, fieldName);
133             }
134
135         } /* While More Fields */
136     }
137
138     /**
139      * Register a field for next number information.
140      */

141     public void registerField(String JavaDoc db, DBObject callingDBOBject,
142                               String JavaDoc fieldName)
143             throws DBException {
144         String JavaDoc hashKey = getKey(callingDBOBject, fieldName);
145         ConcurrentReaderHashMap nextNumMap = (ConcurrentReaderHashMap) contextMap.get(db);
146
147         if (nextNumMap == null) {
148             throw new DBException("Uninitialized nextnumber context map");
149         }
150         if (!nextNumMap.containsKey(hashKey)) {
151
152             // no need to sync for getMax; efficient to keep this out of sync
153
long l = getMax(db, callingDBOBject, fieldName);
154             long setValue = 0;
155             boolean didInit = false;
156
157             // nextNumMap is unique item corresponding to db, and
158
// we are part of a solitaire object, so this sync is
159
// effectively a class-object (static) sync
160
synchronized (nextNumMap) {
161                 // retest after getting sync lock
162
if (!nextNumMap.containsKey(hashKey)) {
163                     nextNumMap.put(hashKey, new ReusableLong(l));
164                     // check setting for logging
165
setValue = ((ReusableLong) nextNumMap.get(hashKey)).longValue();
166                     didInit = true;
167                 }
168             } /* synchronized */
169
170             if (didInit && log.isDebugEnabled()) {
171                 log.debug("initialized hash entry for key: "
172                         + hashKey + " to value: " + l
173                         + " and confirmed value: " + setValue);
174             }
175
176         } /* If key is not present */
177     }
178
179     /**
180      * Returns the next number object without checking correctness for input parameters
181      * May result in NullPointerException if the table is fed bad input.
182      * if the field is not an auto-inc field, a warning will be logged.
183      *
184      * @param db The context to get this out of.
185      * @param callingDBObject - the DBObject that this nextnumber object belongs to.
186      * @param fieldName - the field name within the callingObject that this nextnumber belongs to.
187      * <p/>
188      * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
189      * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
190      */

191     private ReusableLong getNextNumberObject(String JavaDoc db,
192                                              DBObject callingDBObject,
193                                              String JavaDoc fieldName)
194             throws DBException {
195         String JavaDoc key = getKey(callingDBObject, fieldName);
196         ConcurrentReaderHashMap myDBMap = (ConcurrentReaderHashMap) contextMap.get(db);
197         ReusableLong rl = (ReusableLong) myDBMap.get(key);
198
199         if (rl == null) {
200             // synchronize in order to permit only one initialization per key
201
// if we assume that the JVM is pooling strings, then we could synchronize
202
// on just the string. However, the penalty we pay in synchronizing on
203
// "mInitLock" is small because it only happens for each callingobject on the first call
204
synchronized (mInitLock) {
205                 // retest after getting sync
206
if (myDBMap.get(key) == null) {
207                     initialize(db, callingDBObject);
208                 }
209             }
210
211             rl = (ReusableLong) myDBMap.get(key);
212
213             if (rl == null) {
214                 // unexpected since initialize() will register all auto-inc fields
215
// is this field an increment field?
216
DataFieldMetaData meta = callingDBObject.getFieldMetaData(fieldName);
217                 if (!meta.isAutoIncremented() && log.isDebugEnabled()) {
218                     log.debug("NextNumber.getNext() called for table.field, '"
219                             + ((JDBCObjectMetaData) callingDBObject.getMetaData()).getTargetSQLTable(
220                                     callingDBObject.getDataContext())
221                             + "." + fieldName
222                             + "', which is not auto-incrementing type. Setting up hash entry anyway.");
223                 }
224                 registerField(db, callingDBObject, fieldName);
225                 rl = (ReusableLong) myDBMap.get(key);
226             }
227
228             if (rl == null) {
229                 throw new DBException("Unable to locate nextnumber entry for key: " + key);
230             }
231         }
232
233         return rl;
234     }
235
236     /**
237      * All parameters are
238      * fully checked to avoid potential NullPointerExceptions
239      *
240      * @param db The context to get this out of.
241      * @param callingObject - the DBObject that this nextnumber object belongs to.
242      * @param fieldName - the field name within the callingObject that this nextnumber belongs to.
243      */

244     protected void checkParams(String JavaDoc db,
245                                DBObject callingObject,
246                                String JavaDoc fieldName)
247             throws DBException {
248
249         if (db == null || db.length() == 0) {
250             throw new DBException("NextNumber parameter 'db' must not be null or zero length");
251         }
252         if (callingObject == null) {
253             throw new DBException("NextNumber parameter 'callingDBOBject' must not be null");
254         }
255         if (fieldName == null) {
256             throw new DBException("NextNumber parameter 'fieldName' must not be null");
257         }
258
259         if (contextMap.get(db) == null) {
260             throw new DBException("NextNumber Unable to locate nextnumber map for context " + db);
261         }
262
263     }
264
265     /**
266      * Get the nextnumber for this dbobject. Increments the internal value.
267      */

268     public long getNext(String JavaDoc db, DBObject callingDBObject, String JavaDoc fieldName)
269             throws DBException {
270
271         if (CHECK_PARAMETERS) {
272             checkParams(db,
273                     callingDBObject,
274                     fieldName);
275         }
276
277
278         ReusableLong theNextNumberObject = getNextNumberObject(db,
279                 callingDBObject,
280                 fieldName);
281
282
283         long result;
284
285         // theNextNumberObject is unique item corresponding to db + object table, and
286
// we are part of a solitaire object, so this sync is
287
// effectively a class-object (static) sync corresponding to the object table
288
synchronized (theNextNumberObject) {
289             result = theNextNumberObject.longValue();
290             result++;
291             theNextNumberObject.setLong(result);
292         }
293
294         if (log.isDebugEnabled()) {
295             log.debug("getNext returns, for hash key: "
296                     + getKey(callingDBObject, fieldName) + " the value: " + result);
297         }
298
299
300         return result;
301     }
302
303     /**
304      * Reset the counts for the paritcular db each subsequent operation will
305      * require a new getMax(). There is questionable threadsafety about the
306      * reset methods. Please only do it on a "non-live" server. Used after a
307      * DBCreate or DeleteSchema has been called.
308      */

309     public void reset(String JavaDoc db) {
310         ConcurrentReaderHashMap myDBMap = (ConcurrentReaderHashMap) contextMap.get(db);
311
312         if (myDBMap != null) {
313             // theNextNumberObject is unique item corresponding to db + object table, and
314
// we are part of a solitaire object, so this sync is
315
// effectively a class-object (static) sync corresponding to the object table
316
synchronized (myDBMap) {
317                 myDBMap.clear();
318             }
319         }
320     }
321
322     /**
323      * Clears the table on a particular dbobject. Similar to reset db
324      *
325      * @param db The db context that the next number resides in.
326      * @param callingObject the object that links to the various nextnumber
327      * objects
328      */

329     public void reset(String JavaDoc db, DBObject callingObject) {
330         ConcurrentReaderHashMap myDBMap = (ConcurrentReaderHashMap) contextMap.get(db);
331
332         try {
333             if (myDBMap != null) {
334                 Iterator JavaDoc i = callingObject.getMetaData().getAllFieldsMap().values().iterator();
335
336                 while (i.hasNext()) {
337                     DBField dbf = (DBField) i.next();
338
339                     if (dbf.isAutoIncremented()) {
340                         String JavaDoc fieldName = dbf.getName();
341                         String JavaDoc hashKey = getKey(callingObject, fieldName);
342
343                         if (myDBMap.containsKey(hashKey)) {
344
345                             // hm is unique item corresponding to db, and
346
// we are part of a solitaire object, so this sync is
347
// effectively a class-object (static) sync
348
synchronized (myDBMap) {
349                                 // retest after getting sync
350
if (myDBMap.containsKey(hashKey)) {
351                                     myDBMap.remove(hashKey);
352                                 }
353                             } /* synchronized */
354
355                         } /* If key is not present */
356
357                     } /* if the type == auto-inc */
358
359                 } /* While More Fields */
360             }
361         } catch (DBException dbe) {
362             log.error("Error clearing one dbobject for next number, clearing whole stack");
363
364             if (myDBMap != null) {
365                 synchronized (myDBMap) {
366                     myDBMap.clear();
367                 }
368             }
369         }
370     }
371 }
372
Popular Tags