package com.googlecode.objectify.impl.translate; import com.google.appengine.api.datastore.Blob; import com.googlecode.objectify.annotation.Serialize; import com.googlecode.objectify.impl.Path; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; /** * <p>Loader which can load any serialized thing from a Blob.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class SerializeTranslatorFactory implements TranslatorFactory<Object, Blob> { private static final Logger log = Logger.getLogger(SerializeTranslatorFactory.class.getName()); @Override public Translator<Object, Blob> create(TypeKey<Object> tk, CreateContext ctx, Path path) { final Serialize serializeAnno = tk.getAnnotationAnywhere(Serialize.class); // We only work with @Serialize classes if (serializeAnno == null) return null; return new ValueTranslator<Object, Blob>(Blob.class) { @Override protected Object loadValue(Blob value, LoadContext ctx, Path path) throws SkipException { // Need to be careful here because we don't really know if the data was serialized or not. Start // with whatever the annotation says, and if that doesn't work, try the other option. try { ByteArrayInputStream bais = new ByteArrayInputStream(value.getBytes()); // Start with the annotation boolean unzip = serializeAnno.zip(); try { return readObject(bais, unzip); } catch (IOException ex) { // will be one of ZipException or StreamCorruptedException if (log.isLoggable(Level.INFO)) log.log(Level.INFO, "Error trying to deserialize object using unzip=" + unzip + ", retrying with " + !unzip, ex); unzip = !unzip; return readObject(bais, unzip); // this will pass the exception up } } catch (Exception ex) { path.throwIllegalState("Unable to deserialize " + value, ex); return null; // never gets here } } @Override protected Blob saveValue(Object value, boolean index, SaveContext ctx, Path path) throws SkipException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStream out = baos; if (serializeAnno.zip()) { Deflater deflater = new Deflater(serializeAnno.compressionLevel()); out = new DeflaterOutputStream(out, deflater); } ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(value); oos.close(); return new Blob(baos.toByteArray()); } catch (IOException ex) { path.throwIllegalState("Unable to serialize " + value, ex); return null; // never gets here } } /** Try reading an object from the stream */ private Object readObject(ByteArrayInputStream bais, boolean unzip) throws IOException, ClassNotFoundException { bais.reset(); InputStream in = bais; if (unzip) in = new InflaterInputStream(in); ObjectInputStream ois = new ObjectInputStream(in); return ois.readObject(); } }; } }