[jboss-svn-commits] JBoss Common SVN: r4453 - in shrinkwrap/trunk: api/src/main/java/org/jboss/shrinkwrap/api/serialization and 5 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Fri May 28 21:13:58 EDT 2010


Author: ALRubinger
Date: 2010-05-28 21:13:58 -0400 (Fri, 28 May 2010)
New Revision: 4453

Added:
   shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/serialization/
   shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/serialization/ZipSerializable.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/serialization/
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableImpl.java
   shrinkwrap/trunk/impl-base/src/main/resources/META-INF/services/org.jboss.shrinkwrap.api.serialization.ZipSerializable
   shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/
   shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/SerializationTestCase.java
   shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableOriginalImpl.java
Log:
[SHRINKWRAP-178] Define a Serializable view of Archives.  TODO: Test wire compatibility in both directions.  Currently working with same version present.

Added: shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/serialization/ZipSerializable.java
===================================================================
--- shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/serialization/ZipSerializable.java	                        (rev 0)
+++ shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/serialization/ZipSerializable.java	2010-05-29 01:13:58 UTC (rev 4453)
@@ -0,0 +1,36 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.shrinkwrap.api.serialization;
+
+import java.io.Serializable;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.Assignable;
+
+/**
+ * {@link Serializable} view of an {@link Archive} where
+ * contents are encoded in ZIP format during
+ * Serialization.  May be reassigned to a normal {@link Archive}
+ * view via {@link Assignable#as(Class)}.
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public interface ZipSerializable extends Serializable, Assignable
+{
+
+}

Added: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableImpl.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableImpl.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableImpl.java	2010-05-29 01:13:58 UTC (rev 4453)
@@ -0,0 +1,234 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.shrinkwrap.impl.base.serialization;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipInputStream;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.api.importer.ZipImporter;
+import org.jboss.shrinkwrap.api.serialization.ZipSerializable;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.jboss.shrinkwrap.impl.base.AssignableBase;
+import org.jboss.shrinkwrap.impl.base.Validate;
+import org.jboss.shrinkwrap.impl.base.io.IOUtil;
+
+/**
+ * Implementation of a {@link Serializable} view of {@link Archive}s,
+ * backed by ZIP en/decoding the contents during serialization/deserialization.
+ * Defines the wire protocol and must remain backwards-compatible.
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public class ZipSerializableImpl extends AssignableBase implements ZipSerializable
+{
+
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * serialVersionUID
+    */
+   private static final long serialVersionUID = 1L;
+
+   /**
+    * Logger
+    */
+   private static final Logger log = Logger.getLogger(ZipSerializableOriginalImpl.class.getName());
+
+   //-------------------------------------------------------------------------------------||
+   // Instance Members -------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Name of the archive; to be serialized
+    */
+   private final String name;
+
+   /**
+    * Underlying archive.  Won't be directly serialized; 
+    * instead we'll encode it as ZIP and send that
+    */
+   private transient Archive<?> archive;
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new instance, wrapping the specified {@link Archive}
+    */
+   public ZipSerializableImpl(final Archive<?> archive)
+   {
+      Validate.notNull(archive, "Archive must be specified");
+      final String name = archive.getName();
+      Validate.notNullOrEmpty(name, "Name of archive must be specified");
+      this.archive = archive;
+      this.name = name;
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.AssignableBase#getArchive()
+    */
+   @Override
+   protected Archive<?> getArchive()
+   {
+      assert archive != null : "Underlying archive may never be null; illegal state";
+      return archive;
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Serialization ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Serializes the invocation with a custom form
+    * 
+    * @serialData After all non-transient fields are written, we
+    * send the {@link Archive} contents encoded as ZIP.
+    */
+   private void writeObject(final ObjectOutputStream out) throws IOException
+   {
+      // Default write of non-transient fields
+      out.defaultWriteObject();
+
+      // Write as ZIP 
+      final InputStream in = archive.as(ZipExporter.class).exportZip();
+      try
+      {
+         IOUtil.copy(in, out); // Don't close the outstream
+      }
+      finally
+      {
+         // In case we get an InputStream type that supports closing
+         in.close();
+      }
+
+      // Log
+      if (log.isLoggable(Level.FINER))
+      {
+         log.finer("Wrote archive: " + archive.toString());
+      }
+   }
+
+   /**
+    * Deserializes according to the custom form 
+    * defined by {@link ZipSerializableOriginalImpl#writeObject(ObjectOutputStream)}
+    */
+   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
+   {
+      // Get default form
+      in.defaultReadObject();
+
+      // Create new Archive
+      final String name = this.name;
+      final ZipImporter archive = ShrinkWrap.create(ZipImporter.class, name);
+
+      // Read in archive
+      final ZipInputStream remains = new ZipInputStream(new DoNotDelegateCloseInputStream(in));
+
+      try
+      {
+         // Read in
+         archive.importZip(remains);
+
+         // Set
+         this.archive = archive.as(JavaArchive.class);
+      }
+      finally
+      {
+         // Close up our stream, but not the underlying ObjectInputStream
+         remains.close();
+      }
+
+      // Log
+      if (log.isLoggable(Level.FINER))
+      {
+         log.finer("Read in archive: " + archive.toString());
+      }
+
+      /*
+       * Leave this bit here.
+       * 
+       * After reading in the ZIP stream contents, we need to also 
+       * get to the EOF marker (which is not read in by the ZIP import process
+       * because it's the ZIP header, not part of the true contents.  Putting
+       * this loop here ensures we reach the marker, which is *not* the true 
+       * end of the stream.  Object data may be read again after here
+       * via something like:
+       * 
+       * in.readObject();
+       * 
+       * Without this loop we'll get an OptionalDataException when trying to
+       * read more objects in from the stream.  In the future we may add state
+       * which needs to be part of the serialization protocol, and things 
+       * need to stay in order, so they'll be added *after* the archive ZIP contents.
+       * Thus we must be able to read them.
+       */
+      while (in.read() != -1);
+
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Internal Helper Classes ------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * A {@link InputStream} which does not delegate the {@link InputStream#close()}
+    * operation to the wrapped delegate; we cannot close the {@link ObjectInputStream}
+    * passed into {@link ZipSerializableOriginalImpl#readObject(ObjectInputStream)}.
+    * 
+    * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+    * @version $Revision: $
+    */
+   private static class DoNotDelegateCloseInputStream extends FilterInputStream
+   {
+
+      protected DoNotDelegateCloseInputStream(final InputStream in)
+      {
+         super(in);
+      }
+
+      /**
+       * {@inheritDoc}
+       * @see java.io.FilterInputStream#close()
+       */
+      @Override
+      public void close() throws IOException
+      {
+         // NOOP
+      }
+
+   }
+
+}

Added: shrinkwrap/trunk/impl-base/src/main/resources/META-INF/services/org.jboss.shrinkwrap.api.serialization.ZipSerializable
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/resources/META-INF/services/org.jboss.shrinkwrap.api.serialization.ZipSerializable	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/main/resources/META-INF/services/org.jboss.shrinkwrap.api.serialization.ZipSerializable	2010-05-29 01:13:58 UTC (rev 4453)
@@ -0,0 +1 @@
+org.jboss.shrinkwrap.impl.base.serialization.ZipSerializableImpl
\ No newline at end of file

Added: shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/SerializationTestCase.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/SerializationTestCase.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/SerializationTestCase.java	2010-05-29 01:13:58 UTC (rev 4453)
@@ -0,0 +1,288 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.shrinkwrap.impl.base.serialization;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamConstants;
+import java.io.ObjectStreamField;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePath;
+import org.jboss.shrinkwrap.api.Node;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.serialization.ZipSerializable;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Ensures that serialization of Archives is possible via
+ * the {@link ZipSerializable} view.
+ * 
+ * SHRINKWRAP-178
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public class SerializationTestCase
+{
+
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Logger
+    */
+   private static final Logger log = Logger.getLogger(SerializationTestCase.class.getName());
+
+   //-------------------------------------------------------------------------------------||
+   // Tests ------------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Ensures we may serialize an {@link Archive} and preserve contents
+    * as expected
+    */
+   @Test
+   public void serialize() throws Exception
+   {
+      // Define the initial archive
+      final String name = "serializedArchive.jar";
+      final JavaArchive original = ShrinkWrap.create(JavaArchive.class, name).addClasses(SerializationTestCase.class,
+            JavaArchive.class);
+      log.info("Before: " + original.toString(true));
+
+      // Serialize
+      final JavaArchive roundtrip = serializeAndDeserialize(original.as(ZipSerializable.class)).as(JavaArchive.class);
+      log.info("After: " + roundtrip.toString(true));
+
+      // Ensure contents are as expected
+      final Map<ArchivePath, Node> originalContents = original.getContent();
+      final Map<ArchivePath, Node> roundtripContents = roundtrip.getContent();
+      Assert.assertEquals("Contents after serialization were not as expected", originalContents, roundtripContents);
+      Assert.assertEquals("Name of original archive was not as expected", name, original.getName());
+      Assert.assertEquals("Name not as expected after serialization", original.getName(), roundtrip.getName());
+   }
+
+   /**
+    * Ensures that the current serialization protocol is compatible
+    * with the version initially released.  We accomplish this by mocking
+    * {@link ZipSerializableOriginalImpl} and redefining its class name via 
+    * {@link SerializationTestCase#serializeAndDeserialize(ZipSerializable, Class)}, 
+    * which uses the {@link SpoofingObjectOutputStream}.
+    * @throws Exception
+    */
+   //   @Test
+   public void wireProtocolCurrentToOriginal() throws Exception
+   {
+      //TODO
+   }
+
+   /**
+    * Ensures that the original serialization protocol is compatible
+    * with the current version.  We accomplish this by mocking
+    * {@link ZipSerializableOriginalImpl} and redefining its class name via 
+    * {@link SerializationTestCase#serializeAndDeserialize(ZipSerializable, Class)}, 
+    * which uses the {@link SpoofingObjectOutputStream}.
+    * @throws Exception
+    */
+   //   @Test
+   public void wireProtocolOriginalToCurrent() throws Exception
+   {
+      //TODO
+   }
+
+   //TODO Add tests for backwards-compatibility wire protocol, nested archives
+
+   /*
+    * Note: looks like nested archives aren't represented in toString(verbose);
+    * investigate if necessary and open a JIRA
+    */
+
+   //-------------------------------------------------------------------------------------||
+   // Internal Helper Methods ------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Roundtrip serializes/deserializes the specified {@link Archive}
+    * 
+    * @param archive
+    * @return
+    * @throws IOException
+    * @throws ClassNotFoundException
+    */
+   private static ZipSerializable serializeAndDeserialize(final ZipSerializable archive) throws IOException,
+         ClassNotFoundException
+   {
+      assert archive != null : "Archive must be specified";
+      final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+      final ObjectOutputStream out = new ObjectOutputStream(byteOut);
+      out.writeObject(archive);
+      out.flush();
+      out.close();
+      final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
+      final ZipSerializable roundtrip = (ZipSerializable) in.readObject();
+      in.close();
+      return roundtrip;
+   }
+
+   /**
+    * Roundtrip serializes/deserializes the specified {@link Invocation}
+    * and reconsitutes/redefines as the specified target type
+    * 
+    * @param archive The original {@link ZipSerializable} instance
+    * @param The new type we should cast to after deserialization 
+    * @see http://crazybob.org/2006/01/unit-testing-serialization-evolution.html
+    * @see http://crazybob.org/2006/01/unit-testing-serialization-evolution_13.html
+    * @see http://www.theserverside.com/news/thread.tss?thread_id=38398
+    * @author Bob Lee
+    */
+   private static <S extends ZipSerializable> S serializeAndDeserialize(final ZipSerializable archive,
+         final Class<S> targetType) throws IOException
+   {
+      final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+      final ObjectOutputStream oout = new SpoofingObjectOutputStream(bout, archive.getClass(), targetType);
+      oout.writeObject(archive);
+      oout.flush();
+      oout.close();
+      final ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+      final ObjectInputStream oin = new ObjectInputStream(bin);
+      try
+      {
+         final Object obj = oin.readObject();
+         oin.close();
+         log.info("Original invocation type " + archive.getClass().getName() + " now represented as "
+               + obj.getClass().getName());
+         return targetType.cast(obj);
+      }
+      catch (ClassNotFoundException e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Internal Helper Classes ------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * SpoofingObjectOutputStream
+    * 
+    * ObjectOutputStream which will replace a class name with one explicitly given
+    *
+    * @see http://crazybob.org/2006/01/unit-testing-serialization-evolution_13.html
+    * @author Bob Lee
+    * @version $Revision: $
+    */
+   static class SpoofingObjectOutputStream extends ObjectOutputStream
+   {
+
+      String oldName;
+
+      String newName;
+
+      public SpoofingObjectOutputStream(OutputStream out, Class<?> oldClass, Class<?> newClass) throws IOException
+      {
+         super(out);
+         this.oldName = oldClass.getName();
+         this.newName = newClass.getName();
+      }
+
+      @Override
+      protected void writeClassDescriptor(ObjectStreamClass descriptor) throws IOException
+      {
+         Class<?> clazz = descriptor.forClass();
+
+         boolean externalizable = Externalizable.class.isAssignableFrom(clazz);
+         boolean serializable = Serializable.class.isAssignableFrom(clazz);
+         boolean hasWriteObjectData = hasWriteObjectMethod(clazz);
+         boolean isEnum = Enum.class.isAssignableFrom(clazz);
+
+         writeUTF(replace(descriptor.getName()));
+         writeLong(descriptor.getSerialVersionUID());
+         byte flags = 0;
+         if (externalizable)
+         {
+            flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
+            flags |= ObjectStreamConstants.SC_BLOCK_DATA;
+         }
+         else if (serializable)
+         {
+            flags |= ObjectStreamConstants.SC_SERIALIZABLE;
+         }
+         if (hasWriteObjectData)
+         {
+            flags |= ObjectStreamConstants.SC_WRITE_METHOD;
+         }
+         if (isEnum)
+         {
+            flags |= ObjectStreamConstants.SC_ENUM;
+         }
+         writeByte(flags);
+
+         ObjectStreamField[] fields = descriptor.getFields();
+         writeShort(fields.length);
+         for (ObjectStreamField field : fields)
+         {
+            writeByte(field.getTypeCode());
+            writeUTF(field.getName());
+            if (!field.isPrimitive())
+            {
+               writeObject(replace(field.getTypeString()));
+            }
+         }
+      }
+
+      String replace(String className)
+      {
+         if (className.equals(newName))
+         {
+            throw new RuntimeException("Found instance of " + className + "." + " Expected instance of " + oldName
+                  + ".");
+         }
+         return className == oldName ? newName : className;
+      }
+
+      boolean hasWriteObjectMethod(Class<?> clazz)
+      {
+         try
+         {
+            Method method = clazz.getDeclaredMethod("writeObject", ObjectOutputStream.class);
+            int modifiers = method.getModifiers();
+            return method.getReturnType() == Void.TYPE && !Modifier.isStatic(modifiers)
+                  && Modifier.isPrivate(modifiers);
+         }
+         catch (NoSuchMethodException e)
+         {
+            return false;
+         }
+      }
+   }
+}

Added: shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableOriginalImpl.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableOriginalImpl.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/serialization/ZipSerializableOriginalImpl.java	2010-05-29 01:13:58 UTC (rev 4453)
@@ -0,0 +1,235 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.shrinkwrap.impl.base.serialization;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipInputStream;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.api.importer.ZipImporter;
+import org.jboss.shrinkwrap.api.serialization.ZipSerializable;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.jboss.shrinkwrap.impl.base.AssignableBase;
+import org.jboss.shrinkwrap.impl.base.Validate;
+import org.jboss.shrinkwrap.impl.base.io.IOUtil;
+
+/**
+ * Copy of {@link ZipSerializableImpl} so we may test for 
+ * backwards-compatibility of the wire protocol.  This class is 
+ * *never to change* once we publish/lock the wire protocol.  It is 
+ * used to mock different ShrinkWrap versions communicating
+ * with one another.
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public class ZipSerializableOriginalImpl extends AssignableBase implements ZipSerializable
+{
+
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * serialVersionUID
+    */
+   private static final long serialVersionUID = 1L;
+
+   /**
+    * Logger
+    */
+   private static final Logger log = Logger.getLogger(ZipSerializableImpl.class.getName());
+
+   //-------------------------------------------------------------------------------------||
+   // Instance Members -------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Name of the archive; to be serialized
+    */
+   private final String name;
+
+   /**
+    * Underlying archive.  Won't be directly serialized; 
+    * instead we'll encode it as ZIP and send that
+    */
+   private transient Archive<?> archive;
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new instance, wrapping the specified {@link Archive}
+    */
+   public ZipSerializableOriginalImpl(final Archive<?> archive)
+   {
+      Validate.notNull(archive, "Archive must be specified");
+      final String name = archive.getName();
+      Validate.notNullOrEmpty(name, "Name of archive must be specified");
+      this.archive = archive;
+      this.name = name;
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.AssignableBase#getArchive()
+    */
+   @Override
+   protected Archive<?> getArchive()
+   {
+      assert archive != null : "Underlying archive may never be null; illegal state";
+      return archive;
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Serialization ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Serializes the invocation with a custom form
+    * 
+    * @serialData After all non-transient fields are written, we
+    * send the {@link Archive} contents encoded as ZIP.
+    */
+   private void writeObject(final ObjectOutputStream out) throws IOException
+   {
+      // Default write of non-transient fields
+      out.defaultWriteObject();
+
+      // Write as ZIP 
+      final InputStream in = archive.as(ZipExporter.class).exportZip();
+      try
+      {
+         IOUtil.copy(in, out); // Don't close the outstream
+      }
+      finally
+      {
+         // In case we get an InputStream type that supports closing
+         in.close();
+      }
+
+      // Log
+      if (log.isLoggable(Level.FINER))
+      {
+         log.finer("Wrote archive: " + archive.toString());
+      }
+   }
+
+   /**
+    * Deserializes according to the custom form 
+    * defined by {@link ZipSerializableImpl#writeObject(ObjectOutputStream)}
+    */
+   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
+   {
+      // Get default form
+      in.defaultReadObject();
+
+      // Create new Archive
+      final String name = this.name;
+      final ZipImporter archive = ShrinkWrap.create(ZipImporter.class, name);
+
+      // Read in archive
+      final ZipInputStream remains = new ZipInputStream(new DoNotDelegateCloseInputStream(in));
+
+      try
+      {
+         // Read in
+         archive.importZip(remains);
+
+         // Set
+         this.archive = archive.as(JavaArchive.class);
+      }
+      finally
+      {
+         // Close up our stream, but not the underlying ObjectInputStream
+         remains.close();
+      }
+
+      // Log
+      if (log.isLoggable(Level.FINER))
+      {
+         log.finer("Read in archive: " + archive.toString());
+      }
+
+      /*
+       * Leave this bit here.
+       * 
+       * After reading in the ZIP stream contents, we need to also 
+       * get to the EOF marker (which is not read in by the ZIP import process
+       * because it's the ZIP header, not part of the true contents.  Putting
+       * this loop here ensures we reach the marker, which is *not* the true 
+       * end of the stream.  Object data may be read again after here
+       * via something like:
+       * 
+       * in.readObject();
+       * 
+       * Without this loop we'll get an OptionalDataException when trying to
+       * read more objects in from the stream.  In the future we may add state
+       * which needs to be part of the serialization protocol, and things 
+       * need to stay in order, so they'll be added *after* the archive ZIP contents.
+       * Thus we must be able to read them.
+       */
+      while (in.read() != -1);
+
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Internal Helper Classes ------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * A {@link InputStream} which does not delegate the {@link InputStream#close()}
+    * operation to the wrapped delegate; we cannot close the {@link ObjectInputStream}
+    * passed into {@link ZipSerializableImpl#readObject(ObjectInputStream)}.
+    * 
+    * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+    * @version $Revision: $
+    */
+   private static class DoNotDelegateCloseInputStream extends FilterInputStream
+   {
+
+      protected DoNotDelegateCloseInputStream(final InputStream in)
+      {
+         super(in);
+      }
+
+      /**
+       * {@inheritDoc}
+       * @see java.io.FilterInputStream#close()
+       */
+      @Override
+      public void close() throws IOException
+      {
+         // NOOP
+      }
+
+   }
+
+}



More information about the jboss-svn-commits mailing list