001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.keygenerator.hilo;
023:
024: import org.jboss.ejb.plugins.keygenerator.KeyGenerator;
025: import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil;
026: import org.jboss.logging.Logger;
027:
028: import javax.sql.DataSource;
029: import javax.transaction.TransactionManager;
030: import javax.transaction.Transaction;
031: import javax.transaction.SystemException;
032: import java.sql.PreparedStatement;
033: import java.sql.Connection;
034: import java.sql.SQLException;
035: import java.sql.ResultSet;
036:
037: /**
038: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
039: * @version <tt>$Revision: 57209 $</tt>
040: */
041: public class HiLoKeyGenerator implements KeyGenerator {
042: private static long highestHi = 0;
043:
044: public static synchronized long getHighestHi() {
045: return highestHi;
046: }
047:
048: public static synchronized void setHighestHi(long highestHi) {
049: HiLoKeyGenerator.highestHi = highestHi;
050: }
051:
052: private final Logger log;
053: private final DataSource ds;
054: private final long blockSize;
055:
056: private long hi;
057: private long lo;
058:
059: private TransactionManager tm;
060: private String updateHiSql;
061: private String selectHiSql;
062:
063: public HiLoKeyGenerator(DataSource ds, String tableName,
064: String sequenceColumn, String sequenceName,
065: String idColumnName, String selectHiSql, long blockSize,
066: TransactionManager tm) {
067: this .ds = ds;
068: this .blockSize = blockSize;
069: this .tm = tm;
070: this .log = Logger.getLogger(getClass().getName() + "#"
071: + tableName + "_" + sequenceName);
072:
073: updateHiSql = "update " + tableName + " set " + idColumnName
074: + "=?" + " where " + sequenceColumn + "='"
075: + sequenceName + "' and " + idColumnName + "=?";
076:
077: this .selectHiSql = selectHiSql;
078: }
079:
080: public synchronized Object generateKey() {
081: if (lo < hi) {
082: ++lo;
083: } else {
084: Transaction curTx = null;
085: try {
086: curTx = tm.suspend();
087: } catch (SystemException e) {
088: throw new IllegalStateException(
089: "Failed to suspend current transaction.");
090: }
091:
092: try {
093: tm.begin();
094: } catch (Exception e) {
095: throw new IllegalStateException(
096: "Failed to begin a new transaction.");
097: }
098:
099: try {
100: doGenerate();
101: tm.commit();
102: } catch (SQLException e) {
103: log.error("Failed to update table: " + e.getMessage(),
104: e);
105:
106: try {
107: tm.rollback();
108: } catch (SystemException e1) {
109: log.error("Failed to rollback.", e1);
110: }
111:
112: throw new IllegalStateException(e.getMessage());
113: } catch (Exception e) {
114: log.error("Failed to commit.", e);
115: } finally {
116: if (curTx != null) {
117: try {
118: tm.resume(curTx);
119: } catch (Exception e) {
120: throw new IllegalStateException(
121: "Failed to resume transaction: "
122: + e.getMessage());
123: }
124: }
125: }
126: }
127:
128: return new Long(lo);
129: }
130:
131: private void doGenerate() throws SQLException {
132: long curHi;
133: do {
134: curHi = getCurrentHi();
135: lo = curHi + 1;
136: hi = curHi + blockSize;
137: } while (!updateHi(curHi, hi));
138: }
139:
140: private long getCurrentHi() throws SQLException {
141: return selectHiSql != null ? selectHi() : getHighestHi();
142: }
143:
144: private boolean updateHi(long curHi, long newHi)
145: throws SQLException {
146: if (selectHiSql == null) {
147: setHighestHi(newHi);
148: }
149: return updateTable(curHi, newHi);
150: }
151:
152: private long selectHi() throws SQLException {
153: Connection con = null;
154: PreparedStatement selectHiSt = null;
155: ResultSet rs = null;
156:
157: if (log.isTraceEnabled()) {
158: log.trace("Executing SQL: " + selectHiSql);
159: }
160:
161: try {
162: con = ds.getConnection();
163: selectHiSt = con.prepareStatement(selectHiSql);
164: rs = selectHiSt.executeQuery();
165: if (!rs.next()) {
166: throw new IllegalStateException(
167: "The sequence has not been initialized in the service start phase!");
168: }
169: return rs.getLong(1);
170: } finally {
171: JDBCUtil.safeClose(rs);
172: JDBCUtil.safeClose(selectHiSt);
173: JDBCUtil.safeClose(con);
174: }
175: }
176:
177: private boolean updateTable(long curHi, long newHi)
178: throws SQLException {
179: Connection con = null;
180: PreparedStatement updateHi = null;
181:
182: if (log.isTraceEnabled()) {
183: log.trace("Executing SQL: " + updateHiSql + ", [" + newHi
184: + "," + curHi + "]");
185: }
186:
187: try {
188: con = ds.getConnection();
189: updateHi = con.prepareStatement(updateHiSql);
190: updateHi.setLong(1, newHi);
191: updateHi.setLong(2, curHi);
192: return updateHi.executeUpdate() == 1;
193: } finally {
194: JDBCUtil.safeClose(updateHi);
195: JDBCUtil.safeClose(con);
196: }
197: }
198: }
|