package org.neo4j.smack.pipeline.database.event; import org.jboss.netty.channel.Channel; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.smack.pipeline.database.ExceptionOutputWriter; import org.neo4j.smack.pipeline.database.ThreadTransactionManagement; import org.neo4j.smack.pipeline.database.TransactionRegistry; import org.neo4j.smack.pipeline.event.Fallible; import org.neo4j.smack.pipeline.event.WorkTransactionMode; import org.neo4j.smack.routing.Endpoint; import org.neo4j.smack.routing.PathVariables; import org.neo4j.smack.serialization.SerializationFactory; import com.lmax.disruptor.EventFactory; /** * Represents work that has been prepared and is ready to be performed * by the system by a single database thread, usually within some specific * transaction and always by a specific database worker thread. * * Note: There are lots of these instances, keep this as slim as possible to * keep memory usage down. */ public class DatabaseWork implements Fallible { public static EventFactory<DatabaseWork> FACTORY = new EventFactory<DatabaseWork>() { // TODO: This shouldn't be here SerializationFactory serializationFactory = new SerializationFactory(); public DatabaseWork newInstance() { return new DatabaseWork(serializationFactory); } }; private static final ExceptionOutputWriter exceptionOutputWriter = new ExceptionOutputWriter(); private Endpoint endpoint; private WorkTransactionMode txMode; private Channel channel; private final DefaultInvocationImpl invocation; private final NettyChannelBackedOutput output; private Throwable failure; private ThreadTransactionManagement txManage; public DatabaseWork(SerializationFactory serializationFactory) { this.invocation = new DefaultInvocationImpl(); this.output = new NettyChannelBackedOutput(serializationFactory); } public void perform() throws Exception { try { if(!hasFailed()) { txManage.beforeWork(txMode, invocation.getTxId()); endpoint.invoke(invocation, output); txManage.afterWork(txMode, invocation.getTxId()); output.flush(); } else { throw failure; } } catch (Throwable e) { txManage.onWorkFailure(txMode, invocation.getTxId()); if (output.started()) { channel.close(); } else { exceptionOutputWriter.write(output, e); output.flush(); } // TODO: Logging e.printStackTrace(); } } @Override public void setFailed(Throwable ex) { this.failure = ex; } @Override public Throwable getFailureCause() { return this.failure; } @Override public boolean hasFailed() { return failure != null; } public WorkTransactionMode getTransactionMode() { return txMode; } public Invocation getInvocation() { return invocation; } @Override public String toString() { return "DatabaseWork[" + endpoint + ", txMode:" + txMode + ", failure:" + failure + "]"; } public void reset(Channel channel, boolean isPersistentConnection, long txId, WorkTransactionMode txMode, GraphDatabaseService database, TransactionRegistry txs, ThreadTransactionManagement txManage, Throwable failureCause) { reset(null, channel, isPersistentConnection, null, txId, txMode, null, null, database, txs, txManage); setFailed(failureCause); } public void reset(Endpoint endpoint, Channel outputChannel, boolean isPersistentConnection, String path, long txId, WorkTransactionMode txMode, PathVariables pathVariables, Object content, GraphDatabaseService database, TransactionRegistry txRegistry, ThreadTransactionManagement txManage) { this.channel = outputChannel; this.endpoint = endpoint; this.txMode = txMode == null ? WorkTransactionMode.NO_TRANSACTION : txMode; this.failure = null; this.txManage = txManage; invocation.reset(path, txId, pathVariables, content, database, txRegistry); output.reset(outputChannel, endpoint != null ? endpoint.getSerializationStrategy() : null, isPersistentConnection); } }