org.modeshape.jcr.value.binary.CassandraBinaryStore Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* ModeShape is free software. Unless otherwise indicated, all code in ModeShape
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* ModeShape is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.modeshape.jcr.value.binary;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import org.apache.commons.compress.utils.IOUtils;
import org.modeshape.jcr.JcrI18n;
import org.modeshape.jcr.value.BinaryKey;
import org.modeshape.jcr.value.BinaryValue;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.AlreadyExistsException;
import com.datastax.driver.core.exceptions.InvalidQueryException;
/**
* @author kulikov
*/
public class CassandraBinaryStore extends AbstractBinaryStore {
private static final boolean ALIVE = true;
private static final boolean UNUSED = false;
private Cluster cluster;
private Session session;
private String address;
private FileSystemBinaryStore cache;
public CassandraBinaryStore( String address ) {
this.address = address;
this.cache = TransientBinaryStore.get();
}
@Override
protected String getStoredMimeType( BinaryValue source ) throws BinaryStoreException {
try {
checkContentExists(source);
ResultSet rs = session.execute("SELECT mime_type FROM modeshape.binary WHERE cid = '" + source.getKey() + "';");
Row row = rs.one();
if (row == null) {
throw new BinaryStoreException(JcrI18n.unableToFindBinaryValue.text(source.getKey(), session));
}
return row.getString("mime_type");
} catch (BinaryStoreException e) {
throw e;
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
private void checkContentExists( BinaryValue source ) throws BinaryStoreException {
if (!contentExists(source.getKey(), true)) {
throw new BinaryStoreException(JcrI18n.unableToFindBinaryValue.text(source.getKey(), session));
}
}
@Override
protected void storeMimeType( BinaryValue source,
String mimeType ) throws BinaryStoreException {
try {
session.execute("UPDATE modeshape.binary SET mime_type='" + mimeType + "' where cid='" + source.getKey() + "';");
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public void storeExtractedText( BinaryValue source,
String extractedText ) throws BinaryStoreException {
try {
session.execute("UPDATE modeshape.binary SET ext_text='" + extractedText + "' where cid='" + source.getKey() + "';");
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public String getExtractedText( BinaryValue source ) throws BinaryStoreException {
try {
checkContentExists(source);
ResultSet rs = session.execute("SELECT ext_text FROM modeshape.binary WHERE cid = '" + source.getKey() + "';");
Row row = rs.one();
if (row == null) {
throw new BinaryStoreException(JcrI18n.unableToFindBinaryValue.text(source.getKey(), session));
}
return row.getString("ext_text");
} catch (BinaryStoreException e) {
throw e;
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public BinaryValue storeValue( InputStream stream ) throws BinaryStoreException {
// store into temporary file system store and get SHA-1
BinaryValue temp = cache.storeValue(stream);
try {
// prepare new binary key based on SHA-1
BinaryKey key = new BinaryKey(temp.getKey().toString());
// check for duplicate content
if (this.contentExists(key, ALIVE)) {
return new StoredBinaryValue(this, key, temp.getSize());
}
// check unused content
if (this.contentExists(key, UNUSED)) {
session.execute("UPDATE modeshape.binary SET usage=1 WHERE cid='" + key + "';");
return new StoredBinaryValue(this, key, temp.getSize());
}
// store content
PreparedStatement query = session.prepare("INSERT INTO modeshape.binary (cid, usage_time, payload, usage) VALUES ( ?,?,?,1 );");
BoundStatement statement = new BoundStatement(query);
session.execute(statement.bind(key.toString(), new Date(), buffer(stream)));
return new StoredBinaryValue(this, key, temp.getSize());
} catch (BinaryStoreException e) {
throw e;
} catch (IOException e) {
throw new BinaryStoreException(e);
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
} finally {
// remove content from temp store
cache.markAsUnused(temp.getKey());
}
}
@Override
public InputStream getInputStream( BinaryKey key ) throws BinaryStoreException {
try {
ResultSet rs = session.execute("SELECT payload FROM modeshape.binary WHERE cid='" + key.toString() + "' and usage=1;");
Row row = rs.one();
if (row == null) {
throw new BinaryStoreException(JcrI18n.unableToFindBinaryValue.text(key, session));
}
ByteBuffer buffer = row.getBytes("payload");
return new BufferedInputStream(buffer);
} catch (BinaryStoreException e) {
throw e;
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public void markAsUnused( Iterable keys ) throws BinaryStoreException {
try {
for (BinaryKey key : keys) {
session.execute("UPDATE modeshape.binary SET usage=0 where cid='" + key + "';");
}
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public void removeValuesUnusedLongerThan( long minimumAge,
TimeUnit unit ) throws BinaryStoreException {
try {
Date deadline = new Date(new Date().getTime() - unit.toMillis(minimumAge));
// When querying using 2nd indexes, Cassandra
// (it's not CQL specific) requires that you use an '=' for at least one of
// the indexed column in the where clause. This is a limitation of Cassandra.
// So we have to do some tricks here
ResultSet rs = session.execute("SELECT cid from modeshape.binary where usage=0 and usage_time < "
+ deadline.getTime() + " allow filtering;");
Iterator rows = rs.iterator();
while (rows.hasNext()) {
session.execute("DELETE from modeshape.binary where cid = '" + rows.next().getString("cid") + "';");
}
rs = session.execute("SELECT cid from modeshape.binary where usage=1 and usage_time < " + deadline.getTime()
+ " allow filtering;");
rows = rs.iterator();
while (rows.hasNext()) {
session.execute("DELETE from modeshape.binary where cid = '" + rows.next().getString("cid") + "';");
}
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public Iterable getAllBinaryKeys() throws BinaryStoreException {
try {
ResultSet rs = session.execute("SELECT cid from modeshape.binary WHERE usage=1;");
Iterator it = rs.iterator();
HashSet keys = new HashSet();
while (it.hasNext()) {
keys.add(new BinaryKey(it.next().getString("cid")));
}
return keys;
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
@Override
public void start() {
cluster = Cluster.builder().addContactPoint(address).build();
Metadata metadata = cluster.getMetadata();
System.out.printf("Connected to cluster: %s\n", metadata.getClusterName());
for (Host host : metadata.getAllHosts()) {
System.out.printf("Datacenter: %s; Host: %s; Rack: %s\n", host.getDatacenter(), host.getAddress(), host.getRack());
}
session = cluster.connect();
try {
session.execute("CREATE KEYSPACE modeshape WITH replication "
+ "= {'class':'SimpleStrategy', 'replication_factor':3};");
} catch (AlreadyExistsException e) {
}
session.execute("USE modeshape;");
try {
session.execute("CREATE TABLE modeshape.binary(" + "cid text PRIMARY KEY," + "mime_type text," + "ext_text text,"
+ "usage int," + "usage_time timestamp," + "payload blob)");
} catch (AlreadyExistsException e) {
}
try {
session.execute("CREATE INDEX USAGE_IDX ON modeshape.binary (usage);");
} catch (InvalidQueryException e) {
// exists
}
try {
session.execute("CREATE INDEX EXPIRE_IDX ON modeshape.binary (usage_time);");
} catch (InvalidQueryException e) {
// exists
}
}
/**
* Test content for existence.
*
* @param key content identifier
* @param alive true inside used content and false for checking within content marked as unused.
* @return true if content found
* @throws BinaryStoreException
*/
private boolean contentExists( BinaryKey key,
boolean alive ) throws BinaryStoreException {
try {
String query = "SELECT payload from modeshape.binary where cid='" + key.toString() + "'";
query = alive ? query + " and usage=1;" : query + " and usage = 0;";
ResultSet rs = session.execute(query);
return rs.iterator().hasNext();
} catch (RuntimeException e) {
throw new BinaryStoreException(e);
}
}
/**
* Converts input stream into ByteBuffer.
*
* @param stream
* @return the byte buffer
* @throws IOException
*/
private ByteBuffer buffer( InputStream stream ) throws IOException {
stream.reset();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
IOUtils.copy(stream, bout);
return ByteBuffer.wrap(bout.toByteArray());
}
protected final class BufferedInputStream extends InputStream {
private ByteBuffer buffer;
protected BufferedInputStream( ByteBuffer buffer ) {
this.buffer = buffer;
}
@Override
public int read() {
return buffer.position() < buffer.limit() ? buffer.get() & 0xff : -1;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy