1 24 package org.ofbiz.entity.util; 25 26 import java.sql.Connection ; 27 import java.sql.ResultSet ; 28 import java.sql.SQLException ; 29 import java.sql.Statement ; 30 import java.util.Hashtable ; 31 import java.util.Map ; 32 33 import javax.transaction.Transaction ; 34 35 import org.ofbiz.base.util.Debug; 36 import org.ofbiz.entity.GenericEntityException; 37 import org.ofbiz.entity.jdbc.ConnectionFactory; 38 import org.ofbiz.entity.model.ModelEntity; 39 import org.ofbiz.entity.model.ModelField; 40 import org.ofbiz.entity.transaction.GenericTransactionException; 41 import org.ofbiz.entity.transaction.TransactionUtil; 42 43 51 public class SequenceUtil { 52 53 public static final String module = SequenceUtil.class.getName(); 54 55 Map sequences = new Hashtable (); 56 String helperName; 57 ModelEntity seqEntity; 58 String tableName; 59 String nameColName; 60 String idColName; 61 62 private SequenceUtil() {} 63 64 public SequenceUtil(String helperName, ModelEntity seqEntity, String nameFieldName, String idFieldName) { 65 this.helperName = helperName; 66 this.seqEntity = seqEntity; 67 if (seqEntity == null) { 68 throw new IllegalArgumentException ("The sequence model entity was null but is required."); 69 } 70 this.tableName = seqEntity.getTableName(helperName); 71 72 ModelField nameField = seqEntity.getField(nameFieldName); 73 74 if (nameField == null) { 75 throw new IllegalArgumentException ("Could not find the field definition for the sequence name field " + nameFieldName); 76 } 77 this.nameColName = nameField.getColName(); 78 79 ModelField idField = seqEntity.getField(idFieldName); 80 81 if (idField == null) { 82 throw new IllegalArgumentException ("Could not find the field definition for the sequence id field " + idFieldName); 83 } 84 this.idColName = idField.getColName(); 85 } 86 87 public Long getNextSeqId(String seqName, long staggerMax) { 88 SequenceBank bank = (SequenceBank) sequences.get(seqName); 89 90 if (bank == null) { 91 synchronized(this) { 92 bank = (SequenceBank) sequences.get(seqName); 93 if (bank == null) { 94 bank = new SequenceBank(seqName, this); 95 sequences.put(seqName, bank); 96 } 97 } 98 } 99 return bank.getNextSeqId(staggerMax); 100 } 101 102 class SequenceBank { 103 104 public static final long defaultBankSize = 10; 105 public static final long startSeqId = 10000; 106 public static final int minWaitMillis = 5; 107 public static final int maxWaitMillis = 50; 108 public static final int maxTries = 5; 109 110 long curSeqId; 111 long maxSeqId; 112 String seqName; 113 SequenceUtil parentUtil; 114 115 public SequenceBank(String seqName, SequenceUtil parentUtil) { 116 this.seqName = seqName; 117 this.parentUtil = parentUtil; 118 curSeqId = 0; 119 maxSeqId = 0; 120 fillBank(1); 121 } 122 123 public synchronized Long getNextSeqId(long staggerMax) { 124 long stagger = 1; 125 if (staggerMax > 1) { 126 stagger = Math.round(Math.random() * staggerMax); 127 if (stagger == 0) stagger = 1; 128 } 129 130 if ((curSeqId + stagger) <= maxSeqId) { 131 Long retSeqId = new Long (curSeqId); 132 curSeqId += stagger; 133 return retSeqId; 134 } else { 135 fillBank(stagger); 136 if ((curSeqId + stagger) <= maxSeqId) { 137 Long retSeqId = new Long (curSeqId); 138 curSeqId += stagger; 139 return retSeqId; 140 } else { 141 Debug.logError("[SequenceUtil.SequenceBank.getNextSeqId] Fill bank failed, returning null", module); 142 return null; 143 } 144 } 145 } 146 147 protected synchronized void fillBank(long stagger) { 148 150 long bankSize = defaultBankSize; 151 if (stagger > 1) { 152 bankSize = stagger * defaultBankSize; 154 } 155 156 if ((curSeqId + stagger) <= maxSeqId) return; 158 159 long val1 = 0; 160 long val2 = 0; 161 162 Transaction suspendedTransaction = null; 164 try { 165 suspendedTransaction = TransactionUtil.suspend(); 167 168 boolean beganTransaction = false; 169 try { 170 beganTransaction = TransactionUtil.begin(); 171 172 Connection connection = null; 173 Statement stmt = null; 174 ResultSet rs = null; 175 176 try { 177 connection = ConnectionFactory.getConnection(parentUtil.helperName); 178 } catch (SQLException sqle) { 179 Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database... Error was:" + sqle.toString(), module); 180 throw sqle; 181 } catch (GenericEntityException e) { 182 Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database... Error was: " + e.toString(), module); 183 throw e; 184 } 185 186 if (connection == null) { 187 throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database, connection was null..."); 188 } 189 190 String sql = null; 191 192 try { 193 195 stmt = connection.createStatement(); 196 int numTries = 0; 197 198 while (val1 + bankSize != val2) { 199 if (Debug.verboseOn()) Debug.logVerbose("[SequenceUtil.SequenceBank.fillBank] Trying to get a bank of sequenced ids for " + 200 this.seqName + "; start of loop val1=" + val1 + ", val2=" + val2 + ", bankSize=" + bankSize, module); 201 202 sql = "SELECT " + parentUtil.idColName + " FROM " + parentUtil.tableName + " WHERE " + parentUtil.nameColName + "='" + this.seqName + "'"; 203 rs = stmt.executeQuery(sql); 204 boolean gotVal1 = false; 205 if (rs.next()) { 206 val1 = rs.getLong(parentUtil.idColName); 207 gotVal1 = true; 208 } 209 rs.close(); 210 211 if (!gotVal1) { 212 Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] first select failed: will try to add new row, result set was empty for sequence [" + seqName + "] \nUsed SQL: " + sql + " \n Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module); 213 sql = "INSERT INTO " + parentUtil.tableName + " (" + parentUtil.nameColName + ", " + parentUtil.idColName + ") VALUES ('" + this.seqName + "', " + startSeqId + ")"; 214 if (stmt.executeUpdate(sql) <= 0) { 215 throw new GenericEntityException("No rows changed when trying insert new sequence row with this SQL: " + sql); 216 } 217 continue; 218 } 219 220 sql = "UPDATE " + parentUtil.tableName + " SET " + parentUtil.idColName + "=" + parentUtil.idColName + "+" + bankSize + " WHERE " + parentUtil.nameColName + "='" + this.seqName + "'"; 221 if (stmt.executeUpdate(sql) <= 0) { 222 throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] update failed, no rows changes for seqName: " + seqName); 223 } 224 225 sql = "SELECT " + parentUtil.idColName + " FROM " + parentUtil.tableName + " WHERE " + parentUtil.nameColName + "='" + this.seqName + "'"; 226 rs = stmt.executeQuery(sql); 227 boolean gotVal2 = false; 228 if (rs.next()) { 229 val2 = rs.getLong(parentUtil.idColName); 230 gotVal2 = true; 231 } 232 233 rs.close(); 234 235 if (!gotVal2) { 236 throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] second select failed: aborting, result " + "set was empty for sequence: " + seqName); 237 238 } 239 240 if (val1 + bankSize != val2) { 241 if (numTries >= maxTries) { 242 throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] maxTries (" + maxTries + ") reached, giving up."); 243 } 244 245 int waitTime = (new Double (Math.random() * (maxWaitMillis - minWaitMillis))).intValue() + minWaitMillis; 247 248 Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Collision found for seqName [" + seqName + "], val1=" + val1 + ", val2=" + val2 + ", val1+bankSize=" + (val1 + bankSize) + ", bankSize=" + bankSize + ", waitTime=" + waitTime, module); 249 250 try { 251 this.wait(waitTime); 252 } catch (Exception e) { 253 Debug.logWarning(e, "Error waiting in sequence util", module); 254 throw e; 255 } 256 } 257 258 numTries++; 259 } 260 261 curSeqId = val1; 262 maxSeqId = val2; 263 if (Debug.infoOn()) Debug.logInfo("Got bank of sequenced IDs for [" + this.seqName + "]; curSeqId=" + curSeqId + ", maxSeqId=" + maxSeqId + ", bankSize=" + bankSize, module); 264 } catch (SQLException sqle) { 265 Debug.logWarning(sqle, "[SequenceUtil.SequenceBank.fillBank] SQL Exception while executing the following:\n" + sql + "\nError was:" + sqle.getMessage(), module); 266 throw sqle; 267 } finally { 268 try { 269 if (stmt != null) stmt.close(); 270 } catch (SQLException sqle) { 271 Debug.logWarning(sqle, "Error closing statement in sequence util", module); 272 } 273 try { 274 if (connection != null) connection.close(); 275 } catch (SQLException sqle) { 276 Debug.logWarning(sqle, "Error closing connection in sequence util", module); 277 } 278 } 279 } catch (Exception e) { 280 String errMsg = "General error in getting a sequenced ID"; 281 Debug.logError(e, errMsg, module); 282 try { 283 TransactionUtil.rollback(beganTransaction, errMsg, e); 284 } catch (GenericTransactionException gte2) { 285 Debug.logError(gte2, "Unable to rollback transaction", module); 286 } 287 } finally { 288 try { 289 TransactionUtil.commit(beganTransaction); 290 } catch (GenericTransactionException gte) { 291 Debug.logError(gte, "Unable to commit transaction", module); 292 } 293 } 294 } catch (GenericTransactionException e) { 295 Debug.logError(e, "System Error suspending transaction in sequence util", module); 296 } finally { 297 if (suspendedTransaction != null) { 298 try { 299 TransactionUtil.resume(suspendedTransaction); 300 } catch (GenericTransactionException e) { 301 Debug.logError(e, "Error resuming suspended transaction in sequence util", module); 302 } 303 } 304 } 305 } 307 } 308 } 309 | Popular Tags |