package org.springmodules.prevayler; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.LinkedList; import java.util.List; import org.apache.log4j.Logger; import org.prevayler.Prevayler; import org.springmodules.prevayler.callback.PrevaylerCallback; import org.springmodules.prevayler.configuration.PrevaylerConfiguration; import org.springmodules.prevayler.support.PrevaylerTransactionException; import org.springmodules.prevayler.system.PrevalentSystem; import org.springmodules.prevayler.system.callback.SystemCallback; import org.springmodules.prevayler.transaction.CompositeTransactionCommand; /** * {@link TransactionalPersistenceManager} {@link Session} implementation. * @author Sergio Bossa */ public class TransactionalSession implements Session { private static final Logger logger = Logger.getLogger(TransactionalSession.class); private PrevalentSystem system; private List executionQueue; private boolean flushed; public TransactionalSession(PrevaylerConfiguration configuration) { Prevayler prevayler = configuration.getPrevaylerInstance(); this.system = (PrevalentSystem) this.deepCopy(prevayler.prevalentSystem()); this.executionQueue = new LinkedList(); this.flushed = false; } public Object execute(PrevaylerCallback callback) { if (this.flushed) { throw new PrevaylerTransactionException("Error: session already flushed and closed."); } else { // Enqueue a copy of the callback for later execution through prevayler at commit time: PrevaylerCallback copiedCallback = (PrevaylerCallback) this.deepCopy(callback); this.executionQueue.add(copiedCallback); // Locally execute the callback: return callback.doInTransaction(this.system); } } public Object execute(SystemCallback callback) { if (this.flushed) { throw new PrevaylerTransactionException("Error: session already flushed and closed."); } else { // Directly execute the callback into the system: return this.system.execute(callback); // No need to enqueue because this is a callback to directly executed into the system. } } public void flush(Prevayler prevayler) { if (this.flushed) { throw new PrevaylerTransactionException("Error: session already flushed and closed."); } else { CompositeTransactionCommand command = new CompositeTransactionCommand(this.executionQueue); prevayler.execute(command); } } /** Class internals **/ private Object deepCopy(Object target) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); Object replica = null; ObjectOutputStream out = null; ObjectInputStream in = null; try { out = new ObjectOutputStream(buffer); out.writeObject(target); out.flush(); in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); replica = in.readObject(); } catch(Exception ex) { throw new PrevaylerTransactionException("Internal transaction error!", ex); } finally { try { if (out != null) out.close(); if (in != null) in.close(); } catch (IOException ex) { logger.error("Error closing streams!"); } } return replica; } }