diff --git a/docs/src/main/asciidoc/spring-cloud-bus.adoc b/docs/src/main/asciidoc/spring-cloud-bus.adoc index 72d8dda..eb30ea7 100644 --- a/docs/src/main/asciidoc/spring-cloud-bus.adoc +++ b/docs/src/main/asciidoc/spring-cloud-bus.adoc @@ -103,5 +103,9 @@ queries on the data. Or forward it to a specialized tracing service. The Bus can carry any event of type `RemoteApplicationEvent`, but the default transport is JSON and the deserializer needs to know which -types are going to be used ahead of time. To register a new type you -can use `@JsonTypeName` on your custom class. \ No newline at end of file +types are going to be used ahead of time. To register a new type it +needs to be in a subpackage of `org.springframework.cloud.bus.event`. +You can use `@JsonTypeName` on your custom class or rely on the +default strategy which is to use the simple name of the class. Note +that both the producer and the consumer will need access to the class +definition. \ No newline at end of file diff --git a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/BusJacksonAutoConfiguration.java b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/BusJacksonAutoConfiguration.java index 3fc990b..7dbfb5c 100644 --- a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/BusJacksonAutoConfiguration.java +++ b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/BusJacksonAutoConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.messaging.Message; +import org.springframework.messaging.converter.MessageConverter; import org.springframework.util.ClassUtils; import org.springframework.util.MimeTypeUtils; @@ -39,7 +40,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; public class BusJacksonAutoConfiguration { @Bean - public BusJacksonMessageConverter busJsonConverter() { + public MessageConverter busJsonConverter() { return new BusJacksonMessageConverter(); } @@ -68,8 +69,8 @@ class BusJacksonMessageConverter extends AbstractFromMessageConverter { public BusJacksonMessageConverter() { super(MimeTypeUtils.APPLICATION_JSON, MessageConverterUtils.X_JAVA_OBJECT); - mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - mapper.registerModule(new SubtypeModule(findSubTypes())); + this.mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + this.mapper.registerModule(new SubtypeModule(findSubTypes())); } private Class[] findSubTypes() { @@ -125,14 +126,14 @@ class BusJacksonMessageConverter extends AbstractFromMessageConverter { Object payload = message.getPayload(); if (payload instanceof byte[]) { - result = mapper.readValue((byte[]) payload, targetClass); + result = this.mapper.readValue((byte[]) payload, targetClass); } else if (payload instanceof String) { - result = mapper.readValue((String) payload, targetClass); + result = this.mapper.readValue((String) payload, targetClass); } } catch (Exception e) { - logger.error(e.getMessage(), e); + this.logger.error(e.getMessage(), e); return null; } return result; diff --git a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TestRemoteApplicationEvent.java b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TestRemoteApplicationEvent.java new file mode 100644 index 0000000..c9f0bc2 --- /dev/null +++ b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TestRemoteApplicationEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015 the original author or authors. + * + * 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 org.springframework.cloud.bus.event.test; + +import org.springframework.cloud.bus.event.RemoteApplicationEvent; + +@SuppressWarnings("serial") +public class TestRemoteApplicationEvent extends RemoteApplicationEvent { + @SuppressWarnings("unused") + private TestRemoteApplicationEvent() { + } + + protected TestRemoteApplicationEvent(Object source, String originService, + String destinationService) { + super(source, originService, destinationService); + } + + protected TestRemoteApplicationEvent(Object source, String originService) { + super(source, originService); + } +} \ No newline at end of file diff --git a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TypedRemoteApplicationEvent.java b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TypedRemoteApplicationEvent.java new file mode 100644 index 0000000..725d0e6 --- /dev/null +++ b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TypedRemoteApplicationEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 the original author or authors. + * + * 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 org.springframework.cloud.bus.event.test; + +import org.springframework.cloud.bus.event.RemoteApplicationEvent; + +import com.fasterxml.jackson.annotation.JsonTypeName; + +@SuppressWarnings("serial") +@JsonTypeName("typed") +public class TypedRemoteApplicationEvent extends RemoteApplicationEvent { + @SuppressWarnings("unused") + private TypedRemoteApplicationEvent() { + } + + protected TypedRemoteApplicationEvent(Object source, String originService, + String destinationService) { + super(source, originService, destinationService); + } + + protected TypedRemoteApplicationEvent(Object source, String originService) { + super(source, originService); + } +} \ No newline at end of file diff --git a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/jackson/SubtypeModuleTests.java b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/jackson/SubtypeModuleTests.java index 7bcbaf7..229d790 100644 --- a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/jackson/SubtypeModuleTests.java +++ b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/jackson/SubtypeModuleTests.java @@ -5,6 +5,10 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.springframework.cloud.bus.event.RemoteApplicationEvent; +import org.springframework.cloud.bus.event.test.TestRemoteApplicationEvent; +import org.springframework.cloud.bus.event.test.TypedRemoteApplicationEvent; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.support.MessageBuilder; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.ObjectMapper; @@ -15,7 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class SubtypeModuleTests { @Test - public void testSubclass() throws Exception { + public void testDeserializeSubclass() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new SubtypeModule(MyRemoteApplicationEvent.class)); @@ -27,12 +31,35 @@ public class SubtypeModuleTests { assertEquals("originService was wrong", "myorigin", myEvent.getOriginService()); assertEquals("destinationService was wrong", "myservice", myEvent.getDestinationService()); + } - event = mapper.readValue("{\"type\":\"another\"}", + @Test + public void testDeserializeWhenTypeIsKnown() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + RemoteApplicationEvent event = mapper.readValue("{\"type\":\"another\"}", AnotherRemoteApplicationEvent.class); assertTrue("event is wrong type", event instanceof AnotherRemoteApplicationEvent); } + @Test + public void testDeserializeWithMessageConverter() throws Exception { + MessageConverter converter = new BusJacksonAutoConfiguration().busJsonConverter(); + Object event = converter.fromMessage( + MessageBuilder.withPayload("{\"type\":\"TestRemoteApplicationEvent\"}").build(), + RemoteApplicationEvent.class); + assertTrue("event is wrong type", event instanceof TestRemoteApplicationEvent); + } + + @Test + public void testDeserializeJsonTypeWithMessageConverter() throws Exception { + MessageConverter converter = new BusJacksonAutoConfiguration().busJsonConverter(); + Object event = converter.fromMessage( + MessageBuilder.withPayload("{\"type\":\"typed\"}").build(), + RemoteApplicationEvent.class); + assertTrue("event is wrong type", event instanceof TypedRemoteApplicationEvent); + } + @SuppressWarnings("serial") @JsonTypeName("my") public static class MyRemoteApplicationEvent extends RemoteApplicationEvent {