package com.rubiconproject.oss.kv.backends; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import com.rubiconproject.oss.kv.BaseManagedKeyValueStore; import com.rubiconproject.oss.kv.KeyValueStore; import com.rubiconproject.oss.kv.KeyValueStoreException; import com.rubiconproject.oss.kv.annotations.Configurable; import com.rubiconproject.oss.kv.annotations.Configurable.Type; import com.rubiconproject.oss.kv.backends.sql.DefaultJdbcDAO; import com.rubiconproject.oss.kv.backends.sql.JdbcDAO; import com.rubiconproject.oss.kv.backends.sql.KeyValuePair; import com.rubiconproject.oss.kv.backends.sql.SimpleDataSource; import com.rubiconproject.oss.kv.transcoder.SerializableTranscoder; import com.rubiconproject.oss.kv.transcoder.Transcoder; public class JdbcKeyValueStore extends BaseManagedKeyValueStore implements KeyValueStore, IterableKeyValueStore { public static final String IDENTIFIER = "jdbc"; private Transcoder defaultTranscoder = new SerializableTranscoder(); private DataSource ds; private JdbcDAO dao; private String daoClassName; private String dataSourceName; private String url; private String username; private String password; private String table; private String keyField; private String valueField; public String getIdentifier() { return IDENTIFIER; } public void setDataSource(DataSource ds) { this.ds = ds; } public void setDAO(JdbcDAO dao) { this.dao = dao; } @Configurable(name = "dao", accepts = Type.StringType) public void setDAOClass(String className) { this.daoClassName = className; } @Configurable(name = "dataSource", accepts = Type.StringType) public void setDataSourceName(String name) { this.dataSourceName = name; } @Configurable(name = "url", accepts = Type.StringType) public void setUrl(String url) { this.url = url; } @Configurable(name = "username", accepts = Type.StringType) public void setUsername(String username) { this.username = username; } @Configurable(name = "password", accepts = Type.StringType) public void setPassword(String password) { this.password = password; } @Configurable(name = "table", accepts = Type.StringType) public void setTable(String table) { this.table = table; } @Configurable(name = "keyField", accepts = Type.StringType) public void setKeyField(String keyField) { this.keyField = keyField; } @Configurable(name = "valueField", accepts = Type.StringType) public void setValueField(String valueField) { this.valueField = valueField; } public void start() throws IOException { try { if (ds == null) { if (dataSourceName == null) { this.ds = new SimpleDataSource(url, username, password); } else { Context ctx = new InitialContext(); this.ds = (DataSource) ctx.lookup(dataSourceName); } } } catch (NamingException e) { throw new IOException(e); } finally { } if (dao == null) try { dao = getDAO(); } catch (Exception e) { throw new RuntimeException(e); } super.start(); } public void stop() { super.stop(); } public boolean exists(String key) throws KeyValueStoreException, IOException { assertReadable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareExists(conn, key); rs = ps.executeQuery(); if (rs.next()) { return true; } else return false; } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public Object get(String key) throws KeyValueStoreException, IOException { return get(key, defaultTranscoder); } public Object get(String key, Transcoder transcoder) throws KeyValueStoreException, IOException { assertReadable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareSelect(conn, key); rs = ps.executeQuery(); if (rs.next()) { KeyValuePair kp = dao.read(rs, transcoder); return kp.getValue(); } else return null; } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public Map<String, Object> getBulk(String... keys) throws KeyValueStoreException, IOException { assertReadable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareBulkSelect(conn, keys); rs = ps.executeQuery(); Map<String, Object> results = new HashMap<String, Object>(); while (rs.next()) { KeyValuePair kp = dao.read(rs, defaultTranscoder); results.put(kp.getKey(), kp.getValue()); } return results; } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public Map<String, Object> getBulk(List<String> keys) throws KeyValueStoreException, IOException { return getBulk(keys, defaultTranscoder); } public Map<String, Object> getBulk(List<String> keys, Transcoder transcoder) throws KeyValueStoreException, IOException { assertReadable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareBulkSelect(conn, keys); rs = ps.executeQuery(); Map<String, Object> results = new HashMap<String, Object>(); while (rs.next()) { KeyValuePair kp = dao.read(rs, transcoder); results.put(kp.getKey(), kp.getValue()); } return results; } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public void set(String key, Object value) throws KeyValueStoreException, IOException { set(key, value, defaultTranscoder); } public void set(String key, Object value, Transcoder transcoder) throws KeyValueStoreException, IOException { assertWriteable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareInsert(conn, key, value, transcoder); ps.execute(); if (!conn.getAutoCommit()) conn.commit(); } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public void delete(String key) throws KeyValueStoreException, IOException { assertWriteable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareDelete(conn, key); ps.execute(); if (!conn.getAutoCommit()) conn.commit(); } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public long size() throws KeyValueStoreException { assertReadable(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); ps = dao.prepareCount(conn); rs = ps.executeQuery(); if (rs.next()) { long count = rs.getLong(1); return count; } else return 0l; } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { release(rs); release(ps); release(conn); } } public KeyValueStoreIterator iterkeys() throws KeyValueStoreException { assertReadable(); Connection conn = null; PreparedStatement ps = null; try { conn = getConnection(); ps = dao.prepareIterator(conn); ResultSet rs = ps.executeQuery(); return new JdbcKeyValueIterator(conn, ps, rs); } catch (SQLException e) { throw new KeyValueStoreException(e); } finally { } } private JdbcDAO getDAO() throws InstantiationException, IllegalAccessException, ClassNotFoundException { JdbcDAO jdbcDAO = null; if (daoClassName != null) { jdbcDAO = (JdbcDAO) Class.forName(daoClassName).newInstance(); } if (jdbcDAO == null) jdbcDAO = new DefaultJdbcDAO(table, keyField, valueField); return jdbcDAO; } private Connection getConnection() throws SQLException { return ds.getConnection(); } private void release(Connection conn) { if (conn != null) try { conn.close(); } catch (Exception e) { } } private void release(PreparedStatement ps) { if (ps != null) try { ps.close(); } catch (Exception e) { } } private void release(ResultSet rs) { if (rs != null) try { rs.close(); } catch (Exception e) { } } private class JdbcKeyValueIterator implements KeyValueStoreIterator, Iterator<String> { private Connection conn; private PreparedStatement ps; private ResultSet rs; private String next; private JdbcKeyValueIterator(Connection conn, PreparedStatement ps, ResultSet rs) { this.conn = conn; this.ps = ps; this.rs = rs; } public Iterator<String> iterator() { return this; } public boolean hasNext() { try { return rs.next(); } catch (SQLException e) { throw new RuntimeException(e); } } public String next() { try { KeyValuePair kp = dao.read(rs, defaultTranscoder); this.next = kp.getKey(); return this.next; } catch (SQLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } public void remove() { try { PreparedStatement ps = dao.prepareDelete(conn, next); ps.executeUpdate(); if (!conn.getAutoCommit()) conn.commit(); } catch (SQLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { } } public void close() { release(rs); release(ps); release(conn); } } }