
eu.interedition.text.h2.H2TextRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of text-core Show documentation
Show all versions of text-core Show documentation
Stand-off Markup/Annotation Text Model
The newest version!
package eu.interedition.text.h2;
import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.CharStreams;
import com.google.common.io.Closeables;
import eu.interedition.text.Anchor;
import eu.interedition.text.Layer;
import eu.interedition.text.Name;
import eu.interedition.text.Query;
import eu.interedition.text.QueryResult;
import eu.interedition.text.Text;
import eu.interedition.text.TextRange;
import eu.interedition.text.TextRepository;
import eu.interedition.text.util.BackupSupport;
import eu.interedition.text.util.BatchLayerAdditionSupport;
import eu.interedition.text.util.UpdateSupport;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.nio.charset.Charset;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import javax.sql.DataSource;
/**
* @author Gregor Middell
*/
public class H2TextRepository implements TextRepository, UpdateSupport, BackupSupport, BatchLayerAdditionSupport {
private final Class dataType;
private final DataSource ds;
private final boolean transactional;
private final DataStreamMapper dataStreamMapper;
private final H2Query query = new H2Query();
private final Iterator primaryKeySource = new PrimaryKeySource(this);
public H2TextRepository(Class dataType, DataSource ds) {
this(dataType, new SerializableDataStreamMapper(), ds, true);
}
public H2TextRepository(Class dataType, DataStreamMapper dataStreamMapper, DataSource ds, boolean transactional) {
this.dataType = dataType;
this.dataStreamMapper = dataStreamMapper;
this.ds = ds;
this.transactional = transactional;
}
public H2TextRepository withSchema() {
Connection connection = null;
Statement stmt = null;
try {
connection = begin();
stmt = connection.createStatement();
stmt.executeUpdate("create sequence if not exists interedition_text_id");
stmt.executeUpdate("create table if not exists interedition_name (id bigint primary key, ln varchar(100) not null, ns varchar(100), unique (ln, ns))");
stmt.executeUpdate("create table if not exists interedition_text_layer (id bigint primary key, name_id bigint not null references interedition_name (id) on delete cascade, text_content clob not null, layer_data blob)");
stmt.executeUpdate("create table if not exists interedition_text_anchor (id bigint primary key, from_id bigint not null references interedition_text_layer (id) on delete cascade, to_id bigint not null references interedition_text_layer (id) on delete cascade, range_start bigint not null, range_end bigint not null)");
stmt.executeUpdate("create index if not exists interedition_text_range on interedition_text_anchor (range_start, range_end)");
commit(connection);
return this;
} catch (SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(stmt);
JdbcUtil.closeQuietly(connection);
}
}
public Layer findByIdentifier(long id) {
return query.byId(this, id);
}
@Override
public QueryResult query(Query query) {
return this.query.results(this, query);
}
@Override
public void delete(Iterable> layers) {
final StringBuilder deleteSql = new StringBuilder();
for (LayerRelation> layer : Iterables.filter(layers, LayerRelation.class)) {
final long id = layer.getId();
if (deleteSql.length() == 0) {
deleteSql.append("delete from interedition_text_layer where id in (").append(id);
} else {
deleteSql.append(",").append(id);
}
}
if (deleteSql.length() > 0) {
Connection connection = null;
Statement deleteStatement = null;
try {
connection = begin();
deleteStatement = connection.createStatement();
deleteStatement.executeUpdate(deleteSql.append(")").toString());
} catch (SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(deleteStatement);
JdbcUtil.closeQuietly(connection);
}
}
}
@Override
public Iterable> add(Iterable> batch) throws IOException {
final List> added = Lists.newLinkedList();
Connection connection = null;
PreparedStatement insertLayer = null;
PreparedStatement insertAnchor = null;
try {
for (Layer layer : batch) {
if (connection == null) {
connection = begin();
}
if (insertLayer == null) {
insertLayer = connection.prepareStatement("insert into interedition_text_layer (name_id, text_content, layer_data, id) values (?, ?, ?, ?)");
}
final NameRelation nameRelation = name(layer.getName());
insertLayer.setLong(1, nameRelation.getId());
final Clob textClob = connection.createClob();
Writer textWriter = null;
try {
layer.read(textWriter = textClob.setCharacterStream(1));
} finally {
Closeables.close(textWriter, false);
}
insertLayer.setClob(2, textClob);
final T data = layer.data();
if (data != null) {
final Blob dataBlob = connection.createBlob();
OutputStream dataStream = null;
try {
dataStreamMapper.write(data, dataStream = dataBlob.setBinaryStream(1));
} finally {
Closeables.close(dataStream, false);
}
insertLayer.setBlob(3, dataBlob);
} else {
insertLayer.setNull(3, Types.BLOB);
}
final long id = Iterators.getNext(primaryKeySource, null);
insertLayer.setLong(4, id);
insertLayer.addBatch();
final Set> anchors = layer.getAnchors();
final Set> mappedAnchors = Sets.newHashSet();
for (Anchor anchor : anchors) {
final Layer anchorText = anchor.getText();
if (anchorText instanceof LayerRelation) {
if (insertAnchor == null) {
insertAnchor = connection.prepareStatement("insert into interedition_text_anchor (id, from_id, to_id, range_start, range_end) values (?, ?, ?, ?, ?)");
}
final long anchorId = Iterators.getNext(primaryKeySource, null);
final TextRange anchorRange = anchor.getRange();
insertAnchor.setLong(1, anchorId);
insertAnchor.setLong(2, id);
insertAnchor.setLong(3, anchorText.getId());
insertAnchor.setLong(4, anchorRange.getStart());
insertAnchor.setLong(5, anchorRange.getEnd());
insertAnchor.addBatch();
mappedAnchors.add(new AnchorRelation(anchorText, anchorRange, anchorId));
}
}
added.add(new LayerRelation(nameRelation, mappedAnchors, data, id, this));
}
if (insertLayer != null) {
insertLayer.executeBatch();
}
if (insertAnchor != null) {
insertAnchor.executeBatch();
}
if (connection != null) {
commit(connection);
}
return added;
} catch (SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(insertAnchor);
JdbcUtil.closeQuietly(insertLayer);
JdbcUtil.closeQuietly(connection);
}
}
@Override
public Layer add(final Name name, final Reader text, final T data, final Set> anchors) throws IOException {
return Iterables.getOnlyElement(add(Collections.>singleton(new Layer() {
@Override
public Set> getAnchors() {
return anchors;
}
@Override
public Set> getPorts() throws IOException {
return Collections.emptySet();
}
@Override
public long getId() {
throw new UnsupportedOperationException();
}
@Override
public T data() {
return data;
}
@Override
public Name getName() {
return name;
}
@Override
public void read(Writer target) throws IOException {
CharStreams.copy(text, target);
}
@Override
public String read() throws IOException {
return CharStreams.toString(text);
}
@Override
public void read(TextRange range, Writer target) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void stream(Consumer consumer) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void stream(TextRange range, Consumer consumer) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String read(TextRange range) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public SortedMap read(SortedSet textRanges) {
throw new UnsupportedOperationException();
}
@Override
public long length() {
throw new UnsupportedOperationException();
}
})));
}
@Override
public void updateText(Layer target, Reader text) throws IOException {
if (target instanceof LayerRelation) {
Connection connection = null;
PreparedStatement update = null;
try {
connection = begin();
update = connection.prepareStatement("UPDATE interedition_text_layer SET text_content = ? WHERE id = ?");
final Clob textClob = connection.createClob();
Writer textStream = null;
try {
CharStreams.copy(text, textStream = textClob.setCharacterStream(1));
} finally {
Closeables.close(textStream, false);
}
update.setClob(1, textClob);
update.setLong(2, ((LayerRelation>) target).getId());
update.executeUpdate();
commit(connection);
} catch (SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(update);
JdbcUtil.closeQuietly(connection);
}
}
}
@Override
public Layer add(Name name, Reader text, T data, Anchor anchor) throws IOException {
return add(name, text, data, Collections.singleton(anchor));
}
public void backup(Writer to) throws IOException {
Connection connection = null;
PreparedStatement script = null;
ResultSet resultSet = null;
try {
connection = begin();
script = connection.prepareStatement("SCRIPT DROP BLOCKSIZE 10485760");
resultSet = script.executeQuery();
while (resultSet.next()) {
final Reader scriptReader = resultSet.getCharacterStream(1);
try {
CharStreams.copy(scriptReader, to);
} finally {
Closeables.close(scriptReader, false);
}
to.write("\n");
}
commit(connection);
} catch (SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(resultSet);
JdbcUtil.closeQuietly(script);
JdbcUtil.closeQuietly(connection);
}
}
@Override
public void restore(File from, Charset charset) throws IOException {
Connection connection = null;
Statement runScript = null;
ResultSet resultSet = null;
try {
connection = begin();
runScript = connection.createStatement();
runScript.executeUpdate(String.format("RUNSCRIPT FROM '%s' CHARSET '%s'", from.getPath(), charset.name()));
commit(connection);
} catch (SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(resultSet);
JdbcUtil.closeQuietly(runScript);
JdbcUtil.closeQuietly(connection);
}
}
@Override
public void restore(Reader from) throws IOException {
final File restoreSql = File.createTempFile(getClass().getName() + ".restore", ".sql");
restoreSql.deleteOnExit();
try {
final Charset charset = Charset.forName("UTF-8");
Writer tempWriter = null;
try {
CharStreams.copy(from, tempWriter = new OutputStreamWriter(new FileOutputStream(restoreSql), charset));
} finally {
Closeables.close(tempWriter, false);
}
restore(restoreSql, charset);
} finally {
restoreSql.delete();
}
}
NameRelation cachedName(Long id, final NameRelation name) {
try {
nameCache.put(name, name);
return nameIdCache.get(id, new Callable() {
@Override
public NameRelation call() throws Exception {
return name;
}
});
} catch (ExecutionException e) {
throw Throwables.propagate(e);
}
}
NameRelation name(final Name name) throws SQLException {
if (name instanceof NameRelation) {
return (NameRelation) name;
}
try {
return nameCache.get(name, new Callable() {
@Override
public NameRelation call() throws Exception {
final String ln = name.getLocalName();
final URI ns = name.getNamespace();
Connection connection = null;
PreparedStatement findName = null;
PreparedStatement insertName = null;
ResultSet resultSet = null;
try {
connection = begin();
findName = connection.prepareStatement("select id from interedition_name where ln = ? and ns = ?");
findName.setString(1, ln);
if (ns == null) {
findName.setNull(2, Types.VARCHAR);
} else {
findName.setString(2, ns.toString());
}
resultSet = findName.executeQuery();
NameRelation result;
if (resultSet.next()) {
result = new NameRelation(name, resultSet.getLong(1));
} else {
insertName = connection.prepareStatement("insert into interedition_name (id, ln, ns) values (?, ?, ?)");
final long id = Iterators.getNext(primaryKeySource, null);
insertName.setLong(1, id);
insertName.setString(2, ln);
insertName.setString(3, ns == null ? null : ns.toString());
insertName.executeUpdate();
result = new NameRelation(name, id);
}
commit(connection);
nameIdCache.put(result.getId(), result);
return result;
} catch(SQLException e) {
throw rollbackAndConvert(connection, e);
} finally {
JdbcUtil.closeQuietly(resultSet);
JdbcUtil.closeQuietly(insertName);
JdbcUtil.closeQuietly(findName);
JdbcUtil.closeQuietly(connection);
}
}
});
} catch (ExecutionException e) {
Throwables.propagateIfInstanceOf(Throwables.getRootCause(e), SQLException.class);
throw Throwables.propagate(e);
}
}
public void clearNameCache() {
nameCache.invalidateAll();
nameIdCache.invalidateAll();
}
private final Cache nameCache = CacheBuilder.newBuilder().build();
private final Cache nameIdCache = CacheBuilder.newBuilder().build();
Connection begin() throws SQLException {
final Connection connection = ds.getConnection();
if (transactional) {
connection.setAutoCommit(false);
}
return connection;
}
void commit(Connection connection) throws SQLException {
if (transactional) {
connection.commit();
}
}
RuntimeException rollbackAndConvert(Connection connection, Throwable t) {
if (connection != null && transactional) {
try {
connection.rollback();
} catch (SQLException e) {
}
}
return Throwables.propagate(t);
}
public T data(InputStream stream) throws IOException {
return dataStreamMapper.read(stream, dataType);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy