1/** 2 * Copyright 2006-2017 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; 30import org.objenesis.instantiator.annotations.Instantiator; 31import org.objenesis.instantiator.annotations.Typology; 32 33/** 34 * Instantiates a class by using a dummy input stream that always feeds data for an empty object of 35 * the same kind. NOTE: This instantiator may not work properly if the class being instantiated 36 * defines a "readResolve" method, since it may return objects that have been returned previously 37 * (i.e., there's no guarantee that the returned object is a new one), or even objects from a 38 * completely different class. 39 * 40 * @author Leonardo Mesquita 41 * @see org.objenesis.instantiator.ObjectInstantiator 42 */ 43@Instantiator(Typology.SERIALIZATION) 44public class ObjectInputStreamInstantiator<T> implements ObjectInstantiator<T> { 45 private static class MockStream extends InputStream { 46 47 private int pointer; 48 private byte[] data; 49 private int sequence; 50 private static final int[] NEXT = new int[] {1, 2, 2}; 51 private byte[][] buffers; 52 53 private final byte[] FIRST_DATA; 54 private static byte[] HEADER; 55 private static byte[] REPEATING_DATA; 56 57 static { 58 initialize(); 59 } 60 61 private static void initialize() { 62 try { 63 ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); 64 DataOutputStream dout = new DataOutputStream(byteOut); 65 dout.writeShort(ObjectStreamConstants.STREAM_MAGIC); 66 dout.writeShort(ObjectStreamConstants.STREAM_VERSION); 67 HEADER = byteOut.toByteArray(); 68 69 byteOut = new ByteArrayOutputStream(); 70 dout = new DataOutputStream(byteOut); 71 72 dout.writeByte(ObjectStreamConstants.TC_OBJECT); 73 dout.writeByte(ObjectStreamConstants.TC_REFERENCE); 74 dout.writeInt(ObjectStreamConstants.baseWireHandle); 75 REPEATING_DATA = byteOut.toByteArray(); 76 } 77 catch(IOException e) { 78 throw new Error("IOException: " + e.getMessage()); 79 } 80 81 } 82 83 public MockStream(Class<?> clazz) { 84 this.pointer = 0; 85 this.sequence = 0; 86 this.data = HEADER; 87 88 // (byte) TC_OBJECT 89 // (byte) TC_CLASSDESC 90 // (short length) 91 // (byte * className.length) 92 // (long)serialVersionUID 93 // (byte) SC_SERIALIZABLE 94 // (short)0 <fields> 95 // TC_ENDBLOCKDATA 96 // TC_NULL 97 ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); 98 DataOutputStream dout = new DataOutputStream(byteOut); 99 try { 100 dout.writeByte(ObjectStreamConstants.TC_OBJECT); 101 dout.writeByte(ObjectStreamConstants.TC_CLASSDESC); 102 dout.writeUTF(clazz.getName()); 103 dout.writeLong(ObjectStreamClass.lookup(clazz).getSerialVersionUID()); 104 dout.writeByte(ObjectStreamConstants.SC_SERIALIZABLE); 105 dout.writeShort((short) 0); // Zero fields 106 dout.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA); 107 dout.writeByte(ObjectStreamConstants.TC_NULL); 108 } 109 catch(IOException e) { 110 throw new Error("IOException: " + e.getMessage()); 111 } 112 this.FIRST_DATA = byteOut.toByteArray(); 113 buffers = new byte[][] {HEADER, FIRST_DATA, REPEATING_DATA}; 114 } 115 116 private void advanceBuffer() { 117 pointer = 0; 118 sequence = NEXT[sequence]; 119 data = buffers[sequence]; 120 } 121 122 @Override 123 public int read() throws IOException { 124 int result = data[pointer++]; 125 if(pointer >= data.length) { 126 advanceBuffer(); 127 } 128 129 return result; 130 } 131 132 @Override 133 public int available() throws IOException { 134 return Integer.MAX_VALUE; 135 } 136 137 @Override 138 public int read(byte[] b, int off, int len) throws IOException { 139 int left = len; 140 int remaining = data.length - pointer; 141 142 while(remaining <= left) { 143 System.arraycopy(data, pointer, b, off, remaining); 144 off += remaining; 145 left -= remaining; 146 advanceBuffer(); 147 remaining = data.length - pointer; 148 } 149 if(left > 0) { 150 System.arraycopy(data, pointer, b, off, left); 151 pointer += left; 152 } 153 154 return len; 155 } 156 } 157 158 private ObjectInputStream inputStream; 159 160 public ObjectInputStreamInstantiator(Class<T> clazz) { 161 if(Serializable.class.isAssignableFrom(clazz)) { 162 try { 163 this.inputStream = new ObjectInputStream(new MockStream(clazz)); 164 } 165 catch(IOException e) { 166 throw new Error("IOException: " + e.getMessage()); 167 } 168 } 169 else { 170 throw new ObjenesisException(new NotSerializableException(clazz + " not serializable")); 171 } 172 } 173 174 @SuppressWarnings("unchecked") 175 public T newInstance() { 176 try { 177 return (T) inputStream.readObject(); 178 } 179 catch(ClassNotFoundException e) { 180 throw new Error("ClassNotFoundException: " + e.getMessage()); 181 } 182 catch(Exception e) { 183 throw new ObjenesisException(e); 184 } 185 } 186} 187