1/**
2 * Copyright 2006-2013 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.objenesis.instantiator.basic;
17
18import java.io.ByteArrayOutputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21import java.io.InputStream;
22import java.io.NotSerializableException;
23import java.io.ObjectInputStream;
24import java.io.ObjectStreamClass;
25import java.io.ObjectStreamConstants;
26import java.io.Serializable;
27
28import org.objenesis.ObjenesisException;
29import org.objenesis.instantiator.ObjectInstantiator;
30
31/**
32 * Instantiates a class by using a dummy input stream that always feeds data for an empty object of
33 * the same kind. NOTE: This instantiator may not work properly if the class being instantiated
34 * defines a "readResolve" method, since it may return objects that have been returned previously
35 * (i.e., there's no guarantee that the returned object is a new one), or even objects from a
36 * completely different class.
37 *
38 * @author Leonardo Mesquita
39 * @see org.objenesis.instantiator.ObjectInstantiator
40 */
41public class ObjectInputStreamInstantiator implements ObjectInstantiator {
42   private static class MockStream extends InputStream {
43
44      private int pointer;
45      private byte[] data;
46      private int sequence;
47      private static final int[] NEXT = new int[] {1, 2, 2};
48      private byte[][] buffers;
49
50      private final byte[] FIRST_DATA;
51      private static byte[] HEADER;
52      private static byte[] REPEATING_DATA;
53
54      static {
55         initialize();
56      }
57
58      private static void initialize() {
59         try {
60            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
61            DataOutputStream dout = new DataOutputStream(byteOut);
62            dout.writeShort(ObjectStreamConstants.STREAM_MAGIC);
63            dout.writeShort(ObjectStreamConstants.STREAM_VERSION);
64            HEADER = byteOut.toByteArray();
65
66            byteOut = new ByteArrayOutputStream();
67            dout = new DataOutputStream(byteOut);
68
69            dout.writeByte(ObjectStreamConstants.TC_OBJECT);
70            dout.writeByte(ObjectStreamConstants.TC_REFERENCE);
71            dout.writeInt(ObjectStreamConstants.baseWireHandle);
72            REPEATING_DATA = byteOut.toByteArray();
73         }
74         catch(IOException e) {
75            throw new Error("IOException: " + e.getMessage());
76         }
77
78      }
79
80      public MockStream(Class clazz) {
81         this.pointer = 0;
82         this.sequence = 0;
83         this.data = HEADER;
84
85         // (byte) TC_OBJECT
86         // (byte) TC_CLASSDESC
87         // (short length)
88         // (byte * className.length)
89         // (long)serialVersionUID
90         // (byte) SC_SERIALIZABLE
91         // (short)0 <fields>
92         // TC_ENDBLOCKDATA
93         // TC_NULL
94         ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
95         DataOutputStream dout = new DataOutputStream(byteOut);
96         try {
97            dout.writeByte(ObjectStreamConstants.TC_OBJECT);
98            dout.writeByte(ObjectStreamConstants.TC_CLASSDESC);
99            dout.writeUTF(clazz.getName());
100            dout.writeLong(ObjectStreamClass.lookup(clazz).getSerialVersionUID());
101            dout.writeByte(ObjectStreamConstants.SC_SERIALIZABLE);
102            dout.writeShort((short) 0); // Zero fields
103            dout.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA);
104            dout.writeByte(ObjectStreamConstants.TC_NULL);
105         }
106         catch(IOException e) {
107            throw new Error("IOException: " + e.getMessage());
108         }
109         this.FIRST_DATA = byteOut.toByteArray();
110         buffers = new byte[][] {HEADER, FIRST_DATA, REPEATING_DATA};
111      }
112
113      private void advanceBuffer() {
114         pointer = 0;
115         sequence = NEXT[sequence];
116         data = buffers[sequence];
117      }
118
119      public int read() throws IOException {
120         int result = data[pointer++];
121         if(pointer >= data.length) {
122            advanceBuffer();
123         }
124
125         return result;
126      }
127
128      public int available() throws IOException {
129         return Integer.MAX_VALUE;
130      }
131
132      public int read(byte[] b, int off, int len) throws IOException {
133         int left = len;
134         int remaining = data.length - pointer;
135
136         while(remaining <= left) {
137            System.arraycopy(data, pointer, b, off, remaining);
138            off += remaining;
139            left -= remaining;
140            advanceBuffer();
141            remaining = data.length - pointer;
142         }
143         if(left > 0) {
144            System.arraycopy(data, pointer, b, off, left);
145            pointer += left;
146         }
147
148         return len;
149      }
150   }
151
152   private ObjectInputStream inputStream;
153
154   public ObjectInputStreamInstantiator(Class clazz) {
155      if(Serializable.class.isAssignableFrom(clazz)) {
156         try {
157            this.inputStream = new ObjectInputStream(new MockStream(clazz));
158         }
159         catch(IOException e) {
160            throw new Error("IOException: " + e.getMessage());
161         }
162      }
163      else {
164    	  throw new ObjenesisException(new NotSerializableException(clazz+" not serializable"));
165      }
166   }
167
168   public Object newInstance() {
169      try {
170         return inputStream.readObject();
171      }
172      catch(ClassNotFoundException e) {
173         throw new Error("ClassNotFoundException: " + e.getMessage());
174      }
175      catch(Exception e) {
176         throw new ObjenesisException(e);
177      }
178   }
179}
180