Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/lutris/appserver/server/sql/standard/StandardObjectIdAllocator.java


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   * Contributor(s):
20   * 
21   * $Id: StandardObjectIdAllocator.java,v 1.7.12.1 2000/10/19 17:59:06 jasona Exp $
22   */
23  
24  
25  
26  
27  
28  package  com.lutris.appserver.server.sql.standard;
29  
30  import com.lutris.appserver.server.sql.*;
31  import com.lutris.appserver.server.LBS;
32  import java.util.Hashtable;
33  import java.math.BigDecimal;
34  import java.sql.*;
35  import com.lutris.logging.*;
36  import com.lutris.util.*;
37  
38  
39  /**
40   * Object ids can only be created via this manager.
41   * Ensures that all object ids are unique across all
42   * objects in this logical database. Also ensures good performance
43   * for allocating object ids.
44   * <P>
45   * The configuration data is specified in the section:
46   * <CODE>DatabaseManager.DB.<I>dbName</I>.ObjectId</CODE>
47   * <P>
48   * Configuration fields are:
49   * <UL>
50   * <LI> <CODE>CacheSize</CODE> -
51   *      The number of object id's to cache between database queries. Optional,
52   *  if not specified, then it defaults to 1024.
53   * <LI> <CODE>MinValue</CODE> -
54   *      The starting number of Object ID allocation. This will only be used
55   *  if the Object ID table is empty and thus is useful in development
56   *  and testing. Optional, if not specified it defaults to
57   *  100000000000000000. Note that the largest number that can be
58   *  associated with an OID in LBS is "database: DECIMAL(19,0)"
59   * </UL>
60   *
61   * @author  Kyle Clark
62   * @author  Paul Morgan
63   * @since  LBS1.8
64   * @version  $Revision: 1.7.12.1 $
65   */
66  public class StandardObjectIdAllocator implements ObjectIdAllocator {
67  
68      /**
69       * The maximum object id in the cache.
70       */
71      private ObjectId max = null;
72  
73      /**
74       * The object id mos recently allocated.
75       */
76      private ObjectId current = null;
77  
78      /**
79       * The next object id that will be allocated.
80       */
81      private ObjectId next = null;
82  
83      /**
84       * Default cache size if 1K.  Note that if the
85       * application is started and stopped frequently
86       * you should drop this value in order not
87       * to waste object ids.
88       */
89      private long cacheSize = 1024;
90  
91      /**
92       * Starting value for object id creation, if table is empty.
93       * This is a string because very large numbers are possible.
94       */
95      private String oidMinValue = "0";
96  
97      /**
98       * Reference to the logical database for easy access to the
99       * connection pool.
100      */
101     private LogicalDatabase logicalDatabase = null;
102 
103     /**
104      * If allocation of object id's fails (because the contents of
105      * the table are modified by another process) this object
106      * id manager will keep trying to allocate object ids
107      * until it succeeds.  If it fails more than LOG_THRESHOLD
108      * times, then it will log a message to the system.
109      */
110     private static final int LOG_THRESHOLD = 100;
111 
112     /**
113      * The log channel.
114      */
115     private LogChannel log = LBS.getLogChannel();
116 
117 
118     /**
119      * Initialize the object id manager.
120      *
121      * @param objCacheSize The number of object ids to cache between
122      *  database queries.
123      * @param objIdMinValue The starting number for object ids. Used
124      *  only if object id table is empty.
125      * @exception ConfigException if bad configuration information is
126      *  given in the config file.
127      */
128     protected StandardObjectIdAllocator (LogicalDatabase logicalDatabase,
129                                       Config objIdConfig)
130   throws ConfigException
131     {
132   this.logicalDatabase = logicalDatabase;
133 
134   try {
135       cacheSize = objIdConfig.getInt ("CacheSize", 1024);
136       oidMinValue = objIdConfig.getString ("MinValue", "10000000000000000");
137   } catch (KeywordValueException except) {
138       throw new ConfigException ("Bad DatabaseManager.DB." + logicalDatabase.getName() + ".ObjectId section defined in config file.");
139   }
140     }
141 
142 
143     /**
144      * Allocates a new object id.
145      */
146     public synchronized ObjectId allocate () {
147 
148         try {
149             if (next == null || max == null || next.equals(max)) {
150                 updateCache();
151             }
152             current = next;
153             next = next.increment();
154             return current;
155         } catch (Exception e) {
156             throw new ObjectIdAllocationError(
157                  "ObjectIdAllocator: " +
158                  "\nFailed to allocate object id.  Caused by: " +
159                  "\n" + e.getMessage());
160         }
161     }
162 
163 
164     /**
165      * Updates the cache of object id's.
166      */
167     private void updateCache() {
168 
169         final String OID_TABLE = "objectid";
170         DBConnection conn = null;
171 
172         try {
173             boolean tryAgain = false;
174             int tryCount = 0;
175 
176             conn = logicalDatabase.allocateConnection();
177             conn.setAutoCommit(false);
178 
179             do {
180                 BigDecimal dbNext;
181                 ResultSet rs;
182 
183                 // Query next available object id from database.
184                 rs = conn.executeQuery("select * from " + OID_TABLE);
185                 if (!rs.next()) {
186                     // Table is empty - initialize
187                     conn.executeUpdate("insert into " + OID_TABLE +
188                                        " values(" + oidMinValue + ")");
189                     dbNext = new BigDecimal(oidMinValue);
190                 }
191                 else {
192                     dbNext = rs.getBigDecimal("next", 0);
193                 }
194     rs.close(); // In case there's more // JOHN
195 
196                 // Sync this managers next oid w/ current value in db.
197                 next = new ObjectId(dbNext); 
198 
199                 // Update db contents
200                 // i.e. Allocate a block of oids of size cacheSize.
201                 BigDecimal dbLast = dbNext;
202                 dbNext = dbNext.add(BigDecimal.valueOf(cacheSize));
203     // This now updates all rows if there's more than one.
204                 String sql =
205                     "update " + OID_TABLE + " \n" +
206                     "set next = ?";
207                     //"where next = ?";
208                 PreparedStatement stmt = conn.prepareStatement(sql);
209                 stmt.setBigDecimal(1, dbNext);
210                 //stmt.setBigDecimal(2, dbLast);
211                 if (conn.executeUpdate(stmt, sql) < 1) {
212                     // We were unable to update the table.
213                     tryAgain = true;
214                     tryCount++;
215                     if (tryCount >= LOG_THRESHOLD) {
216                         log.write(Logger.ALERT,
217                                   "ObjectIdAllocator: " +
218                                   "\n  Failed to allocate object ids." +
219                                   "\n  Trying again....");
220                     }
221                     // Transaction rollback
222                     conn.rollback();
223                     conn.reset();  // free resources
224                     //conn.setLockModeToWait(true); 
225                     conn.setAutoCommit(false);
226         if (tryCount >= 50) {
227                         log.write(Logger.ALERT, 
228                                   "ObjectIdAllocator: " +
229                                   "\n  Failed to allocate object ids." +
230                                   "\n  Tried 50 times.  Giving up.");
231                         throw new ObjectIdAllocationError("Failed to allocate object id." +
232                                                           "\nTried 50 times.  Giving up.");
233                     }
234                 }
235                 else {
236                     tryAgain = false;
237 
238                     // Sync this managers max oid w/ db contents.
239                     max = new ObjectId(dbNext);
240                     // Transcation commit.
241                     conn.commit();
242 
243                     if (tryCount >= LOG_THRESHOLD) {
244                         log.write(Logger.ALERT,
245                                   "ObjectIdAllocator: " +
246                                   "\n  Succeeded in allocating object ids." +
247                                   "\n  Continuing...");
248                     }
249                 }
250             } while (tryAgain);
251         } catch (SQLException sqlExcept) {
252             // An error occured.  Rollback all changes.
253             max = next; // The cache is not updated.
254 
255             log.write(Logger.ALERT, 
256                  "ObjectIdAllocator: " +
257                  "\n  Failed to allocate object ids.  Giving up!" +
258                  "\n    SQLState = " + sqlExcept.getSQLState() +
259                  "\n    SQLError = " + sqlExcept.getErrorCode() +
260                  "\n    SQLMsg   = " + sqlExcept.getMessage());
261 
262             if (conn != null) {
263                 try {
264                     conn.rollback();
265                 } catch (SQLException rollbackExcept) {
266                     log.write(Logger.ALERT, 
267                          "ObjectIdAllocator: " +
268                          "\n  Failed to rollback transaction." +
269                          "\n    SQLState = " + rollbackExcept.getSQLState() +
270                          "\n    SQLError = " + rollbackExcept.getErrorCode() +
271                          "\n    SQLMsg   = " + rollbackExcept.getMessage());
272                 }
273                 if (!conn.handleException(sqlExcept))
274                     conn = null;
275             }
276             throw new ObjectIdAllocationError(
277                  "ObjectIdAllocator: " +
278                  "\n  Failed to allocate object id.  Caused by SQLException:" +
279                  "\n    SQLState = " + sqlExcept.getSQLState() +
280                  "\n    SQLError = " + sqlExcept.getErrorCode() +
281                  "\n    SQLMsg   = " + sqlExcept.getMessage());
282 
283         } catch (ObjectIdException oidEx) {
284             throw new ObjectIdAllocationError(
285                  "ObjectIdAllocator: " +
286                  "\nFailed to allocate object id.  Caused by: " +
287                  "\n" + oidEx.getMessage());
288         } finally {
289             if (conn != null) {
290                 try {
291                     conn.reset(); // free existing resource if any
292                 } catch (SQLException sqlExcept) {
293                     log.write(Logger.ALERT, 
294                          "ObjectIdAllocator: " +
295                          "\n  Failed to reset connection." +
296                          "\n    SQLState = " + sqlExcept.getSQLState() +
297                          "\n    SQLError = " + sqlExcept.getErrorCode() +
298                          "\n    SQLMsg   = " + sqlExcept.getMessage());
299                 } finally {
300                     conn.release();
301                 }
302             }
303         }
304     }
305 }