View Javadoc

1   package com.atlassian.vcache.marshallers;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.ByteArrayOutputStream;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.io.ObjectInputStream;
8   import java.io.ObjectOutputStream;
9   import java.io.ObjectStreamClass;
10  import java.io.Serializable;
11  import java.util.Optional;
12  import javax.annotation.Nonnull;
13  
14  import com.atlassian.annotations.Internal;
15  import com.atlassian.vcache.Marshaller;
16  import com.atlassian.vcache.MarshallerException;
17  
18  import static java.util.Objects.requireNonNull;
19  
20  /**
21   * Implementation for {@link Serializable} objects that uses the standard Java Serialization mechanism. Any
22   * IOExceptions are wrapped as {@link MarshallerException}s.
23   *
24   * @param <T> the type being marshalled
25   *
26   * @since 1.0
27   */
28  @Internal
29  class JavaSerializationMarshaller<T extends Serializable> implements Marshaller<T>
30  {
31      private final Class<T> clazz;
32      private final Optional<ClassLoader> loader;
33  
34      /**
35       * Creates an instance that uses the default implementation of
36       * {@link java.io.ObjectInputStream#resolveClass(ObjectStreamClass)} to resolve {@link Class} objects.
37       */
38      JavaSerializationMarshaller(Class<T> clazz)
39      {
40          this.clazz = requireNonNull(clazz);
41          this.loader = Optional.empty();
42      }
43  
44      /**
45       * Creates an instance that uses the supplied {@link ClassLoader} to resolve
46       * {@link Class} objects.
47       */
48      JavaSerializationMarshaller(Class<T> clazz, ClassLoader loader)
49      {
50          this.clazz = requireNonNull(clazz);
51          this.loader = Optional.of(loader);
52      }
53  
54      @Nonnull
55      @Override
56      public byte[] marshall(T obj) throws MarshallerException
57      {
58          final ByteArrayOutputStream baos = new ByteArrayOutputStream();
59          try (ObjectOutputStream oos = new ObjectOutputStream(baos))
60          {
61              oos.writeObject(obj);
62          }
63          catch (IOException ioe)
64          {
65              throw new MarshallerException("Unable to marshall", ioe);
66          }
67  
68          return baos.toByteArray();
69      }
70  
71      @Nonnull
72      @Override
73      public T unmarshall(byte[] raw) throws MarshallerException
74      {
75          final ByteArrayInputStream bais = new ByteArrayInputStream(raw);
76          try (ObjectInputStream ois = createObjectInputStream(bais))
77          {
78              return clazz.cast(ois.readObject());
79          }
80          catch (ClassCastException | ClassNotFoundException | IOException ex)
81          {
82              throw new MarshallerException("Unable to unmarshall", ex);
83          }
84      }
85  
86      private ObjectInputStream createObjectInputStream(InputStream istr) throws IOException
87      {
88          return loader.isPresent()
89                  ? new ObjectInputStreamWithLoader(istr, loader.get())
90                  : new ObjectInputStream(istr);
91      }
92  
93      private static class ObjectInputStreamWithLoader extends ObjectInputStream
94      {
95          private final ClassLoader loader;
96  
97          public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader) throws IOException
98          {
99              super(in);
100             this.loader = requireNonNull(loader);
101         }
102 
103         @Override
104         protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException
105         {
106             return Class.forName(desc.getName(), false, loader);
107         }
108     }
109 }