package org.prevayler.foundation;
import org.prevayler.foundation.serialization.JavaSerializer;
import org.prevayler.foundation.serialization.Serializer;
import java.io.*;
public class DeepCopier {
/**
* Same as deepCopy(original, new JavaSerializer()).
*
* @throws ClassNotFoundException
* @throws IOException
*/
public static Object deepCopy(Object original) {
return deepCopy(original, new JavaSerializer());
}
/**
* Produce a deep copy of the given object. Serializes the entire object to a byte array in memory. Recommended for
* relatively small objects, such as individual transactions.
*/
public static Object deepCopy(Object original, Serializer serializer) {
try {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
serializer.writeObject(byteOut, original);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
return serializer.readObject(byteIn);
} catch (Exception e) {
Cool.unexpected(e);
return null;
}
}
/**
* Produce a deep copy of the given object. Serializes the object through a pipe between two threads. Recommended for
* very large objects, such as an entire prevalent system. The current thread is used for serializing the original
* object in order to respect any synchronization the caller may have around it, and a new thread is used for
* deserializing the copy.
*/
public static Object deepCopyParallel(Object original, Serializer serializer) throws Exception {
PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream = new PipedInputStream(outputStream);
Receiver receiver = new Receiver(inputStream, serializer);
try {
serializer.writeObject(outputStream, original);
} finally {
outputStream.close();
}
return receiver.getResult();
}
private static class Receiver extends Thread {
private InputStream _inputStream;
private Serializer _serializer;
private Object _result;
private Exception _exception;
private Error _error;
public Receiver(InputStream inputStream, Serializer serializer) {
_inputStream = inputStream;
_serializer = serializer;
start();
}
public void run() {
try {
_result = _serializer.readObject(_inputStream);
} catch (Exception e) {
_exception = e;
} catch (Error e) {
_error = e;
throw e;
}
try {
// Some serializers may write more than they actually need to deserialize the object, but if
// we don't read it all the PipedOutputStream will choke.
while (_inputStream.read() != -1) {
}
} catch (IOException e) {
// The object has been successfully deserialized, so ignore problems at this point (for example,
// the serializer may have explicitly closed the _inputStream itself, causing this read to fail).
}
}
public Object getResult() throws Exception {
try {
join();
} catch (InterruptedException e) {
throw new RuntimeException("Unexpected InterruptedException", e);
}
// join() guarantees that all shared memory is synchronized between the two threads
if (_error != null) throw new RuntimeException("Error during deserialization", _error);
if (_exception != null) throw _exception;
if (_result == null) throw new RuntimeException("Deep copy failed in an unknown way");
return _result;
}
}
}