KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > appserver > server > sql > standard > StandardObjectIdAllocator


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  *
19  * $Id: StandardObjectIdAllocator.java,v 1.7 2005/05/26 08:08:10 predrag Exp $
20  *
21  */

22 package com.lutris.appserver.server.sql.standard;
23
24 import java.math.BigDecimal JavaDoc;
25 import java.math.BigInteger JavaDoc;
26 import java.sql.PreparedStatement JavaDoc;
27 import java.sql.ResultSet JavaDoc;
28 import java.sql.SQLException JavaDoc;
29
30 import org.enhydra.dods.DODS;
31
32 import com.lutris.appserver.server.sql.DBConnection;
33 import com.lutris.appserver.server.sql.DatabaseManagerException;
34 import com.lutris.appserver.server.sql.LogicalDatabase;
35 import com.lutris.appserver.server.sql.ObjectId;
36 import com.lutris.appserver.server.sql.ObjectIdAllocationError;
37 import com.lutris.appserver.server.sql.ObjectIdAllocator;
38 import com.lutris.appserver.server.sql.ObjectIdException;
39 import com.lutris.logging.Logger;
40 import com.lutris.util.Config;
41 import com.lutris.util.ConfigException;
42 import com.lutris.util.KeywordValueException;
43
44 /**
45  * Object ids can only be created via this manager.
46  * Ensures that all object ids are unique across all
47  * objects in this logical database. Also ensures good performance
48  * for allocating object ids.
49  * <P>
50  * The configuration data is specified in the section:
51  * <CODE>DatabaseManager.DB.<I>dbName</I>.ObjectId</CODE>
52  * <P>
53  * Configuration fields are:
54  * <UL>
55  * <LI> <CODE>CacheSize</CODE> -
56  * The number of object id's to cache between database queries. Optional,
57  * if not specified, then it defaults to 1024.
58  * <LI> <CODE>MinValue</CODE> -
59  * The starting number of Object ID allocation. This will only be used
60  * if the Object ID table is empty and thus is useful in development
61  * and testing. Optional, if not specified it defaults to
62  * 100000000000000000. Note that the largest number that can be
63  * associated with an OID in LBS is "database: DECIMAL(19,0)"
64  * </UL>
65  *
66  * @author Kyle Clark
67  * @author Paul Morgan
68  * @since LBS1.8
69  * @version $Revision: 1.7 $
70  */

71 public class StandardObjectIdAllocator implements ObjectIdAllocator {
72     
73     /**
74      * The maximum object id in the cache.
75      */

76     private ObjectId max = null;
77         
78     /**
79      * The object id mos recently allocated.
80      */

81     private ObjectId current = null;
82         
83     /**
84      * The next object id that will be allocated.
85      */

86     private ObjectId next = null;
87         
88     /**
89      * Default cache size if 1K. Note that if the
90      * application is started and stopped frequently
91      * you should drop this value in order not
92      * to waste object ids.
93      */

94     private long cacheSize = 1024;
95
96    /**
97      * Used in updateCache method in update statement because
98      * some databases has NEXT as a reserved word (HSQL)
99      */

100     private boolean columnWithPrefix = false;
101                 
102
103    /**
104      * Name of objectid table in database
105      */

106     private String JavaDoc oidTableName = "objectid";
107
108    /**
109      * Name of next column in objectid table
110      */

111     private String JavaDoc nextColumnName = "next";
112     /**
113      * Starting value for object id creation, if table is empty.
114      * This is a string because very large numbers are possible.
115      */

116     private String JavaDoc oidMinValue = "0";
117         
118     /**
119      * Reference to the logical database for easy access to the
120      * connection pool.
121      */

122     private LogicalDatabase logicalDatabase = null;
123
124     
125     /**
126      * Name of primary logical database for access to the
127      * objectId table.
128      */

129     private String JavaDoc primaryLogicalDatabaseName = null;
130     
131     /**
132      * If allocation of object id's fails (because the contents of
133      * the table are modified by another process) this object
134      * id manager will keep trying to allocate object ids
135      * until it succeeds. If it fails more than LOG_THRESHOLD
136      * times, then it will log a message to the system.
137      */

138     private static final int LOG_THRESHOLD = 100;
139     
140     
141     /**
142      * Initialize the object id manager.
143      *
144      * @param logicalDatabase LogicalDatabase.
145      * @param objIdConfig LogicalDatabase Config object.
146      * @exception ConfigException if bad configuration information is
147      * given in the config file.
148      */

149     protected StandardObjectIdAllocator(LogicalDatabase lDb, Config objIdConfig)
150         throws ConfigException {
151         try {
152             primaryLogicalDatabaseName = objIdConfig.getString("PrimaryLogicalDatabase", lDb.getName());
153             cacheSize = objIdConfig.getInt("CacheSize", 1024);
154             oidMinValue = objIdConfig.getString("MinValue", "10000000000000000");
155             columnWithPrefix = objIdConfig.getBoolean("NextWithPrefix",false);
156             oidTableName = objIdConfig.getString("OidTableName","objectid");
157             nextColumnName = objIdConfig.getString("NextColumnName","next");
158         } catch (KeywordValueException except) {
159             throw new ConfigException("Bad DatabaseManager.DB."
160                     + primaryLogicalDatabaseName
161                     + ".ObjectId section defined in config file.");
162         }
163     }
164     
165     /**
166      * Check if oid belongs to Object id's range [minOId, currentOId]
167      *
168      * @param oid
169      * oid which will be checked.
170      * @exception ObjectIdException
171      * If a oid does't belong to range.
172      */

173     public synchronized void checkOId(ObjectId oid) throws ObjectIdException {
174         if (oid == null) {
175             throw new ObjectIdException("Object id is null");
176         }
177         BigDecimal JavaDoc min = new BigDecimal JavaDoc(new BigInteger JavaDoc(oidMinValue));
178         BigDecimal JavaDoc id = new BigDecimal JavaDoc(oid.toString());
179
180         if (id.compareTo(min) == -1) {
181             throw new ObjectIdException("Object id out of range");
182         }
183         if (next == null || max == null) {
184             updateCache();
185         }
186         BigDecimal JavaDoc bdNext = new BigDecimal JavaDoc(next.toString());
187
188         if (id.compareTo(bdNext) == 0 || id.compareTo(bdNext) == 1) {
189             throw new ObjectIdException("Object id out of range");
190         }
191     }
192     
193     /**
194      * Allocates a new object id.
195      */

196     public synchronized ObjectId allocate() {
197         try {
198             if (next == null || max == null || next.equals(max)) {
199                 updateCache();
200             }
201             current = next; // next free
202
next = next.increment();
203             return current;
204         } catch (Exception JavaDoc e) {
205             throw new ObjectIdAllocationError("ObjectIdAllocator: "
206                     + "\nFailed to allocate object id. Caused by: " + "\n"
207                     + e.getMessage());
208         }
209     }
210     
211     /**
212      * Updates the cache of object id's.
213      *
214      * contains WebDocWf bug fix, concurrent applications should be allowed to generate oid's
215      */

216     private void updateCache() {
217         final String JavaDoc OID_TABLE = oidTableName;
218         DBConnection conn = null;
219
220         try {
221             boolean tryAgain = false;
222             int tryCount = 0;
223
224             conn = allocateConnection();
225             conn.setAutoCommit(false);
226             do {
227                 BigDecimal JavaDoc dbNext;
228                 ResultSet JavaDoc rs;
229
230                 // Query next available object id from database.
231
rs = conn.executeQuery("select * from " + OID_TABLE);
232                 if (!rs.next()) {
233                     // Table is empty - initialize
234
conn.executeUpdate("insert into " + OID_TABLE + " values("
235                             + oidMinValue + ")");
236                     dbNext = new BigDecimal JavaDoc(oidMinValue);
237                 } else {
238                     dbNext = rs.getBigDecimal(nextColumnName);
239                 }
240                 rs.close(); // In case there's more // JOHN
241
// Sync this managers next oid w/ current value in db.
242
next = new ObjectId(dbNext);
243                 // Update db contents
244
// i.e. Allocate a block of oids of size cacheSize.
245
BigDecimal JavaDoc dbLast = dbNext;
246
247                 dbNext = dbNext.add(BigDecimal.valueOf(cacheSize));
248                 // This now updates all rows if there's more than one.
249
String JavaDoc sql = "update " + OID_TABLE + " "; // compliance with WEBDOCWF begin
250
// WebDocWf bug fix, concurrent applications should be allowed to generate oid's
251
// the "+ "where next = ?"" has been under comments before
252
if(!columnWithPrefix)
253                         sql+= "set " +nextColumnName +" = ?" + " " + "where "+nextColumnName +"= ?";
254                 else
255                         sql+= "set " + OID_TABLE + "."+nextColumnName+" = ?" + " " + "where " + OID_TABLE + "."+nextColumnName+" = ?";
256                 // end of first part of bugfix
257
// original line
258
// compliance with WEBDOCWF end
259
PreparedStatement JavaDoc stmt = conn.prepareStatement(sql);
260
261                 stmt.setBigDecimal(1, dbNext);
262                 // compliance with WEBDOCWF begin
263
// WebDocWf bug fix, concurrent applications should be allowed to generate oid's
264
// The following line has been under comments before:
265
stmt.setBigDecimal(2, dbLast);
266                 // end of second part of bugfix
267
// original line
268
// compliance with WEBDOCWF end
269
if (conn.executeUpdate(stmt, sql) < 1) {
270                     // We were unable to update the table.
271
tryAgain = true;
272                     tryCount++;
273                     if (tryCount >= LOG_THRESHOLD) {
274                               DODS.getLogChannel().write(Logger.EMERGENCY,
275                                 "ObjectIdAllocator: "
276                                 + "\n Failed to allocate object ids."
277                                 + "\n Trying again....");
278                     }
279                     // Transaction rollback
280
conn.rollback();
281                     conn.reset(); // free resources
282
conn.setAutoCommit(false);
283                     if (tryCount >= 50) {
284                               DODS.getLogChannel().write(Logger.EMERGENCY,
285                                 "ObjectIdAllocator: "
286                                 + "\n Failed to allocate object ids."
287                                 + "\n Tried 50 times. Giving up.");
288                         throw new ObjectIdAllocationError("Failed to allocate object id."
289                                 + "\nTried 50 times. Giving up.");
290                     }
291                 } else {
292                     tryAgain = false;
293                     // Sync this managers max oid w/ db contents.
294
max = new ObjectId(dbNext);
295                     // Transcation commit.
296
conn.commit();
297                     if (tryCount >= LOG_THRESHOLD) {
298                                DODS.getLogChannel().write(Logger.EMERGENCY,
299                                 "ObjectIdAllocator: "
300                                 + "\n Succeeded in allocating object ids."
301                                 + "\n Continuing...");
302                     }
303                 }
304             } while (tryAgain);
305         } catch (SQLException JavaDoc sqlExcept) {
306             // An error occured. Rollback all changes.
307
max = next; // The cache is not updated.
308
DODS.getLogChannel().write(Logger.EMERGENCY,
309                     "ObjectIdAllocator: "
310                     + "\n Failed to allocate object ids. Giving up!"
311                     + "\n SQLState = " + sqlExcept.getSQLState()
312                     + "\n SQLError = " + sqlExcept.getErrorCode()
313                     + "\n SQLMsg = " + sqlExcept.getMessage());
314             if (conn != null) {
315                 try {
316                     conn.rollback();
317                 } catch (SQLException JavaDoc rollbackExcept) {
318                          DODS.getLogChannel().write(Logger.EMERGENCY,
319                             "ObjectIdAllocator: "
320                             + "\n Failed to rollback transaction."
321                             + "\n SQLState = " + rollbackExcept.getSQLState()
322                             + "\n SQLError = "
323                             + rollbackExcept.getErrorCode()
324                             + "\n SQLMsg = " + rollbackExcept.getMessage());
325                 }
326                 if (!conn.handleException(sqlExcept)) {
327                     conn = null;
328                 }
329             }
330             throw new ObjectIdAllocationError("ObjectIdAllocator: "
331                     + "\n Failed to allocate object id. Caused by SQLException:"
332                     + "\n SQLState = " + sqlExcept.getSQLState()
333                     + "\n SQLError = " + sqlExcept.getErrorCode()
334                     + "\n SQLMsg = " + sqlExcept.getMessage());
335         } catch (ObjectIdException oidEx) {
336             throw new ObjectIdAllocationError("ObjectIdAllocator: "
337                     + "\nFailed to allocate object id. Caused by: " + "\n"
338                     + oidEx.getMessage());
339         }
340         finally {
341             if (conn != null) {
342                 try {
343                     conn.reset(); // free existing resource if any
344
} catch (SQLException JavaDoc sqlExcept) {
345                          DODS.getLogChannel().write(Logger.EMERGENCY,
346                             "ObjectIdAllocator: "
347                             + "\n Failed to reset connection."
348                             + "\n SQLState = " + sqlExcept.getSQLState()
349                             + "\n SQLError = " + sqlExcept.getErrorCode()
350                             + "\n SQLMsg = " + sqlExcept.getMessage());
351                 }
352                 finally {
353                     conn.release();
354                 }
355             }
356         }
357     }
358
359     private DBConnection allocateConnection() throws SQLException JavaDoc {
360         if (logicalDatabase == null) {
361             try {
362                 logicalDatabase = DODS.getDatabaseManager().findLogicalDatabase(primaryLogicalDatabaseName);
363             } catch (DatabaseManagerException e) {
364                 e.printStackTrace();
365                 throw new SQLException JavaDoc(e.getMessage());
366             }
367         }
368         return logicalDatabase.allocateConnection();
369     }
370 }
371
Popular Tags