Author: pedro Date: Mon Jan 24 15:48:57 2011 New Revision: 1062841 URL: http://svn.apache.org/viewvc?rev=1062841&view=rev Log: changing the SerializableChecker to catch runtime exceptions while scanning fields Issue: WICKET-3354
Added: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/Log4jEventHistory.java wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/io/SerializableCheckerTest.java Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/util/io/SerializableChecker.java Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/util/io/SerializableChecker.java URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/util/io/SerializableChecker.java?rev=1062841&r1=1062840&r2=1062841&view=diff ============================================================================== --- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/util/io/SerializableChecker.java (original) +++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/util/io/SerializableChecker.java Mon Jan 24 15:48:57 2011 @@ -341,8 +341,22 @@ public final class SerializableChecker e return; } - if (stack.contains(obj)) + try + { + if (stack.contains(obj)) + { + return; + } + } + catch (RuntimeException e) { + log.warn("Wasn't possible to check the object " + obj.getClass() + + " possible due an problematic implementation of equals method"); + /* + * Can't check if this obj were in stack, giving up because we don't want to throw an + * invaluable exception to user. The main goal of this checker is to find non + * serializable data + */ return; } @@ -381,7 +395,7 @@ public final class SerializableChecker e { desc = (ObjectStreamClass)LOOKUP_METHOD.invoke(null, cls, Boolean.TRUE); Class<?> repCl; - if (!(Boolean) HAS_WRITE_REPLACE_METHOD_METHOD.invoke(desc, (Object[]) null) || + if (!(Boolean)HAS_WRITE_REPLACE_METHOD_METHOD.invoke(desc, (Object[])null) || (obj = INVOKE_WRITE_REPLACE_METHOD.invoke(desc, obj)) == null || (repCl = obj.getClass()) == cls) { @@ -546,7 +560,7 @@ public final class SerializableChecker e { Field descField = slot.getClass().getDeclaredField("desc"); descField.setAccessible(true); - slotDesc = (ObjectStreamClass) descField.get(slot); + slotDesc = (ObjectStreamClass)descField.get(slot); } catch (Exception e) { @@ -567,7 +581,7 @@ public final class SerializableChecker e int numFields; try { - numFields = (Integer) GET_NUM_OBJ_FIELDS_METHOD.invoke(desc, (Object[]) null); + numFields = (Integer)GET_NUM_OBJ_FIELDS_METHOD.invoke(desc, (Object[])null); } catch (IllegalAccessException e) { @@ -640,7 +654,7 @@ public final class SerializableChecker e */ private StringBuilder currentPath() { - StringBuilder b = new StringBuilder(); + StringBuilder b = new StringBuilder(); for (Iterator<String> it = nameStack.iterator(); it.hasNext();) { b.append(it.next()); @@ -661,8 +675,8 @@ public final class SerializableChecker e */ private final String toPrettyPrintedStack(String type) { - StringBuilder result = new StringBuilder(); - StringBuilder spaces = new StringBuilder(); + StringBuilder result = new StringBuilder(); + StringBuilder spaces = new StringBuilder(); result.append("Unable to serialize class: "); result.append(type); result.append("\nField hierarchy is:"); Added: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/Log4jEventHistory.java URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/Log4jEventHistory.java?rev=1062841&view=auto ============================================================================== --- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/Log4jEventHistory.java (added) +++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/Log4jEventHistory.java Mon Jan 24 15:48:57 2011 @@ -0,0 +1,72 @@ +/* + * 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.wicket.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Level; +import org.apache.log4j.spi.LoggingEvent; + +/** + * Log the log4j messages for further assertions + * */ +public class Log4jEventHistory extends AppenderSkeleton +{ + private List<LoggingEvent> history = new ArrayList<LoggingEvent>(); + + /** + * @return log history + */ + public List<LoggingEvent> getHistory() + { + return history; + } + + public void close() + { + } + + public boolean requiresLayout() + { + return false; + } + + @Override + protected void append(LoggingEvent event) + { + history.add(event); + } + + /** + * @param level + * @param msg + * @return if this message was logged + */ + public boolean contains(Level level, String msg) + { + for (LoggingEvent event : history) + { + if (msg.equals(event.getMessage()) && level.equals(event.getLevel())) + { + return true; + } + } + return false; + } +} \ No newline at end of file Added: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/io/SerializableCheckerTest.java URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/io/SerializableCheckerTest.java?rev=1062841&view=auto ============================================================================== --- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/io/SerializableCheckerTest.java (added) +++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/util/io/SerializableCheckerTest.java Mon Jan 24 15:48:57 2011 @@ -0,0 +1,121 @@ +/* + * 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.wicket.util.io; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.Serializable; + +import junit.framework.TestCase; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.apache.wicket.util.Log4jEventHistory; +import org.apache.wicket.util.io.SerializableChecker.WicketNotSerializableException; + +/** + * @author Pedro Santos + */ +public class SerializableCheckerTest extends TestCase +{ + + /** + * Asserting an meaningful message get logged on console when serializable checker is testing + * problematic {@link Object#equals(Object)} method implementations. + * + * @see <a href="https://issues.apache.org/jira/browse/WICKET-3354">WICKET-3354</a> + * @throws IOException + */ + public void testRuntimeExceptionTolerance() throws IOException + { + Logger logger = LogManager.getLogger(SerializableChecker.class); + logger.setLevel(Level.WARN); + Log4jEventHistory logHistory = new Log4jEventHistory(); + logger.addAppender(logHistory); + SerializableChecker serializableChecker = new SerializableChecker( + new NotSerializableException()); + try + { + serializableChecker.writeObject(new TestType1()); + String expectedMessage = "Wasn't possible to check the object class org.apache.wicket.util.io.SerializableCheckerTest$ProblematicType possible due an problematic implementation of equals method"; + assertTrue(logHistory.contains(Level.WARN, expectedMessage)); + } + catch (TestException notMeaningfulException) + { + fail("Should have just logged on console, the checker is after another problem"); + } + } + + /** + * @throws IOException + */ + public void testNonSerializableTypeDetection() throws IOException + { + SerializableChecker serializableChecker = new SerializableChecker( + new NotSerializableException()); + String exceptionMessage = null; + try + { + serializableChecker.writeObject(new TestType2()); + } + catch (WicketNotSerializableException e) + { + exceptionMessage = e.getMessage(); + } + assertTrue(exceptionMessage.contains(NonSerializableType.class.getName())); + } + + private static class TestType1 implements Serializable + { + private static final long serialVersionUID = 1L; + ProblematicType problematicType = new ProblematicType(); + } + + private static class TestType2 implements Serializable + { + private static final long serialVersionUID = 1L; + ProblematicType problematicType = new ProblematicType(); + SerializableType serializableType = new SerializableType(); + NonSerializableType nonSerializable = new NonSerializableType(); + } + + private static class NonSerializableType + { + + } + + private static class SerializableType implements Serializable + { + private static final long serialVersionUID = 1L; + } + private static class TestException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + } + private static class ProblematicType implements Serializable + { + private static final long serialVersionUID = 1L; + + @Override + public boolean equals(Object obj) + { + throw new TestException(); + } + } +}