/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb.plugins.keygenerator.hilo; import org.jboss.ejb.plugins.keygenerator.KeyGenerator; import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil; import org.jboss.logging.Logger; import javax.sql.DataSource; import javax.transaction.TransactionManager; import javax.transaction.Transaction; import javax.transaction.SystemException; import java.sql.PreparedStatement; import java.sql.Connection; import java.sql.SQLException; import java.sql.ResultSet; /** * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a> * @version <tt>$Revision: 81030 $</tt> */ public class HiLoKeyGenerator implements KeyGenerator { private static long highestHi = 0; public static synchronized long getHighestHi() { return highestHi; } public static synchronized void setHighestHi(long highestHi) { HiLoKeyGenerator.highestHi = highestHi; } private final Logger log; private final DataSource ds; private final long blockSize; private long hi; private long lo; private TransactionManager tm; private String updateHiSql; private String selectHiSql; public HiLoKeyGenerator( DataSource ds, String tableName, String sequenceColumn, String sequenceName, String idColumnName, String selectHiSql, long blockSize, TransactionManager tm ) { this.ds = ds; this.blockSize = blockSize; this.tm = tm; this.log = Logger.getLogger(getClass().getName() + "#" + tableName + "_" + sequenceName); updateHiSql = "update " + tableName + " set " + idColumnName + "=?" + " where " + sequenceColumn + "='" + sequenceName + "' and " + idColumnName + "=?"; this.selectHiSql = selectHiSql; } public synchronized Object generateKey() { if(lo < hi) { ++lo; } else { Transaction curTx = null; try { curTx = tm.suspend(); } catch(SystemException e) { throw new IllegalStateException("Failed to suspend current transaction."); } try { tm.begin(); } catch(Exception e) { throw new IllegalStateException("Failed to begin a new transaction."); } try { doGenerate(); tm.commit(); } catch(SQLException e) { log.error("Failed to update table: " + e.getMessage(), e); try { tm.rollback(); } catch(SystemException e1) { log.error("Failed to rollback.", e1); } throw new IllegalStateException(e.getMessage()); } catch(Exception e) { log.error("Failed to commit.", e); } finally { if(curTx != null) { try { tm.resume(curTx); } catch(Exception e) { throw new IllegalStateException("Failed to resume transaction: " + e.getMessage()); } } } } return new Long(lo); } private void doGenerate() throws SQLException { long curHi; do { curHi = getCurrentHi(); lo = curHi + 1; hi = curHi + blockSize; } while(!updateHi(curHi, hi)); } private long getCurrentHi() throws SQLException { return selectHiSql != null ? selectHi() : getHighestHi(); } private boolean updateHi(long curHi, long newHi) throws SQLException { if(selectHiSql == null) { setHighestHi(newHi); } return updateTable(curHi, newHi); } private long selectHi() throws SQLException { Connection con = null; PreparedStatement selectHiSt = null; ResultSet rs = null; if(log.isTraceEnabled()) { log.trace("Executing SQL: " + selectHiSql); } try { con = ds.getConnection(); selectHiSt = con.prepareStatement(selectHiSql); rs = selectHiSt.executeQuery(); if(!rs.next()) { throw new IllegalStateException("The sequence has not been initialized in the service start phase!"); } return rs.getLong(1); } finally { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(selectHiSt); JDBCUtil.safeClose(con); } } private boolean updateTable(long curHi, long newHi) throws SQLException { Connection con = null; PreparedStatement updateHi = null; if(log.isTraceEnabled()) { log.trace("Executing SQL: " + updateHiSql + ", [" + newHi + "," + curHi + "]"); } try { con = ds.getConnection(); updateHi = con.prepareStatement(updateHiSql); updateHi.setLong(1, newHi); updateHi.setLong(2, curHi); return updateHi.executeUpdate() == 1; } finally { JDBCUtil.safeClose(updateHi); JDBCUtil.safeClose(con); } } }