package org.prevayler.implementation; import org.prevayler.foundation.Chunk; import org.prevayler.foundation.serialization.Serializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Serializable; import java.util.Date; public abstract class Capsule implements Serializable { private final byte[] _serialized; protected Capsule(Object transaction, Serializer journalSerializer) { try { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); journalSerializer.writeObject(bytes, transaction); _serialized = bytes.toByteArray(); } catch (Exception exception) { throw new Error("Unable to serialize transaction", exception); } } protected Capsule(byte[] serialized) { _serialized = serialized; } /** * Get the serialized representation of the transaction. Callers must not modify the returned array. */ public byte[] serialized() { return _serialized; } /** * Deserialize the contained Transaction or TransactionWithQuery. */ public Object deserialize(Serializer journalSerializer) { try { return journalSerializer.readObject(new ByteArrayInputStream(_serialized)); } catch (Exception exception) { throw new Error("Unable to deserialize transaction", exception); } } /** * Execute a freshly deserialized copy of the transaction. This method will synchronize on the prevalentSystem * while running the transaction but after deserializing it. */ public void executeOn(Object prevalentSystem, Date executionTime, Serializer journalSerializer) { Object transaction = deserialize(journalSerializer); synchronized (prevalentSystem) { justExecute(transaction, prevalentSystem, executionTime); } } /** * Actually execute the Transaction or TransactionWithQuery. The caller * is responsible for synchronizing on the prevalentSystem. */ protected abstract void justExecute(Object transaction, Object prevalentSystem, Date executionTime); /** * Make a clean copy of this capsule that will have its own query result fields. */ public abstract Capsule cleanCopy(); Chunk toChunk() { Chunk chunk = new Chunk(_serialized); chunk.setParameter("withQuery", String.valueOf(this instanceof TransactionWithQueryCapsule)); return chunk; } static Capsule fromChunk(Chunk chunk) { boolean withQuery = Boolean.valueOf(chunk.getParameter("withQuery")).booleanValue(); if (withQuery) { return new TransactionWithQueryCapsule(chunk.getBytes()); } else { return new TransactionCapsule(chunk.getBytes()); } } }