1/* 2 * Copyright (C) 2010 The Android Open Source Project 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 */ 16 17package libcore.java.io; 18 19import java.io.ByteArrayInputStream; 20import java.io.ByteArrayOutputStream; 21import java.io.IOException; 22import java.io.ObjectInputStream; 23import java.io.ObjectOutputStream; 24import java.io.Serializable; 25import java.util.Arrays; 26import java.util.List; 27import junit.framework.TestCase; 28 29public final class ObjectOutputStreamTest extends TestCase { 30 public void testLongString() throws Exception { 31 // Most modified UTF-8 is limited to 64KiB, but serialized strings can have an 8-byte 32 // length, so this should never throw java.io.UTFDataFormatException... 33 StringBuilder sb = new StringBuilder(); 34 for (int i = 0; i < 64*1024 * 2; ++i) { 35 sb.append('a'); 36 } 37 String s = sb.toString(); 38 ObjectOutputStream os = new ObjectOutputStream(new ByteArrayOutputStream()); 39 os.writeObject(s); 40 } 41 42 public static class CallsCloseInWriteObjectMethod implements Serializable { 43 private String message; 44 45 public CallsCloseInWriteObjectMethod(String message) { 46 this.message = message; 47 } 48 49 private void writeObject(ObjectOutputStream oos) throws IOException { 50 oos.writeObject(message); 51 oos.close(); 52 } 53 54 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 55 message = (String) ois.readObject(); 56 } 57 58 @Override 59 public boolean equals(Object o) { 60 if (this == o) { 61 return true; 62 } 63 if (o == null || getClass() != o.getClass()) { 64 return false; 65 } 66 67 CallsCloseInWriteObjectMethod that = (CallsCloseInWriteObjectMethod) o; 68 69 return message.equals(that.message); 70 } 71 72 @Override 73 public int hashCode() { 74 return message.hashCode(); 75 } 76 } 77 78 // http://b/28159133 79 public void testCloseInWriteObject() throws Exception { 80 String hello = "Hello"; 81 CallsCloseInWriteObjectMethod object = new CallsCloseInWriteObjectMethod(hello); 82 // This reproduces the problem in http://b/28159133 as follows: 83 // the list class gets handle N 84 // the object closes the ObjectOutputStream and clears the handle table 85 // the hello gets handle N 86 // the reuse of hello has a reference to handle N 87 // When it is deserialized the list contains object, hello, Arrays.asList().getClass() 88 // instead of object, hello, hello. 89 List<Serializable> input = Arrays.asList(object, hello, hello); 90 @SuppressWarnings("unchecked") 91 List<CallsCloseInWriteObjectMethod> output = (List<CallsCloseInWriteObjectMethod>) 92 roundTrip(input); 93 94 assertEquals(input, output); 95 } 96 97 private Serializable roundTrip(Object object) 98 throws IOException, ClassNotFoundException { 99 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 100 try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 101 oos.writeObject(object); 102 } 103 104 Serializable read; 105 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 106 try (ObjectInputStream ois = new ObjectInputStream(bais)) { 107 read = (Serializable) ois.readObject(); 108 } 109 return read; 110 } 111} 112