View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3;
18  
19  import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertNotSame;
26  import static org.junit.jupiter.api.Assertions.assertNull;
27  import static org.junit.jupiter.api.Assertions.assertSame;
28  import static org.junit.jupiter.api.Assertions.assertThrows;
29  import static org.junit.jupiter.api.Assertions.assertTrue;
30  
31  import java.io.ByteArrayInputStream;
32  import java.io.ByteArrayOutputStream;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.ObjectInputStream;
36  import java.io.ObjectOutputStream;
37  import java.io.OutputStream;
38  import java.io.Serializable;
39  import java.lang.reflect.Constructor;
40  import java.lang.reflect.Modifier;
41  import java.util.HashMap;
42  import java.util.function.Supplier;
43  
44  import org.junit.jupiter.api.BeforeEach;
45  import org.junit.jupiter.api.Test;
46  
47  final class ClassNotFoundSerialization implements Serializable {
48  
49      private static final long serialVersionUID = 1L;
50  
51      private void readObject(final ObjectInputStream in) throws ClassNotFoundException    {
52          throw new ClassNotFoundException(SerializationUtilsTest.CLASS_NOT_FOUND_MESSAGE);
53      }
54  }
55  
56  interface SerializableSupplier<T> extends Supplier<T>, Serializable {
57      // empty
58  }
59  
60  /**
61   * Tests {@link SerializationUtils}.
62   */
63  class SerializationUtilsTest extends AbstractLangTest {
64  
65      static final String CLASS_NOT_FOUND_MESSAGE = "ClassNotFoundSerialization.readObject fake exception";
66      protected static final String SERIALIZE_IO_EXCEPTION_MESSAGE = "Anonymous OutputStream I/O exception";
67  
68      private String iString;
69      private Integer iInteger;
70      private HashMap<Object, Object> iMap;
71  
72      @BeforeEach
73      public void setUp() {
74          iString = "foo";
75          iInteger = Integer.valueOf(7);
76          iMap = new HashMap<>();
77          iMap.put("FOO", iString);
78          iMap.put("BAR", iInteger);
79      }
80  
81      @Test
82      void testClone() {
83          final Object test = SerializationUtils.clone(iMap);
84          assertNotNull(test);
85          assertInstanceOf(HashMap.class, test);
86          assertNotSame(test, iMap);
87          final HashMap<?, ?> testMap = (HashMap<?, ?>) test;
88          assertEquals(iString, testMap.get("FOO"));
89          assertNotSame(iString, testMap.get("FOO"));
90          assertEquals(iInteger, testMap.get("BAR"));
91          assertNotSame(iInteger, testMap.get("BAR"));
92          assertEquals(iMap, testMap);
93      }
94  
95      @Test
96      void testCloneNull() {
97          final Object test = SerializationUtils.clone(null);
98          assertNull(test);
99      }
100 
101     @Test
102     void testCloneSerializableSupplier() {
103         final SerializableSupplier<String> supplier = () -> "test";
104         assertEquals("test", supplier.get());
105         final SerializableSupplier<String> clone = SerializationUtils.clone(supplier);
106         assertEquals("test", clone.get());
107     }
108 
109     @Test
110     void testCloneUnserializable() {
111         iMap.put(new Object(), new Object());
112         assertThrows(SerializationException.class, () -> SerializationUtils.clone(iMap));
113     }
114 
115     @Test
116     void testConstructor() {
117         assertNotNull(new SerializationUtils());
118         final Constructor<?>[] cons = SerializationUtils.class.getDeclaredConstructors();
119         assertEquals(1, cons.length);
120         assertTrue(Modifier.isPublic(cons[0].getModifiers()));
121         assertTrue(Modifier.isPublic(SerializationUtils.class.getModifiers()));
122         assertFalse(Modifier.isFinal(SerializationUtils.class.getModifiers()));
123     }
124 
125     @Test
126     void testDeserializeBytes() throws Exception {
127         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
128         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
129             oos.writeObject(iMap);
130             oos.flush();
131         }
132 
133         final Object test = SerializationUtils.deserialize(streamReal.toByteArray());
134         assertNotNull(test);
135         assertInstanceOf(HashMap.class, test);
136         assertNotSame(test, iMap);
137         final HashMap<?, ?> testMap = (HashMap<?, ?>) test;
138         assertEquals(iString, testMap.get("FOO"));
139         assertNotSame(iString, testMap.get("FOO"));
140         assertEquals(iInteger, testMap.get("BAR"));
141         assertNotSame(iInteger, testMap.get("BAR"));
142         assertEquals(iMap, testMap);
143     }
144 
145     @Test
146     void testDeserializeBytesBadStream() {
147         assertThrows(SerializationException.class, () -> SerializationUtils.deserialize(new byte[0]));
148     }
149 
150     @Test
151     void testDeserializeBytesNull() {
152         assertNullPointerException(() -> SerializationUtils.deserialize((byte[]) null));
153     }
154 
155     @Test
156     void testDeserializeBytesOfNull() throws Exception {
157         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
158         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
159             oos.writeObject(null);
160             oos.flush();
161         }
162 
163         final Object test = SerializationUtils.deserialize(streamReal.toByteArray());
164         assertNull(test);
165     }
166 
167     @Test
168     void testDeserializeClassCastException() {
169         final String value = "Hello";
170         final byte[] serialized = SerializationUtils.serialize(value);
171         assertEquals(value, SerializationUtils.deserialize(serialized));
172         assertThrows(ClassCastException.class, () -> {
173             // Causes ClassCastException in call site, not in SerializationUtils.deserialize
174             @SuppressWarnings("unused") // needed to cause Exception
175             final Integer i = SerializationUtils.deserialize(serialized);
176         });
177     }
178 
179     @Test
180     void testDeserializeStream() throws Exception {
181         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
182         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
183             oos.writeObject(iMap);
184             oos.flush();
185         }
186 
187         final ByteArrayInputStream inTest = new ByteArrayInputStream(streamReal.toByteArray());
188         final Object test = SerializationUtils.deserialize(inTest);
189         assertNotNull(test);
190         assertInstanceOf(HashMap.class, test);
191         assertNotSame(test, iMap);
192         final HashMap<?, ?> testMap = (HashMap<?, ?>) test;
193         assertEquals(iString, testMap.get("FOO"));
194         assertNotSame(iString, testMap.get("FOO"));
195         assertEquals(iInteger, testMap.get("BAR"));
196         assertNotSame(iInteger, testMap.get("BAR"));
197         assertEquals(iMap, testMap);
198     }
199 
200     @Test
201     void testDeserializeStreamBadStream() {
202         assertThrows(SerializationException.class, () -> SerializationUtils.deserialize(new ByteArrayInputStream(new byte[0])));
203     }
204 
205     @Test
206     void testDeserializeStreamClassNotFound() throws Exception {
207         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
208         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
209             oos.writeObject(new ClassNotFoundSerialization());
210             oos.flush();
211         }
212 
213         final ByteArrayInputStream inTest = new ByteArrayInputStream(streamReal.toByteArray());
214         final SerializationException se = assertThrows(SerializationException.class, () -> SerializationUtils.deserialize(inTest));
215         assertEquals("java.lang.ClassNotFoundException: " + CLASS_NOT_FOUND_MESSAGE, se.getMessage());
216     }
217 
218     @Test
219     void testDeserializeStreamNull() {
220         assertNullPointerException(() -> SerializationUtils.deserialize((InputStream) null));
221     }
222 
223     @Test
224     void testDeserializeStreamOfNull() throws Exception {
225         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
226         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
227             oos.writeObject(null);
228             oos.flush();
229         }
230 
231         final ByteArrayInputStream inTest = new ByteArrayInputStream(streamReal.toByteArray());
232         final Object test = SerializationUtils.deserialize(inTest);
233         assertNull(test);
234     }
235 
236     @Test
237     void testException() {
238         SerializationException serEx;
239         final Exception ex = new Exception();
240 
241         serEx = new SerializationException();
242         assertSame(null, serEx.getMessage());
243         assertSame(null, serEx.getCause());
244 
245         serEx = new SerializationException("Message");
246         assertSame("Message", serEx.getMessage());
247         assertSame(null, serEx.getCause());
248 
249         serEx = new SerializationException(ex);
250         assertEquals("java.lang.Exception", serEx.getMessage());
251         assertSame(ex, serEx.getCause());
252 
253         serEx = new SerializationException("Message", ex);
254         assertSame("Message", serEx.getMessage());
255         assertSame(ex, serEx.getCause());
256     }
257 
258     @Test
259     void testNegativeByteArray() {
260         final byte[] byteArray = {
261             (byte) -84, (byte) -19, (byte) 0, (byte) 5, (byte) 125, (byte) -19, (byte) 0,
262             (byte) 5, (byte) 115, (byte) 114, (byte) -1, (byte) 97, (byte) 122, (byte) -48, (byte) -65
263         };
264 
265         assertThrows(SerializationException.class, () -> SerializationUtils.deserialize(new ByteArrayInputStream(byteArray)));
266     }
267 
268     @Test
269     void testPrimitiveTypeClassSerialization() {
270         final Class<?>[] primitiveTypes = { byte.class, short.class, int.class, long.class, float.class, double.class,
271                 boolean.class, char.class, void.class };
272 
273         for (final Class<?> primitiveType : primitiveTypes) {
274             final Class<?> clone = SerializationUtils.clone(primitiveType);
275             assertEquals(primitiveType, clone);
276         }
277     }
278 
279     @Test
280     void testRoundtrip() {
281         final HashMap<Object, Object> newMap = SerializationUtils.roundtrip(iMap);
282         assertEquals(iMap, newMap);
283     }
284 
285     @Test
286     void testSerializeBytes() throws Exception {
287         final byte[] testBytes = SerializationUtils.serialize(iMap);
288 
289         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
290         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
291             oos.writeObject(iMap);
292             oos.flush();
293         }
294 
295         final byte[] realBytes = streamReal.toByteArray();
296         assertEquals(testBytes.length, realBytes.length);
297         assertArrayEquals(realBytes, testBytes);
298     }
299 
300     @Test
301     void testSerializeBytesNull() throws Exception {
302         final byte[] testBytes = SerializationUtils.serialize(null);
303 
304         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
305         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
306             oos.writeObject(null);
307             oos.flush();
308         }
309 
310         final byte[] realBytes = streamReal.toByteArray();
311         assertEquals(testBytes.length, realBytes.length);
312         assertArrayEquals(realBytes, testBytes);
313     }
314 
315     @Test
316     void testSerializeBytesUnserializable() {
317         iMap.put(new Object(), new Object());
318         assertThrows(SerializationException.class, () -> SerializationUtils.serialize(iMap));
319     }
320 
321     @Test
322     void testSerializeIOException() {
323         // forces an IOException when the ObjectOutputStream is created, to test not closing the stream
324         // in the finally block
325         final OutputStream streamTest = new OutputStream() {
326             @Override
327             public void write(final int arg0) throws IOException {
328                 throw new IOException(SERIALIZE_IO_EXCEPTION_MESSAGE);
329             }
330         };
331         final SerializationException e =
332                 assertThrows(SerializationException.class, () -> SerializationUtils.serialize(iMap, streamTest));
333         assertEquals("java.io.IOException: " + SERIALIZE_IO_EXCEPTION_MESSAGE, e.getMessage());
334     }
335 
336     @Test
337     void testSerializeStream() throws Exception {
338         final ByteArrayOutputStream streamTest = new ByteArrayOutputStream();
339         SerializationUtils.serialize(iMap, streamTest);
340 
341         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
342         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
343             oos.writeObject(iMap);
344             oos.flush();
345         }
346 
347         final byte[] testBytes = streamTest.toByteArray();
348         final byte[] realBytes = streamReal.toByteArray();
349         assertEquals(testBytes.length, realBytes.length);
350         assertArrayEquals(realBytes, testBytes);
351     }
352 
353     @Test
354     void testSerializeStreamNullNull() {
355         assertNullPointerException(() -> SerializationUtils.serialize(null, null));
356     }
357 
358     @Test
359     void testSerializeStreamNullObj() throws Exception {
360         final ByteArrayOutputStream streamTest = new ByteArrayOutputStream();
361         SerializationUtils.serialize(null, streamTest);
362 
363         final ByteArrayOutputStream streamReal = new ByteArrayOutputStream();
364         try (ObjectOutputStream oos = new ObjectOutputStream(streamReal)) {
365             oos.writeObject(null);
366             oos.flush();
367         }
368 
369         final byte[] testBytes = streamTest.toByteArray();
370         final byte[] realBytes = streamReal.toByteArray();
371         assertEquals(testBytes.length, realBytes.length);
372         assertArrayEquals(realBytes, testBytes);
373     }
374 
375     @Test
376     void testSerializeStreamObjNull() {
377         assertNullPointerException(() -> SerializationUtils.serialize(iMap, null));
378     }
379 
380     @Test
381     void testSerializeStreamUnserializable() {
382         final ByteArrayOutputStream streamTest = new ByteArrayOutputStream();
383         iMap.put(new Object(), new Object());
384         assertThrows(SerializationException.class, () -> SerializationUtils.serialize(iMap, streamTest));
385     }
386 }