/* * #! * Ontopia Content Store * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with 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. * !# */ package net.ontopia.infoset.content; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collections; import net.ontopia.persistence.proxy.ConnectionFactoryIF; import net.ontopia.persistence.proxy.HighLowKeyGenerator; import net.ontopia.persistence.proxy.IdentityIF; import net.ontopia.persistence.proxy.KeyGeneratorIF; import net.ontopia.topicmaps.core.TopicMapIF; /** * INTERNAL: Content store implementation on top of JDBC that uses a * non-native sequence generator to generate keys. */ public class JDBCContentStore implements ContentStoreIF { /* Default database schema: create table TM_CONTENT_STORE ( rkey integer not null, rvalue bytea null, primary key (rkey) ); insert into TM_ADMIN_SEQUENCE values ('TM_CONTENT_STORE', 0); */ protected Connection conn; protected KeyGeneratorIF keygen; protected String sql_get; protected String sql_put; protected String sql_remove; // --- Static interface public static ContentStoreIF getInstance(TopicMapIF topicmap) { net.ontopia.topicmaps.impl.rdbms.RDBMSTopicMapStore store = (net.ontopia.topicmaps.impl.rdbms.RDBMSTopicMapStore)topicmap.getStore(); return new JDBCContentStore(store.getConnection(), store.getConnectionFactory(false)); } // --- ContentStoreIF implementation public JDBCContentStore(Connection conn, ConnectionFactoryIF connfactory) { this(conn, connfactory, "TM_CONTENT_STORE", "rkey", "rvalue"); } public JDBCContentStore(Connection conn, ConnectionFactoryIF connfactory, String tblname, String keyname, String valname) { this.conn = conn; init(tblname, keyname, valname); String global_entry = tblname; int grabsize = 5; String select_suffix = "for update"; this.keygen = new HighLowKeyGenerator(connfactory, "TM_ADMIN_SEQUENCE", "seq_name", "seq_count", global_entry, grabsize, "generic", Collections.EMPTY_MAP); } protected void init(String tblname, String keyname, String valname) { this.sql_get = "select " + valname + " from " + tblname +" where " + keyname + " = ?"; this.sql_put = "insert into " + tblname + " (" + keyname + ", " + valname + ") values (?, ?)"; this.sql_remove = "delete from " + tblname + " where " + keyname + " = ?"; } public boolean containsKey(int key) throws ContentStoreException { try { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql_get); ps.setInt(1, key); try { rs = ps.executeQuery(); return rs.next(); } catch (SQLException e) { return false; } } finally { if (ps != null) ps.close(); if (rs != null) rs.close(); } } catch (Throwable t) { throw new ContentStoreException(t); } } public ContentInputStream get(int key) throws ContentStoreException { try { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql_get); ps.setInt(1, key); } catch (SQLException e) { if (ps != null) ps.close(); throw e; } try { rs = ps.executeQuery(); } catch (SQLException e) { return null; } try { // NOTE: ps and rs will not get closed until binary input stream is closed. if (rs.next()) { InputStream istream = rs.getBinaryStream(1); return new JDBCBinaryInputStream(ps, rs, istream, readLength(istream)); } else return null; } catch (SQLException e) { if (ps != null) ps.close(); if (rs != null) rs.close(); throw e; } } catch (Throwable t) { throw new ContentStoreException(t); } } protected int readLength(InputStream stream) throws IOException { byte[] b = new byte[4]; if (stream.read(b) < 4) throw new RuntimeException("Could not read content length."); return ((b[3] & 0xFF) << 0) + ((b[2] & 0xFF) << 8) + ((b[1] & 0xFF) << 16) + ((b[0] & 0xFF) << 24); } protected int generateId() { // Generate id for new entry IdentityIF id = keygen.generateKey(null); return ((Long)id.getKey(0)).intValue(); } public int add(ContentInputStream data) throws ContentStoreException { return add(data, data.getLength()); } public int add(InputStream data, int length) throws ContentStoreException { try { // Push length onto stream PushbackInputStream pstream = new PushbackInputStream(data, 4); byte[] b = new byte[4]; b[3] = (byte)(length >>> 0); b[2] = (byte)(length >>> 8); b[1] = (byte)(length >>> 16); b[0] = (byte)(length >>> 24); pstream.unread(b); // Add new entry int key = generateId(); PreparedStatement ps = null; try { ps = conn.prepareStatement(sql_put); ps.setInt(1, key); ps.setBinaryStream(2, pstream, length + b.length); ps.executeUpdate(); } finally { if (ps != null) ps.close(); if (pstream != null) pstream.close(); } return key; } catch (Throwable t) { throw new ContentStoreException(t); } } public boolean remove(int key) throws ContentStoreException { try { PreparedStatement ps = null; try { ps = conn.prepareStatement(sql_remove); ps.setInt(1, key); int rows = ps.executeUpdate(); return (rows >= 1); } finally { if (ps != null) ps.close(); } } catch (Throwable t) { throw new ContentStoreException(t); } } public void close() throws ContentStoreException { //! conn.close(); } }