// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.utils.db; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.persistence.TableGenerator; import org.apache.log4j.Logger; import com.cloud.utils.concurrency.NamedThreadFactory; /** * Since Mysql does not have sequence support, we have * table retrieval was inside a transaction, the value * gets locked until the transaction is over. * * allocation size. * */ public class SequenceFetcher { private final static Logger s_logger = Logger.getLogger(SequenceFetcher.class); ExecutorService _executors; private final static Random random = new Random(); public <T> T getNextSequence(Class<T> clazz, TableGenerator tg) { return getNextSequence(clazz, tg, null, false); } public <T> T getNextSequence(Class<T> clazz, TableGenerator tg, Object key) { return getNextSequence(clazz, tg, key, false); } public <T> T getRandomNextSequence(Class<T> clazz, TableGenerator tg) { return getNextSequence(clazz, tg, null, true); } public <T> T getNextSequence(Class<T> clazz, TableGenerator tg, Object key, boolean isRandom) { Future<T> future = _executors.submit(new Fetcher<T>(clazz, tg, key, isRandom)); try { return future.get(); } catch (Exception e) { s_logger.warn("Unable to get sequeunce for " + tg.table() + ":" + tg.pkColumnValue(), e); return null; } } protected SequenceFetcher() { _executors = new ThreadPoolExecutor(100, 100, 120l, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(250), new NamedThreadFactory("SequenceFetcher")); } protected static final SequenceFetcher s_instance = new SequenceFetcher(); public static SequenceFetcher getInstance() { return s_instance; } protected class Fetcher<T> implements Callable<T> { TableGenerator _tg; Class<T> _clazz; Object _key; boolean isRandom = false; protected Fetcher(Class<T> clazz, TableGenerator tg, Object key, boolean isRandom) { _tg = tg; _clazz = clazz; _key = key; this.isRandom = isRandom; } @Override @SuppressWarnings("unchecked") public T call() throws Exception { try { PreparedStatement stmt = null; StringBuilder sql = new StringBuilder("SELECT "); sql.append(_tg.valueColumnName()).append(" FROM ").append(_tg.table()); sql.append(" WHERE ").append(_tg.pkColumnName()).append(" = ? FOR UPDATE"); Transaction txn = Transaction.open("Sequence"); PreparedStatement selectStmt = txn.prepareStatement(sql.toString()); if (_key == null) { selectStmt.setString(1, _tg.pkColumnValue()); } else { selectStmt.setObject(1, _key); } sql = new StringBuilder("UPDATE "); sql.append(_tg.table()).append(" SET ").append(_tg.valueColumnName()).append("=").append("?+?"); sql.append(" WHERE ").append(_tg.pkColumnName()).append("=?"); PreparedStatement updateStmt = txn.prepareStatement(sql.toString()); if(isRandom){ updateStmt.setInt(2, random.nextInt(10) + 1); } else { updateStmt.setInt(2, _tg.allocationSize()); } if (_key == null) { updateStmt.setString(3, _tg.pkColumnValue()); } else { updateStmt.setObject(3, _key); } ResultSet rs = null; try { txn.start(); stmt = selectStmt; rs = stmt.executeQuery(); Object obj = null; while (rs.next()) { if (_clazz.isAssignableFrom(Long.class)) { obj = rs.getLong(1); } else if (_clazz.isAssignableFrom(Integer.class)) { obj = rs.getInt(1); } else { obj = rs.getObject(1); } } if (obj == null) { s_logger.warn("Unable to get a sequence: " + updateStmt.toString()); return null; } updateStmt.setObject(1, obj); stmt = updateStmt; int rows = stmt.executeUpdate(); assert rows == 1 : "Come on....how exactly did we update this many rows " + rows + " for " + updateStmt.toString(); txn.commit(); return (T)obj; } catch (SQLException e) { s_logger.warn("Caught this exception when running: " + (stmt != null ? stmt.toString() : ""), e); } finally { if (rs != null) { rs.close(); } selectStmt.close(); updateStmt.close(); txn.close(); } } catch (Exception e) { s_logger.warn("Caught this exception when running.", e); } return null; } } }