Repository: cxf Updated Branches: refs/heads/master 530498a45 -> 6d185c2c9
[CXF-7462] Minor improvements to OutboundSseEventImpl This closes #301. Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/6d185c2c Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/6d185c2c Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/6d185c2c Branch: refs/heads/master Commit: 6d185c2c95e8e49d861bb4cd46784f690e0d46a6 Parents: 530498a Author: Andy McCright <[email protected]> Authored: Tue Aug 1 11:32:40 2017 -0500 Committer: reta <[email protected]> Committed: Thu Aug 3 11:08:14 2017 -0400 ---------------------------------------------------------------------- .../cxf/jaxrs/sse/OutboundSseEventImpl.java | 32 ++- .../cxf/jaxrs/sse/OutboundSseEventImplTest.java | 236 +++++++++++++++++++ 2 files changed, 257 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/6d185c2c/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImpl.java ---------------------------------------------------------------------- diff --git a/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImpl.java b/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImpl.java index 52566bb..c35602a 100644 --- a/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImpl.java +++ b/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImpl.java @@ -24,15 +24,15 @@ import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MediaType; import javax.ws.rs.sse.OutboundSseEvent; -public class OutboundSseEventImpl implements OutboundSseEvent { - private String id; - private String name; - private String comment; - private long reconnectDelay = -1; - private Class<?> type; - private Type genericType; - private MediaType mediaType; - private Object data; +public final class OutboundSseEventImpl implements OutboundSseEvent { + private final String id; + private final String name; + private final String comment; + private final long reconnectDelay; + private final Class<?> type; + private final Type genericType; + private final MediaType mediaType; + private final Object data; public static class BuilderImpl implements Builder { private String id; @@ -41,7 +41,7 @@ public class OutboundSseEventImpl implements OutboundSseEvent { private long reconnectDelay = -1; private Class<?> type; private Type genericType; - private MediaType mediaType; + private MediaType mediaType = MediaType.TEXT_PLAIN_TYPE; private Object data; @Override @@ -77,6 +77,9 @@ public class OutboundSseEventImpl implements OutboundSseEvent { @Override @SuppressWarnings("rawtypes") public Builder data(Class newType, Object newData) { + if (newType == null || newData == null) { + throw new IllegalArgumentException("Parameters 'type' and 'data' must not be null."); + } this.type = newType; this.data = newData; return this; @@ -85,6 +88,9 @@ public class OutboundSseEventImpl implements OutboundSseEvent { @Override @SuppressWarnings("rawtypes") public Builder data(GenericType newType, Object newData) { + if (newType == null || newData == null) { + throw new IllegalArgumentException("Parameters 'type' and 'data' must not be null."); + } this.genericType = newType.getType(); this.data = newData; return this; @@ -92,6 +98,10 @@ public class OutboundSseEventImpl implements OutboundSseEvent { @Override public Builder data(Object newData) { + if (newData == null) { + throw new IllegalArgumentException("Parameter 'data' must not be null."); + } + this.type = newData.getClass(); this.data = newData; return this; } @@ -112,7 +122,7 @@ public class OutboundSseEventImpl implements OutboundSseEvent { } //CHECKSTYLE:OFF - OutboundSseEventImpl(String id, String name, String comment, long reconnectDelay, + private OutboundSseEventImpl(String id, String name, String comment, long reconnectDelay, Class<?> type, Type genericType, MediaType mediaType, Object data) { this.id = id; this.name = name; http://git-wip-us.apache.org/repos/asf/cxf/blob/6d185c2c/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImplTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImplTest.java b/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImplTest.java new file mode 100644 index 0000000..6e5eef3 --- /dev/null +++ b/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/OutboundSseEventImplTest.java @@ -0,0 +1,236 @@ +/** + * 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.cxf.jaxrs.sse; + +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.sse.OutboundSseEvent; +import javax.ws.rs.sse.Sse; + +import org.junit.Assert; +import org.junit.Test; + +public class OutboundSseEventImplTest extends Assert { + + /** + * Ensure that the <code>SseImpl</code> returns the correct builder class, + * <code>OutboundSseEventImpl.BuilderImpl.class</code>. + */ + @Test + public void testSseImplReturnsExpectedOutboundSseEventBuilder() { + Sse sse = new SseImpl(); + assertEquals(sse.newEventBuilder().getClass(), OutboundSseEventImpl.BuilderImpl.class); + } + + /** + * A user should not need to specify a media type when creating an outbound event. The default + * should be <code>MediaType.SERVER_SENT_EVENTS_TYPE</code>. + */ + @Test + public void testDefaultMediaType() { + Sse sse = new SseImpl(); + + // test newEvent(data) + OutboundSseEvent event = sse.newEvent("myData"); + assertNull(event.getName()); + assertEquals("myData", event.getData()); + assertEquals(MediaType.TEXT_PLAIN_TYPE, event.getMediaType()); + + // test newEvent(name, data) + event = sse.newEvent("myName", "myData2"); + assertEquals("myName", event.getName()); + assertEquals("myData2", event.getData()); + assertEquals(MediaType.TEXT_PLAIN_TYPE, event.getMediaType()); + + // test newEventBuilder()...build() + event = sse.newEventBuilder().comment("myComment").data("myData3").build(); + assertEquals("myComment", event.getComment()); + assertEquals("myData3", event.getData()); + assertEquals(MediaType.TEXT_PLAIN_TYPE, event.getMediaType()); + } + + /** + * A user should not need to specify the type of data being sent in an outbound + * event. In that case the OutboundSseEvent should use the data object's type. Other + * types may be specified, but the default (if not specified by the user) should be + * the return value from the object's <code>getClass()</code> method. + */ + @Test + public void testDefaultClass() { + Sse sse = new SseImpl(); + + // test newEvent(string) + OutboundSseEvent event = sse.newEvent("myData"); + assertNull(event.getName()); + assertEquals("myData", event.getData()); + assertEquals(String.class, event.getType()); + + // test newEvent(name, data) + event = sse.newEvent("myName", "myData2"); + assertEquals("myName", event.getName()); + assertEquals("myData2", event.getData()); + assertEquals(String.class, event.getType()); + + // test newEventBuilder()...build() + event = sse.newEventBuilder().comment("myComment").data("myData3").build(); + assertEquals("myComment", event.getComment()); + assertEquals("myData3", event.getData()); + assertEquals(String.class, event.getType()); + + // test that object's class is re-enabled when calling different signatures of the data method + OutboundSseEvent.Builder builder = sse.newEventBuilder(); + builder.data(TestData.class, new TestDataImpl("1", "2")); + event = builder.build(); + assertEquals(TestData.class, event.getType()); + builder.data("myString"); + event = builder.build(); + assertEquals(String.class, event.getType()); + + // same thing, but don't build in between calls to data + event = sse.newEventBuilder().data(TestDataImpl.class, new TestDataImpl("3")).data("anotherString").build(); + assertEquals(String.class, event.getType()); + assertEquals("anotherString", event.getData()); + } + + /** + * If the user passes null in as the data object or type object for <code>Builder.data(Object)</code>, + * <code>Builder.data(Class, Object)</code>, or <code>Builder.data(GenericType, Object)</code>, they + * should expect an IllegalArgumentException to be thrown. + */ + @Test + public void testNullsForDataOrTypes() { + Sse sse = new SseImpl(); + OutboundSseEvent.Builder builder = sse.newEventBuilder(); + + try { + builder.data(null); + fail("Passing a null data object should have resulted in an IllegalArgumentException"); + } catch (Throwable t) { + assertTrue(t instanceof IllegalArgumentException); + } + + try { + builder.data(Object.class, null); + fail("Passing a null data object should have resulted in an IllegalArgumentException"); + } catch (Throwable t) { + assertTrue(t instanceof IllegalArgumentException); + } + + try { + builder.data((Class<?>)null, "123"); + fail("Passing a null data object should have resulted in an IllegalArgumentException"); + } catch (Throwable t) { + assertTrue(t instanceof IllegalArgumentException); + } + + try { + builder.data(new GenericType<List<String>>() { }, null); + fail("Passing a null data object should have resulted in an IllegalArgumentException"); + } catch (Throwable t) { + assertTrue(t instanceof IllegalArgumentException); + } + + try { + builder.data((GenericType<?>)null, "456"); + fail("Passing a null data object should have resulted in an IllegalArgumentException"); + } catch (Throwable t) { + assertTrue(t instanceof IllegalArgumentException); + } + } + /** + * Test that event built by the builder contains all of the data passed in + * to it. + */ + @Test + public void testBuilderAPIs() { + SseImpl sse = new SseImpl(); + OutboundSseEvent.Builder builder = sse.newEventBuilder(); + builder.comment("myComment"); + builder.data(new TestDataImpl("dataNoSpecifiedType")); + builder.id("id"); + builder.mediaType(MediaType.APPLICATION_JSON_TYPE); + builder.name("name"); + builder.reconnectDelay(5000); + OutboundSseEvent event = builder.build(); + assertEquals("myComment", event.getComment()); + assertEquals(TestDataImpl.class, event.getType()); + assertTrue(event.getData() instanceof TestDataImpl); + assertEquals("dataNoSpecifiedType", ((TestDataImpl)event.getData()).getData().get(0)); + + assertEquals("id", event.getId()); + assertEquals(MediaType.APPLICATION_JSON_TYPE, event.getMediaType()); + assertEquals("name", event.getName()); + assertEquals(5000, event.getReconnectDelay()); + + // now reuse the builder to build a new event + builder.comment("myComment2"); + builder.data(TestData.class, new TestDataImpl("data1", "data2")); + builder.id("id2"); + builder.mediaType(MediaType.TEXT_PLAIN_TYPE); + builder.name("name2"); + builder.reconnectDelay(9000); + event = builder.build(); + assertEquals("myComment2", event.getComment()); + assertEquals(new TestDataImpl("data1", "data2"), event.getData()); + assertEquals(TestData.class, event.getType()); + assertEquals("id2", event.getId()); + assertEquals(MediaType.TEXT_PLAIN_TYPE, event.getMediaType()); + assertEquals("name2", event.getName()); + assertEquals(9000, event.getReconnectDelay()); + } + + interface TestData { + List<String> getData(); + } + + class TestDataImpl implements TestData { + final List<String> data = new ArrayList<>(); + TestDataImpl(String...entries) { + for (String entry : entries) { + data.add(entry); + } + } + + @Override + public List<String> getData() { + return data; + } + @Override + public boolean equals(Object o) { + if (o instanceof TestDataImpl && ((TestDataImpl)o).data.size() == data.size()) { + for (int i = 0; i < data.size(); i++) { + if (((TestDataImpl)o).data.get(i) != data.get(i)) { + return false; + } + } + return true; + } + return false; + } + + @Override + public int hashCode() { + return data.hashCode(); + } + } +}
