diff --git a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableJsonSerializer.java b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/client/RemoteThrowableJsonSerializer.java similarity index 71% rename from extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableJsonSerializer.java rename to extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/client/RemoteThrowableJsonSerializer.java index bb07865d..7f2d89f8 100644 --- a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableJsonSerializer.java +++ b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/client/RemoteThrowableJsonSerializer.java @@ -20,12 +20,11 @@ import com.github.nmorel.gwtjackson.client.JsonSerializer; import com.github.nmorel.gwtjackson.client.JsonSerializerParameters; import com.github.nmorel.gwtjackson.client.stream.JsonWriter; +import com.github.nmorel.gwtjackson.remotelogging.shared.RemoteThrowable; -public class ThrowableJsonSerializer extends JsonSerializer { +public class RemoteThrowableJsonSerializer extends JsonSerializer { - private static final ThrowableJsonSerializer INSTANCE = new ThrowableJsonSerializer(); - - private static final String JSON_TYPE_INFO = "@class"; + private static final RemoteThrowableJsonSerializer INSTANCE = new RemoteThrowableJsonSerializer(); private static final String CAUSE = "cause"; @@ -33,19 +32,21 @@ public class ThrowableJsonSerializer extends JsonSerializer { private static final String MESSAGE = "message"; - private static final String LOCALIZED_MESSAGE = "localizedMessage"; + private static final String REMOTE_CLASS_NAME = "remoteClassName"; - public static ThrowableJsonSerializer getInstance() { + public static RemoteThrowableJsonSerializer getInstance() { return INSTANCE; } - private ThrowableJsonSerializer() { } + private RemoteThrowableJsonSerializer() { } @Override - protected void doSerialize( JsonWriter writer, Throwable throwable, JsonSerializationContext ctx, JsonSerializerParameters params ) { + protected void doSerialize( JsonWriter writer, RemoteThrowable throwable, JsonSerializationContext ctx, JsonSerializerParameters + params ) { writer.beginObject(); - writer.name( JSON_TYPE_INFO ); - writer.value( throwable.getClass().getName() ); + + writer.name( MESSAGE ); + writer.value( throwable.getMessage() ); writer.name( CAUSE ); if ( throwable.getCause() != null ) { @@ -54,6 +55,9 @@ protected void doSerialize( JsonWriter writer, Throwable throwable, JsonSerializ writer.nullValue(); } + writer.name( REMOTE_CLASS_NAME ); + writer.value( throwable.getRemoteClassName() ); + writer.name( STACK_TRACE ); writer.beginArray(); for ( StackTraceElement stackTraceElement : throwable.getStackTrace() ) { @@ -61,12 +65,6 @@ protected void doSerialize( JsonWriter writer, Throwable throwable, JsonSerializ } writer.endArray(); - writer.name( MESSAGE ); - writer.value( throwable.getMessage() ); - - writer.name( LOCALIZED_MESSAGE ); - writer.value( throwable.getLocalizedMessage() ); - writer.endObject(); } } \ No newline at end of file diff --git a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/rebind/RemoteLoggingConfiguration.java b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/rebind/RemoteLoggingConfiguration.java index 8625843b..32399ae3 100644 --- a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/rebind/RemoteLoggingConfiguration.java +++ b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/rebind/RemoteLoggingConfiguration.java @@ -17,15 +17,16 @@ package com.github.nmorel.gwtjackson.remotelogging.rebind; import com.github.nmorel.gwtjackson.client.AbstractConfiguration; +import com.github.nmorel.gwtjackson.remotelogging.client.RemoteThrowableJsonSerializer; import com.github.nmorel.gwtjackson.remotelogging.client.StackTraceElementJsonSerializer; -import com.github.nmorel.gwtjackson.remotelogging.client.ThrowableJsonSerializer; +import com.github.nmorel.gwtjackson.remotelogging.shared.RemoteThrowable; public class RemoteLoggingConfiguration extends AbstractConfiguration { @Override protected void configure() { type( StackTraceElement.class ).serializer( StackTraceElementJsonSerializer.class ); - type( Throwable.class ).serializer( ThrowableJsonSerializer.class ); + type( RemoteThrowable.class ).serializer( RemoteThrowableJsonSerializer.class ); } } diff --git a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModule.java b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModule.java index b0f8f6ad..1cce0a00 100644 --- a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModule.java +++ b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModule.java @@ -17,7 +17,6 @@ package com.github.nmorel.gwtjackson.remotelogging.server; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -32,14 +31,9 @@ public RemoteLoggingJacksonModule() { @Override public void setupModule( SetupContext context ) { super.setupModule( context ); - context.setMixInAnnotations( Throwable.class, ThrowableMixIn.class ); context.setMixInAnnotations( StackTraceElement.class, StackTraceElementMixIn.class ); } - @JsonTypeInfo( use = JsonTypeInfo.Id.CLASS ) - @JsonIgnoreProperties( "suppressed" ) - public abstract static class ThrowableMixIn {} - @JsonIgnoreProperties( "nativeMethod" ) public abstract static class StackTraceElementMixIn {} } \ No newline at end of file diff --git a/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/shared/RemoteThrowable.java b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/shared/RemoteThrowable.java new file mode 100644 index 00000000..72e6ccb1 --- /dev/null +++ b/extensions/remotelogging/src/main/java/com/github/nmorel/gwtjackson/remotelogging/shared/RemoteThrowable.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Nicolas Morel + * + * Licensed 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 com.github.nmorel.gwtjackson.remotelogging.shared; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties( {"localizedMessage", "suppressed"} ) +public final class RemoteThrowable extends Throwable { + + private final String remoteClassName; + + public RemoteThrowable( Throwable throwable ) { + super( throwable.getMessage(), throwable.getCause() != null ? new RemoteThrowable( throwable.getCause() ) : null ); + setStackTrace( throwable.getStackTrace() ); + remoteClassName = throwable.getClass().getName(); + } + + @JsonCreator + public RemoteThrowable( @JsonProperty( "message" ) String message, @JsonProperty( "cause" ) RemoteThrowable cause, @JsonProperty( + "remoteClassName" ) String remoteClassName ) { + super( message, cause ); + this.remoteClassName = remoteClassName; + } + + public String getRemoteClassName() { + return remoteClassName; + } + + @Override + public synchronized RemoteThrowable getCause() { + return (RemoteThrowable) super.getCause(); + } + + @Override + public String toString() { + String message = getMessage(); + return (message != null) ? (remoteClassName + ": " + message) : remoteClassName; + } +} diff --git a/extensions/remotelogging/src/main/resources/com/github/nmorel/gwtjackson/remotelogging/GwtJacksonRemoteLogging.gwt.xml b/extensions/remotelogging/src/main/resources/com/github/nmorel/gwtjackson/remotelogging/GwtJacksonRemoteLogging.gwt.xml index 9455d254..ac19974c 100644 --- a/extensions/remotelogging/src/main/resources/com/github/nmorel/gwtjackson/remotelogging/GwtJacksonRemoteLogging.gwt.xml +++ b/extensions/remotelogging/src/main/resources/com/github/nmorel/gwtjackson/remotelogging/GwtJacksonRemoteLogging.gwt.xml @@ -7,4 +7,5 @@ value="com.github.nmorel.gwtjackson.remotelogging.rebind.RemoteLoggingConfiguration" /> + diff --git a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableTestCase.java b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableTestCase.java index cb1b9f0f..ce2db0a1 100644 --- a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableTestCase.java +++ b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/client/ThrowableTestCase.java @@ -5,6 +5,7 @@ import com.github.nmorel.gwtjackson.client.GwtJacksonTestCase; import com.github.nmorel.gwtjackson.client.JsonSerializationContext; import com.github.nmorel.gwtjackson.client.ObjectWriter; +import com.github.nmorel.gwtjackson.remotelogging.shared.RemoteThrowable; import com.github.nmorel.gwtjackson.remotelogging.shared.ThrowableTester; import com.google.gwt.core.client.GWT; import org.junit.Test; @@ -20,12 +21,16 @@ public String getModuleName() { @Test public void testThrowable() throws IOException { - tester.testSerialize( createWriter( ThrowableWriter.INSTANCE ) ); + tester.testSerializeIllegalArgumentException( createWriter( ThrowableWriter.INSTANCE ) ); - tester.testSerializeNonNull( createWriter( ThrowableWriter.INSTANCE, createNonNullContext() ) ); + tester.testSerializeCustomException( createWriter( ThrowableWriter.INSTANCE ) ); + + tester.testSerializeIllegalArgumentExceptionNonNull( createWriter( ThrowableWriter.INSTANCE, createNonNullContext() ) ); + + tester.testSerializeCustomExceptionNonNull( createWriter( ThrowableWriter.INSTANCE, createNonNullContext() ) ); } - public interface ThrowableWriter extends ObjectWriter { + public interface ThrowableWriter extends ObjectWriter { ThrowableWriter INSTANCE = GWT.create( ThrowableWriter.class ); } diff --git a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModuleTest.java b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModuleTest.java index ff7b80ad..c56e4a3d 100644 --- a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModuleTest.java +++ b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/server/RemoteLoggingJacksonModuleTest.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.github.nmorel.gwtjackson.jackson.AbstractJacksonTest; +import com.github.nmorel.gwtjackson.remotelogging.shared.RemoteThrowable; import com.github.nmorel.gwtjackson.remotelogging.shared.ThrowableTester; import org.junit.Before; import org.junit.Test; @@ -18,11 +19,15 @@ public void setUp() { @Test public void testThrowable() throws IOException { - ThrowableTester.INSTANCE.testSerialize( createWriter( Throwable.class ) ); - ThrowableTester.INSTANCE.testDeserialize( createReader( Throwable.class ) ); + ThrowableTester.INSTANCE.testSerializeIllegalArgumentException( createWriter( RemoteThrowable.class ) ); + ThrowableTester.INSTANCE.testDeserializeIllegalArgumentException( createReader( RemoteThrowable.class ) ); + + ThrowableTester.INSTANCE.testSerializeCustomException( createWriter( RemoteThrowable.class ) ); + ThrowableTester.INSTANCE.testDeserializeCustomException( createReader( RemoteThrowable.class ) ); objectMapper.setSerializationInclusion( Include.NON_NULL ); - ThrowableTester.INSTANCE.testSerializeNonNull( createWriter( Throwable.class ) ); - ThrowableTester.INSTANCE.testDeserializeNonNull( createReader( Throwable.class ) ); + ThrowableTester.INSTANCE.testSerializeIllegalArgumentExceptionNonNull( createWriter( RemoteThrowable.class ) ); + + ThrowableTester.INSTANCE.testSerializeCustomExceptionNonNull( createWriter( RemoteThrowable.class ) ); } } diff --git a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/CustomException.java b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/CustomException.java new file mode 100644 index 00000000..bb437f3c --- /dev/null +++ b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/CustomException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 Nicolas Morel + * + * Licensed 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 com.github.nmorel.gwtjackson.remotelogging.shared; + +public class CustomException extends Exception { + + private final Object object; + + public CustomException( String message, Throwable cause, Object object ) { + super( message, cause ); + this.object = object; + } + + public Object getObject() { + return object; + } +} diff --git a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/ThrowableTester.java b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/ThrowableTester.java index 9ddc6534..26f8ce5e 100644 --- a/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/ThrowableTester.java +++ b/extensions/remotelogging/src/test/java/com/github/nmorel/gwtjackson/remotelogging/shared/ThrowableTester.java @@ -3,22 +3,24 @@ import com.github.nmorel.gwtjackson.shared.AbstractTester; import com.github.nmorel.gwtjackson.shared.ObjectReaderTester; import com.github.nmorel.gwtjackson.shared.ObjectWriterTester; +import com.google.gwt.core.shared.GwtIncompatible; +import org.apache.commons.lang3.exception.ExceptionUtils; public final class ThrowableTester extends AbstractTester { public static final ThrowableTester INSTANCE = new ThrowableTester(); - public static final String THROWABLE_JSON = "" + + public static final String ILLEGAL_ARGUMENT_EXCEPTION_JSON = "" + "{" + - "\"@class\":\"java.lang.IllegalArgumentException\"," + + "\"message\":\"Var cannot be null.\"," + "\"cause\":" + "{" + - "\"@class\":\"java.lang.NullPointerException\"," + - "\"cause\":null," + - "\"stackTrace\":[]," + "\"message\":null," + - "\"localizedMessage\":null" + + "\"cause\":null" + + ",\"remoteClassName\":\"java.lang.NullPointerException\"," + + "\"stackTrace\":[]" + "}," + + "\"remoteClassName\":\"java.lang.IllegalArgumentException\"," + "\"stackTrace\":" + "[" + "{" + @@ -33,44 +35,112 @@ public final class ThrowableTester extends AbstractTester { "\"lineNumber\":1," + "\"className\":\"com.Class1\"" + "}" + - "]," + + "]" + + "}"; + + public static final String CUSTOM_EXCEPTION_JSON = "" + + "{" + + "\"message\":\"My custom exception.\"," + + "\"cause\":" + + "{" + "\"message\":\"Var cannot be null.\"," + - "\"localizedMessage\":\"Var cannot be null.\"" + + "\"cause\":{\"message\":null," + + "\"cause\":null," + + "\"remoteClassName\":\"java.lang.NullPointerException\"," + + "\"stackTrace\":[]" + + "}," + + "\"remoteClassName\":\"java.lang.IllegalArgumentException\"," + + "\"stackTrace\":" + + "[" + + "{" + + "\"methodName\":\"method0\"," + + "\"fileName\":\"Class0.java\"," + + "\"lineNumber\":0," + + "\"className\":\"com.Class0\"" + + "}," + + "{" + + "\"methodName\":\"method1\"," + + "\"fileName\":\"Class1.java\"," + + "\"lineNumber\":1," + + "\"className\":\"com.Class1\"" + + "}" + + "]" + + "}," + + "\"remoteClassName\":\"com.github.nmorel.gwtjackson.remotelogging.shared.CustomException\"," + + "\"stackTrace\":" + + "[" + + "{" + + "\"methodName\":\"method2\"," + + "\"fileName\":\"Class2.java\"," + + "\"lineNumber\":2," + + "\"className\":\"com.Class2\"" + + "}," + + "{" + + "\"methodName\":\"method3\"," + + "\"fileName\":\"Class3.java\"," + + "\"lineNumber\":3," + + "\"className\":\"com.Class3\"" + + "}" + + "]" + "}"; - public static final String THROWABLE_NON_NULL_JSON = THROWABLE_JSON; + public static final IllegalArgumentException ILLEGAL_ARGUMENT_EXCEPTION; - public static final IllegalArgumentException THROWABLE; + public static final CustomException CUSTOM_EXCEPTION; static { NullPointerException cause = new NullPointerException(); cause.setStackTrace( new StackTraceElement[]{} ); - THROWABLE = new IllegalArgumentException( "Var cannot be null.", cause ); + ILLEGAL_ARGUMENT_EXCEPTION = new IllegalArgumentException( "Var cannot be null.", cause ); StackTraceElement elt0 = new StackTraceElement( "com.Class0", "method0", "Class0.java", 0 ); StackTraceElement elt1 = new StackTraceElement( "com.Class1", "method1", "Class1.java", 1 ); - THROWABLE.setStackTrace( new StackTraceElement[]{elt0, elt1} ); + ILLEGAL_ARGUMENT_EXCEPTION.setStackTrace( new StackTraceElement[]{elt0, elt1} ); + + CUSTOM_EXCEPTION = new CustomException( "My custom exception.", ILLEGAL_ARGUMENT_EXCEPTION, "object" ); + StackTraceElement elt2 = new StackTraceElement( "com.Class2", "method2", "Class2.java", 2 ); + StackTraceElement elt3 = new StackTraceElement( "com.Class3", "method3", "Class3.java", 3 ); + CUSTOM_EXCEPTION.setStackTrace( new StackTraceElement[]{elt2, elt3} ); } private ThrowableTester() { } - public void testSerialize( ObjectWriterTester writer ) { - assertEquals( THROWABLE_JSON, writer.write( THROWABLE ) ); + public void testSerializeIllegalArgumentException( ObjectWriterTester writer ) { + assertEquals( ILLEGAL_ARGUMENT_EXCEPTION_JSON, writer.write( new RemoteThrowable( ILLEGAL_ARGUMENT_EXCEPTION ) ) ); + } + + @GwtIncompatible + public void testDeserializeIllegalArgumentException( ObjectReaderTester reader ) { + RemoteThrowable throwable = reader.read( ILLEGAL_ARGUMENT_EXCEPTION_JSON ); + assertEquals( "" + + "java.lang.IllegalArgumentException: Var cannot be null.\n" + + "\tat com.Class0.method0(Class0.java:0)\n" + + "\tat com.Class1.method1(Class1.java:1)\n" + + "Caused by: java.lang.NullPointerException\n", ExceptionUtils.getStackTrace( throwable ) ); + } + + public void testSerializeCustomException( ObjectWriterTester writer ) { + assertEquals( CUSTOM_EXCEPTION_JSON, writer.write( new RemoteThrowable( CUSTOM_EXCEPTION ) ) ); } - public void testDeserialize( ObjectReaderTester reader ) { - Throwable throwable = reader.read( THROWABLE_JSON ); - assertTrue( throwable instanceof IllegalArgumentException ); - assertEquals( "Var cannot be null.", throwable.getMessage() ); + @GwtIncompatible + public void testDeserializeCustomException( ObjectReaderTester reader ) { + RemoteThrowable throwable = reader.read( CUSTOM_EXCEPTION_JSON ); + assertEquals( "" + + "com.github.nmorel.gwtjackson.remotelogging.shared.CustomException: My custom exception.\n" + + "\tat com.Class2.method2(Class2.java:2)\n" + + "\tat com.Class3.method3(Class3.java:3)\n" + + "Caused by: java.lang.IllegalArgumentException: Var cannot be null.\n" + + "\tat com.Class0.method0(Class0.java:0)\n" + + "\tat com.Class1.method1(Class1.java:1)\n" + + "Caused by: java.lang.NullPointerException\n", ExceptionUtils.getStackTrace( throwable ) ); } - public void testSerializeNonNull( ObjectWriterTester writer ) { - assertEquals( THROWABLE_NON_NULL_JSON, writer.write( THROWABLE ) ); + public void testSerializeIllegalArgumentExceptionNonNull( ObjectWriterTester writer ) { + assertEquals( ILLEGAL_ARGUMENT_EXCEPTION_JSON, writer.write( new RemoteThrowable( ILLEGAL_ARGUMENT_EXCEPTION ) ) ); } - public void testDeserializeNonNull( ObjectReaderTester reader ) { - Throwable throwable = reader.read( THROWABLE_NON_NULL_JSON ); - assertTrue( throwable instanceof IllegalArgumentException ); - assertEquals( "Var cannot be null.", throwable.getMessage() ); + public void testSerializeCustomExceptionNonNull( ObjectWriterTester writer ) { + assertEquals( CUSTOM_EXCEPTION_JSON, writer.write( new RemoteThrowable( CUSTOM_EXCEPTION ) ) ); } }