From 812100aa042384ff9cba75af81a6f90bc2930895 Mon Sep 17 00:00:00 2001 From: Patrick Mentz Date: Fri, 4 Aug 2017 21:51:24 +0200 Subject: [PATCH] Compatibility for independent bus clients (#76) independent bus clients may not have all events implemented or the location of the event classes could be in a different package. This PR fixes issues in such an environment (e.g. Microservices). Therefore the event type in the AckRemoteApplicationEvent will be set via a special setter, so the availability of the type can be checked. In addition the UnknownRemoteApplicationEvent is fixed to not cause a NullPointerException when raised. Fixes #74 --- .../bus/event/AckRemoteApplicationEvent.java | 19 +++++++++++- .../event/UnknownRemoteApplicationEvent.java | 3 +- .../cloud/bus/BusAutoConfigurationTests.java | 14 +++++++++ .../cloud/bus/jackson/SubtypeModuleTests.java | 31 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/AckRemoteApplicationEvent.java b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/AckRemoteApplicationEvent.java index af6da0c..55834c3 100644 --- a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/AckRemoteApplicationEvent.java +++ b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/AckRemoteApplicationEvent.java @@ -17,6 +17,8 @@ package org.springframework.cloud.bus.event; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * An event that signals an ack of a specific {@link RemoteApplicationEvent}. These events * can be monitored by any applications that want to audit the responses to bus events. @@ -31,7 +33,7 @@ public class AckRemoteApplicationEvent extends RemoteApplicationEvent { private final String ackId; private final String ackDestinationService; - private final Class event; + private Class event; @SuppressWarnings("unused") private AckRemoteApplicationEvent() { @@ -62,6 +64,21 @@ public class AckRemoteApplicationEvent extends RemoteApplicationEvent { return event; } + /** + * Used by Jackson to set the remote class name of the event implementation. If the implementing class is unknown to + * this app, set the event to {@link UnknownRemoteApplicationEvent}. + * + * @param eventName the fq class name of the event implementation, not null + */ + @JsonProperty("event") + public void setEventName(String eventName) { + try { + event = (Class) Class.forName(eventName); + } catch (ClassNotFoundException e) { + event = UnknownRemoteApplicationEvent.class; + } + } + @Override public int hashCode() { final int prime = 31; diff --git a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/UnknownRemoteApplicationEvent.java b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/UnknownRemoteApplicationEvent.java index 42e804a..d289344 100644 --- a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/UnknownRemoteApplicationEvent.java +++ b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/UnknownRemoteApplicationEvent.java @@ -34,7 +34,8 @@ public class UnknownRemoteApplicationEvent extends RemoteApplicationEvent { } public UnknownRemoteApplicationEvent(Object source, String typeInfo, byte[] payload) { - super(source, null, null); + // Initialize originService with an empty String, to avoid NullPointer in AntPathMatcher. + super(source, "", null); this.typeInfo = typeInfo; this.payload = payload; } diff --git a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/BusAutoConfigurationTests.java b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/BusAutoConfigurationTests.java index a802577..f75b98b 100644 --- a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/BusAutoConfigurationTests.java +++ b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/BusAutoConfigurationTests.java @@ -35,6 +35,7 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati import org.springframework.cloud.bus.event.AckRemoteApplicationEvent; import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; import org.springframework.cloud.bus.event.SentApplicationEvent; +import org.springframework.cloud.bus.event.UnknownRemoteApplicationEvent; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; @@ -206,6 +207,19 @@ public class BusAutoConfigurationTests { this.context.getBean(InboundMessageHandlerConfiguration.class).refresh); } + /** + * see https://github.com/spring-cloud/spring-cloud-bus/issues/74 + */ + @Test + public void inboundNotFromSelfUnknown() { + this.context = SpringApplication.run(InboundMessageHandlerConfiguration.class); + this.context.setId("bar"); + this.context.getBean(SpringCloudBusClient.INPUT, MessageChannel.class) + .send(new GenericMessage<>( + new UnknownRemoteApplicationEvent(this, "UnknownEvent", "yada".getBytes()))); + // No Exception expected + } + @Configuration @Import({ MessageConsumer.class, BusAutoConfiguration.class, TestSupportBinderAutoConfiguration.class, 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 10b5072..4fbc7c1 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 @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.junit.Test; +import org.springframework.cloud.bus.event.AckRemoteApplicationEvent; import org.springframework.cloud.bus.event.RemoteApplicationEvent; import org.springframework.cloud.bus.event.UnknownRemoteApplicationEvent; import org.springframework.cloud.bus.event.test.TestRemoteApplicationEvent; @@ -92,6 +93,36 @@ public class SubtypeModuleTests { assertTrue("event is wrong type", event instanceof TypedRemoteApplicationEvent); } + /** + * see https://github.com/spring-cloud/spring-cloud-bus/issues/74 + */ + @Test + public void testDeserializeAckRemoteApplicationEventWithKnownType() throws Exception { + BusJacksonMessageConverter converter = new BusJacksonAutoConfiguration().busJsonConverter(); + converter.afterPropertiesSet(); + Object event = converter.fromMessage(MessageBuilder.withPayload( + "{\"type\":\"AckRemoteApplicationEvent\", \"event\":\"org.springframework.cloud.bus.event.test.TestRemoteApplicationEvent\"}") + .build(), RemoteApplicationEvent.class); + assertTrue("event is no ack", event instanceof AckRemoteApplicationEvent); + AckRemoteApplicationEvent ackEvent = AckRemoteApplicationEvent.class.cast(event); + assertEquals("inner ack event has wrong type", TestRemoteApplicationEvent.class, ackEvent.getEvent()); + } + + /** + * see https://github.com/spring-cloud/spring-cloud-bus/issues/74 + */ + @Test + public void testDeserializeAckRemoteApplicationEventWithUnknownType() throws Exception { + BusJacksonMessageConverter converter = new BusJacksonAutoConfiguration().busJsonConverter(); + converter.afterPropertiesSet(); + Object event = converter.fromMessage(MessageBuilder.withPayload( + "{\"type\":\"AckRemoteApplicationEvent\", \"event\":\"foo.bar.TestRemoteApplicationEvent\"}").build(), + RemoteApplicationEvent.class); + assertTrue("event is no ack", event instanceof AckRemoteApplicationEvent); + AckRemoteApplicationEvent ackEvent = AckRemoteApplicationEvent.class.cast(event); + assertEquals("inner ack event has wrong type", UnknownRemoteApplicationEvent.class, ackEvent.getEvent()); + } + @SuppressWarnings("serial") @JsonTypeName("my") public static class MyRemoteApplicationEvent extends RemoteApplicationEvent {