package org.oddjob.sql; import java.io.InputStream; import java.sql.Blob; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.oddjob.arooa.registry.Path; import org.oddjob.persist.SerializeWithBinaryStream; import org.oddjob.persist.SerializeWithBytes; /** * An implementation {@link SQLSerializationFactory} for HSQLDB but * implemented in ANSI SQL so should work with most DBs. * <p> * This class is synchronised on the statements to avoid what appeared to be * deadlock in HSQL but no detailed investigation of this was * undertaken. * * @author rob * */ public class HSQLSerializationFactory implements SQLSerializationFactory { private String table; @Override public SQLSerialization createSerialization(Connection connection) throws SQLException { return new HSQLSerialization(connection, table); } public void setTable(String tableName) { this.table = tableName; } public String getTable() { return table; } } class HSQLSerialization implements SQLSerialization { private static final Logger logger = Logger.getLogger(HSQLSerialization.class); private final Connection connection; private final PreparedStatement updateStmt; private final PreparedStatement insertStmt; private final PreparedStatement selectStmt; private final PreparedStatement deleteStmt; private final PreparedStatement clearStmt; private final PreparedStatement listStmt; HSQLSerialization(Connection connection, String tableName) throws SQLException { this.connection = connection; String table = tableName; if (table == null) { table = "ODDJOB"; } try { String insertSQL = "insert into " + table + " (path, id, job) values (?, ?, ?)"; logger.debug("Preparing: " + insertSQL); this.insertStmt = connection.prepareStatement( insertSQL); String updateSQL = "update " + table + " set job = ? where path = ? and id = ?"; logger.debug("Preparing: " + updateSQL); this.updateStmt = connection.prepareStatement( updateSQL); String selectSQL = "select job from " + table + " where path = ? and id = ?"; logger.debug("Preparing: " + selectSQL); this.selectStmt = connection.prepareStatement( selectSQL); String deleteSQL = "delete from " + table + " where path = ? and id = ?"; logger.debug("Preparing: " + deleteSQL); this.deleteStmt = connection.prepareStatement( deleteSQL); String clearSQL = "delete from " + table + " where path = ?"; logger.debug("Preparing: " + clearSQL); this.clearStmt = connection.prepareStatement( clearSQL); String listSQL = "select id from " + table + " where path = ?"; logger.debug("Preparing: " + listSQL); this.listStmt = connection.prepareStatement( listSQL); } catch (SQLException e) { try { close(); } catch (SQLException e2) { // ignore } throw e; } } @Override public synchronized void close() throws SQLException { SQLException ex = null; if (updateStmt != null) { try { updateStmt.close(); } catch (SQLException e) { ex = e; } } if (insertStmt != null) { try { insertStmt.close(); } catch (SQLException e) { ex = e; } } if (selectStmt != null) { try { selectStmt.close(); } catch (SQLException e) { ex = e; } } if (deleteStmt != null) { try { deleteStmt.close(); } catch (SQLException e) { ex = e; } } if (clearStmt != null) { try { clearStmt.close(); } catch (SQLException e) { ex = e; } } if (listStmt != null) { try { listStmt.close(); } catch (SQLException e) { ex = e; } } if (connection != null) { try { connection.close(); } catch (SQLException e) { ex = e; } } if (ex != null) { throw ex; } } @Override public void persist(Path path, String id, Object o) throws SQLException { synchronized (updateStmt) { byte[] bytes = new SerializeWithBytes().toBytes(o); logger.debug("Saving: " + path + ", " + id + ", " + bytes.length + " bytes."); updateStmt.setBytes(1, bytes); updateStmt.setString(2, path.toString()); updateStmt.setString(3, id); int count = updateStmt.executeUpdate(); if (count == 1) { return; } insertStmt.setString(1, path.toString()); insertStmt.setString(2, id); insertStmt.setBytes(3, bytes); insertStmt.execute(); } } @Override public Object restore(Path path, String id, ClassLoader classLoader) throws SQLException { synchronized (selectStmt) { selectStmt.setString(1, path.toString()); selectStmt.setString(2, id); ResultSet rs = selectStmt.executeQuery(); try { if (!rs.next()) { return null; } logger.debug("Retrieved: " + path + ", " + id + "."); Blob blob = rs.getBlob(1); InputStream is = blob.getBinaryStream(); return new SerializeWithBinaryStream().fromStream( is, classLoader); } finally { rs.close(); } } } @Override public void remove(Path path, String id) throws SQLException { synchronized (deleteStmt) { deleteStmt.setString(1, path.toString()); deleteStmt.setString(2, id); deleteStmt.executeUpdate(); } } @Override public void clear(Path path) throws SQLException { synchronized (clearStmt) { clearStmt.setString(1, path.toString()); clearStmt.executeUpdate(); } } @Override public String[] children(Path path) throws SQLException { synchronized (listStmt) { listStmt.setString(1, path.toString()); ResultSet rs = listStmt.executeQuery(); try { List<String> results = new ArrayList<String>(); while (rs.next()) { results.add(rs.getString(1)); } return results.toArray(new String[results.size()]); } finally { rs.close(); } } } }