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