LOG4J2-1663 Ensure SortedArrayStringMap can be serialized and deserialized without errors regardless of content
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/4df78d0a Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/4df78d0a Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/4df78d0a Branch: refs/heads/LOG4J2-1661 Commit: 4df78d0ad5343593ff1a8b929d277d1f17f3f30f Parents: dee36ee Author: rpopma <[email protected]> Authored: Sat Nov 5 22:11:12 2016 +0900 Committer: rpopma <[email protected]> Committed: Sat Nov 5 22:11:12 2016 +0900 ---------------------------------------------------------------------- .../log4j/util/SortedArrayStringMap.java | 23 ++++++- .../logging/log4j/util/DeserializerHelper.java | 47 +++++++++++++ .../log4j/util/SortedArrayStringMapTest.java | 71 +++++++++++++++++++- src/changes/changes.xml | 3 + 4 files changed, 140 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4df78d0a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java index 07f8f0b..7b602b8 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java @@ -18,12 +18,15 @@ package org.apache.logging.log4j.util; import java.io.IOException; import java.io.InvalidObjectException; +import java.rmi.MarshalledObject; import java.util.Arrays; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import org.apache.logging.log4j.status.StatusLogger; + /** * <em>Consider this class private.</em> * Array-based implementation of the {@code ReadOnlyStringMap} interface. Keys are held in a sorted array. @@ -468,12 +471,16 @@ public class SortedArrayStringMap implements StringMap { if (size > 0) { for (int i = 0; i < size; i++) { s.writeObject(keys[i]); - s.writeObject(values[i]); + try { + s.writeObject(new MarshalledObject<>(values[i])); + } catch (final Exception e) { + handleSerializationException(e, i, keys[i]); + s.writeObject(null); + } } } } - /** * Calculate the next power of 2, greater than or equal to x. * <p> @@ -521,8 +528,18 @@ public class SortedArrayStringMap implements StringMap { // Read the keys and values, and put the mappings in the arrays for (int i = 0; i < mappings; i++) { keys[i] = (String) s.readObject(); - values[i] = s.readObject(); + try { + final MarshalledObject<Object> marshalledObject = (MarshalledObject<Object>) s.readObject(); + values[i] = marshalledObject == null ? null : marshalledObject.get(); + } catch (final Exception | LinkageError error) { + handleSerializationException(error, i, keys[i]); + values[i] = null; + } } size = mappings; } + + private void handleSerializationException(final Throwable t, final int i, final String key) { + StatusLogger.getLogger().warn("Ignoring {} for key[{}] ('{}')", String.valueOf(t), i, keys[i]); + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4df78d0a/log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java new file mode 100644 index 0000000..1fc8307 --- /dev/null +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.ObjectInputStream; + +/** + * Deserializes a specified file. + * + * @see SortedArrayStringMapTest#testDeserializationOfUnknownClass() + */ +public class DeserializerHelper { + public static void main(String... args) throws Exception { + final File file = new File(args[0]); + ObjectInputStream in = null; + try { + in = new ObjectInputStream(new FileInputStream(file)); + final Object result = in.readObject(); + System.out.println(result); + } catch (Throwable t) { + System.err.println("Could not deserialize: "); + t.printStackTrace(); + } finally { + try { + in.close(); + } catch (Throwable t) { + System.err.println("Error while closing: " + t); + } + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4df78d0a/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java index f4f0710..58386d2 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java @@ -1,4 +1,4 @@ -package org.apache.logging.log4j.util;/* +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,13 +14,17 @@ package org.apache.logging.log4j.util;/* * See the license for the specific language governing permissions and * limitations under the license. */ +package org.apache.logging.log4j.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; +import java.net.URL; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Map; @@ -70,6 +74,71 @@ public class SortedArrayStringMapTest { assertEquals(original, copy); } + @Test + public void testSerializationOfNonSerializableValue() throws Exception { + final SortedArrayStringMap original = new SortedArrayStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("unserializable", new Object()); + + final byte[] binary = serialize(original); + final SortedArrayStringMap copy = deserialize(binary); + + final SortedArrayStringMap expected = new SortedArrayStringMap(); + expected.putValue("a", "avalue"); + expected.putValue("B", "Bvalue"); + expected.putValue("unserializable", null); + assertEquals(expected, copy); + } + + @Test + public void testDeserializationOfUnknownClass() throws Exception { + final SortedArrayStringMap original = new SortedArrayStringMap(); + original.putValue("a", "avalue"); + original.putValue("serializableButNotInClasspathOfDeserializer", new org.junit.runner.Result()); + original.putValue("zz", "last"); + + final File file = new File("target/SortedArrayStringMap.ser"); + try (FileOutputStream fout = new FileOutputStream(file, false)) { + fout.write(serialize(original)); + fout.flush(); + } + final Process process = new ProcessBuilder("java", "-cp", + createClassPath(SortedArrayStringMap.class, DeserializerHelper.class), + DeserializerHelper.class.getName(), file.getPath()).inheritIO().start(); + int exitValue = process.waitFor(); + +// file.delete(); + assertEquals("no error", 0, exitValue); + } + + private String createClassPath(Class<?>... classes) { + final StringBuilder result = new StringBuilder(); + for (final Class<?> cls : classes) { + if (result.length() > 0) { + result.append(File.pathSeparator); + } + result.append(createClassPath(cls)); + } + return result.toString(); + } + + private String createClassPath(Class<?> cls) { + final String resource = "/" + cls.getName().replace('.', '/') + ".class"; + final URL url = cls.getResource(resource); + String location = url.toString(); + if (location.startsWith("jar:")) { + location = location.substring("jar:".length(), location.indexOf('!')); + } + if (location.startsWith("file:/")) { + location = location.substring("file:/".length()); + } + if (location.endsWith(resource)) { + location = location.substring(0, location.length() - resource.length()); + } + return location.isEmpty() ? "." : location; + } + private byte[] serialize(final SortedArrayStringMap data) throws IOException { final ByteArrayOutputStream arr = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(arr); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4df78d0a/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d017257..f2b9fa5 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -24,6 +24,9 @@ </properties> <body> <release version="2.8" date="2016-MM-DD" description="GA Release 2.8"> + <action issue="LOG4J2-1663" dev="rpopma" type="fix"> + Ensure SortedArrayStringMap can be serialized and deserialized without errors regardless of content. + </action> <action issue="LOG4J2-1660" dev="rpopma" type="add"> Added public method ThreadContext::getThreadContextMap; removed class ThreadContextAccess. </action>
