Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 5537bd6

Browse files
committed
Allow GenericEntity to be used with SSE
Change-Id: Ia5e1d4b5d359481dc28ba1aea76225f406f130a8
1 parent 12ee22a commit 5537bd6

File tree

2 files changed

+289
-2
lines changed

2 files changed

+289
-2
lines changed

media/sse/src/main/java/org/glassfish/jersey/media/sse/OutboundEvent.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2012-2018 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -42,6 +42,7 @@
4242

4343
import java.lang.reflect.Type;
4444

45+
import javax.ws.rs.core.GenericEntity;
4546
import javax.ws.rs.core.GenericType;
4647
import javax.ws.rs.core.MediaType;
4748
import javax.ws.rs.sse.OutboundSseEvent;
@@ -216,7 +217,11 @@ public Builder data(GenericType type, Object data) {
216217
}
217218

218219
this.type = type;
219-
this.data = data;
220+
if (data instanceof GenericEntity) {
221+
this.data = ((GenericEntity) data).getEntity();
222+
} else {
223+
this.data = data;
224+
}
220225
return this;
221226
}
222227

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
12+
* or LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package org.glassfish.jersey.tests.e2e.sse;
42+
43+
import javafx.util.Pair;
44+
import org.glassfish.jersey.server.ResourceConfig;
45+
import org.glassfish.jersey.test.JerseyTest;
46+
import org.junit.Test;
47+
48+
import javax.inject.Singleton;
49+
import javax.ws.rs.GET;
50+
import javax.ws.rs.Path;
51+
import javax.ws.rs.Produces;
52+
import javax.ws.rs.WebApplicationException;
53+
import javax.ws.rs.client.WebTarget;
54+
import javax.ws.rs.core.Application;
55+
import javax.ws.rs.core.Context;
56+
import javax.ws.rs.core.GenericEntity;
57+
import javax.ws.rs.core.MediaType;
58+
import javax.ws.rs.core.MultivaluedMap;
59+
import javax.ws.rs.ext.MessageBodyReader;
60+
import javax.ws.rs.ext.MessageBodyWriter;
61+
import javax.ws.rs.sse.Sse;
62+
import javax.ws.rs.sse.SseEventSink;
63+
import javax.ws.rs.sse.SseEventSource;
64+
import java.io.IOException;
65+
import java.io.InputStream;
66+
import java.io.OutputStream;
67+
import java.lang.annotation.Annotation;
68+
import java.lang.reflect.Type;
69+
import java.nio.charset.Charset;
70+
import java.util.LinkedList;
71+
import java.util.List;
72+
import java.util.StringTokenizer;
73+
import java.util.concurrent.CountDownLatch;
74+
import java.util.concurrent.TimeUnit;
75+
76+
import static junit.framework.TestCase.assertEquals;
77+
import static junit.framework.TestCase.assertTrue;
78+
79+
public class GenericEntityTest extends JerseyTest {
80+
private static final int BUFFER_SIZE = 20;
81+
82+
@Override
83+
protected Application configure() {
84+
return new ResourceConfig(GenericEntityTest.SSEGenericEntityResource.class, ListPairMBRW.class, PairMBRW.class);
85+
}
86+
87+
@Test
88+
public void testGenericString() throws InterruptedException {
89+
WebTarget sseTarget = target("genericentityresource/string");
90+
CountDownLatch countDownLatch = new CountDownLatch(1);
91+
MessageLatch<String> messageLatch = new MessageLatch<>(countDownLatch);
92+
try (SseEventSource source = SseEventSource.target(sseTarget).build()) {
93+
source.register(event -> messageLatch.consume(event.readData()));
94+
source.open();
95+
assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
96+
assertEquals("Cindy", messageLatch.data().get(0));
97+
}
98+
}
99+
100+
@Test
101+
public void testGenericPair() throws InterruptedException {
102+
WebTarget sseTarget = target("genericentityresource/pair").register(PairMBRW.class);
103+
CountDownLatch countDownLatch = new CountDownLatch(1);
104+
MessageLatch<Pair<String, Integer>> messageLatch = new MessageLatch<>(countDownLatch);
105+
try (SseEventSource source = SseEventSource.target(sseTarget).build()) {
106+
source.register(event -> messageLatch.consume(event.readData(Pair.class)));
107+
source.open();
108+
assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
109+
Pair<String, Integer> pair = messageLatch.data().get(0);
110+
assertEquals("Cindy", pair.getKey());
111+
assertEquals(30, pair.getValue().intValue());
112+
}
113+
}
114+
115+
@Test
116+
public void testGenericList() throws InterruptedException {
117+
WebTarget sseTarget = target("genericentityresource/list").register(ListPairMBRW.class);
118+
CountDownLatch countDownLatch = new CountDownLatch(2);
119+
MessageLatch<Pair<String, Integer>> messageLatch = new MessageLatch<>(countDownLatch);
120+
try (SseEventSource source = SseEventSource.target(sseTarget).build()) {
121+
source.register(event -> messageLatch.consume((List<Pair<String, Integer>>) event.readData(List.class)));
122+
source.open();
123+
assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
124+
Pair<String, Integer> cindy = messageLatch.data().get(0);
125+
Pair<String, Integer> jack = messageLatch.data().get(1);
126+
assertEquals("Cindy", cindy.getKey());
127+
assertEquals(30, cindy.getValue().intValue());
128+
assertEquals("Jack", jack.getKey());
129+
assertEquals(32, jack.getValue().intValue());
130+
}
131+
}
132+
133+
@Singleton
134+
@Path("genericentityresource")
135+
public static class SSEGenericEntityResource {
136+
@GET
137+
@Path("string")
138+
@Produces(MediaType.SERVER_SENT_EVENTS)
139+
public void sendString(@Context SseEventSink sink, @Context Sse sse) {
140+
GenericEntity<String> ges = new GenericEntity<String>("Cindy") {
141+
};
142+
try (SseEventSink s = sink) {
143+
s.send(sse.newEventBuilder().data(ges).build());
144+
}
145+
}
146+
147+
@GET
148+
@Path("pair")
149+
@Produces(MediaType.SERVER_SENT_EVENTS)
150+
public void sendPair(@Context SseEventSink sink, @Context Sse sse) {
151+
Pair<String, Integer> person = new Pair<>("Cindy", 30);
152+
GenericEntity<Pair<String, Integer>> entity = new GenericEntity<Pair<String, Integer>>(person) {
153+
};
154+
try (SseEventSink s = sink) {
155+
s.send(sse.newEventBuilder().data(entity).build());
156+
}
157+
}
158+
159+
@GET
160+
@Path("list")
161+
@Produces(MediaType.SERVER_SENT_EVENTS)
162+
public void sendList(@Context SseEventSink sink, @Context Sse sse) {
163+
Pair<String, Integer> person1 = new Pair<>("Cindy", 30);
164+
Pair<String, Integer> person2 = new Pair<>("Jack", 32);
165+
List<Pair<String, Integer>> people = new LinkedList<>();
166+
people.add(person1);
167+
people.add(person2);
168+
GenericEntity<List<Pair<String, Integer>>> entity = new GenericEntity<List<Pair<String, Integer>>>(people) {
169+
};
170+
try (SseEventSink s = sink) {
171+
s.send(sse.newEventBuilder().data(entity).build());
172+
}
173+
}
174+
}
175+
176+
private static class PairMBRW implements
177+
MessageBodyWriter<Pair<String, Integer>>, MessageBodyReader<Pair<String, Integer>> {
178+
179+
@Override
180+
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
181+
return type == Pair.class;
182+
}
183+
184+
@Override
185+
public Pair<String, Integer> readFrom(Class<Pair<String, Integer>> type, Type genericType,
186+
Annotation[] annotations, MediaType mediaType,
187+
MultivaluedMap<String, String> httpHeaders,
188+
InputStream entityStream) throws IOException, WebApplicationException {
189+
byte[] buffer = new byte[GenericEntityTest.BUFFER_SIZE];
190+
entityStream.read(buffer);
191+
return readFrom(new String(buffer, Charset.defaultCharset()).trim());
192+
}
193+
194+
static Pair<String, Integer> readFrom(String from) {
195+
String[] split = from.split(",", 2);
196+
return new Pair<String, Integer>(split[0], Integer.parseInt(split[1]));
197+
}
198+
199+
@Override
200+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
201+
return type == Pair.class;
202+
}
203+
204+
@Override
205+
public void writeTo(Pair<String, Integer> stringIntegerPair, Class<?> type, Type genericType,
206+
Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
207+
OutputStream entityStream) throws IOException, WebApplicationException {
208+
writeTo(stringIntegerPair, entityStream);
209+
}
210+
211+
static void writeTo(Pair<String, Integer> stringIntegerPair, OutputStream entityStream) throws IOException {
212+
StringBuilder sb = new StringBuilder();
213+
sb.append(stringIntegerPair.getKey()).append(",").append(stringIntegerPair.getValue());
214+
entityStream.write(sb.toString().getBytes(Charset.defaultCharset()));
215+
}
216+
}
217+
218+
private static class ListPairMBRW implements MessageBodyWriter<List<Pair<String, Integer>>>,
219+
MessageBodyReader<List<Pair<String, Integer>>> {
220+
221+
@Override
222+
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
223+
return type == List.class;
224+
}
225+
226+
@Override
227+
public List<Pair<String, Integer>> readFrom(Class<List<Pair<String, Integer>>> type, Type genericType,
228+
Annotation[] annotations, MediaType mediaType,
229+
MultivaluedMap<String, String> httpHeaders,
230+
InputStream entityStream)
231+
throws IOException, WebApplicationException {
232+
List<Pair<String, Integer>> list = new LinkedList<>();
233+
byte[] buffer = new byte[20];
234+
entityStream.read(buffer);
235+
StringTokenizer st = new StringTokenizer(new String(buffer, Charset.defaultCharset()).trim(), ";", false);
236+
while (st.hasMoreTokens()) {
237+
list.add(PairMBRW.readFrom(st.nextToken()));
238+
}
239+
return list;
240+
}
241+
242+
@Override
243+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
244+
return type == List.class;
245+
}
246+
247+
@Override
248+
public void writeTo(List<Pair<String, Integer>> pairs, Class<?> type, Type genericType,
249+
Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
250+
OutputStream entityStream) throws IOException, WebApplicationException {
251+
for (Pair<String, Integer> pair : pairs) {
252+
PairMBRW.writeTo(pair, entityStream);
253+
entityStream.write(";".getBytes());
254+
}
255+
}
256+
}
257+
258+
private static class MessageLatch<T> {
259+
private CountDownLatch countDownLatch;
260+
private List<T> data = new LinkedList<>();
261+
262+
private MessageLatch(CountDownLatch countDownLatch) {
263+
this.countDownLatch = countDownLatch;
264+
}
265+
266+
private void consume(List<T> list) {
267+
for (T o : list) {
268+
data.add(o);
269+
countDownLatch.countDown();
270+
}
271+
}
272+
273+
private void consume(T o) {
274+
data.add(o);
275+
countDownLatch.countDown();
276+
}
277+
278+
private List<T> data() {
279+
return data;
280+
}
281+
}
282+
}

0 commit comments

Comments
 (0)