Skip to content

Commit

Permalink
New approach: use RemoteThrowable in order to avoid any kind of deser…
Browse files Browse the repository at this point in the history
…ialized Exception

2 issues could happens with previous implementation:

- InvalidDefinitionException: when you try to deserialize a Class that doesn't have a default constructor
- InvalidTypeIdException: when you try to deserialize a Class that is not accessible (not in the classpath) by server side

The new approach is to convert your original Throwable to a RemoteThrowable.
  • Loading branch information
freddyboucher authored and nmorel committed May 13, 2018
1 parent 0efbe64 commit b3d5e34
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,33 @@
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<Throwable> {
public class RemoteThrowableJsonSerializer extends JsonSerializer<RemoteThrowable> {

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";

private static final String STACK_TRACE = "stackTrace";

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 ) {
Expand All @@ -54,19 +55,16 @@ 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() ) {
StackTraceElementJsonSerializer.getInstance().doSerialize( writer, stackTraceElement, ctx, params );
}
writer.endArray();

writer.name( MESSAGE );
writer.value( throwable.getMessage() );

writer.name( LOCALIZED_MESSAGE );
writer.value( throwable.getLocalizedMessage() );

writer.endObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 {}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
value="com.github.nmorel.gwtjackson.remotelogging.rebind.RemoteLoggingConfiguration" />

<source path="client" />
<source path="shared" />
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Throwable> {
public interface ThrowableWriter extends ObjectWriter<RemoteThrowable> {

ThrowableWriter INSTANCE = GWT.create( ThrowableWriter.class );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 ) );
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit b3d5e34

Please sign in to comment.