RestController
(server-sent-eventの場合)を返し、各イベントにHATEOASリンクを追加しました。ここでは、このコントローラの単純化されたが、実施例は次のとおりです。Springを使用して着信SSEイベントを購読する方法
package hello;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Greeting extends ResourceSupport {
private final String content;
private final static AtomicInteger idProvider = new AtomicInteger();
private int greetingId;
private Status status;
enum Status {
ENQUEUED,
PROCESSING,
COMPLETE;
}
@JsonCreator
public Greeting(@JsonProperty("content") final String content) {
this.greetingId = idProvider.addAndGet(1);
this.status = Status.ENQUEUED;
this.content = content;
}
public Status getStatus() {
return this.status;
}
protected void setStatus(final Status status) {
this.status = status;
}
public int getGreetingId() {
return this.greetingId;
}
public String getContent() {
return this.content;
}
@Override
public String toString() {
return "Greeting{id='" + this.greetingId + "', status='" + this.status + "' content='" + this.content + "', " + super.toString() + "}";
}
public void incrementStatus() {
switch (this.status) {
case ENQUEUED:
this.status = Status.PROCESSING;
break;
case PROCESSING:
this.status = Status.COMPLETE;
break;
default:
break;
}
}
}
このコードは完璧に動作します:
package hello;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import hello.Greeting.Status;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@RestController
public class GreetingController {
private static final Logger log = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
class GreetingRequestHandler implements Runnable {
private ResponseBodyEmitter emitter;
private Greeting greeting;
public GreetingRequestHandler(final ResponseBodyEmitter emitter, final Greeting greeting) {
this.emitter = emitter;
this.greeting = greeting;
}
@Override
public void run() {
try {
log.info(this.greeting.toString());
this.emitter.send(this.greeting);
Thread.sleep(5000);
if (Status.COMPLETE.equals(this.greeting.getStatus())) {
this.emitter.complete();
} else {
this.greeting.incrementStatus();
new Thread(new GreetingRequestHandler(this.emitter, this.greeting)).start();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
@RequestMapping(path = "/greeting")
public SseEmitter greeting(@RequestParam(value = "name", defaultValue = "World") final String name) {
SseEmitter emitter = new SseEmitter();
Greeting greeting = new Greeting(String.format(template, name));
greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());
new Thread(new GreetingRequestHandler(emitter, greeting)).start();
log.info("returning emitter");
return emitter;
}
}
Greeting
クラスは次のようです。 Webブラウザを使用してRESTサービスにアクセスしようとすると、正しいコンテンツとリンクでイベントが表示されます。
結果は(各イベントが前の5秒後に表示される)のようになります。
data:{"content":"Hello, Kraal!","greetingId":8,"status":"ENQUEUED","_links":{"self":{"href":"http://localhost:8080/greeting?name=Kraal"}}}
data:{"content":"Hello, Kraal!","greetingId":8,"status":"PROCESSING","_links":{"self":{"href":"http://localhost:8080/greeting?name=Kraal"}}}
data:{"content":"Hello, Kraal!","greetingId":8,"status":"COMPLETE","_links":{"self":{"href":"http://localhost:8080/greeting?name=Kraal"}}}
今、私はこのRESTサービスを呼び出し、別のSpringアプリケーションからこれらのイベントを読み取る必要がある...しかし、私はありません持っていますSpringを使ってクライアントコードを書く方法を手がかりにしてください。 RestTemplate
は、同期クライアント側のHTTPアクセスのために設計されているので、これは動作しません...
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new Jackson2HalModule());
// required for HATEOAS
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
converter.setObjectMapper(mapper);
// required in order to be able to read serialized objects
MappingJackson2HttpMessageConverter converter2 = new MappingJackson2HttpMessageConverter();
converter2.setSupportedMediaTypes(MediaType.parseMediaTypes("application/octet-stream"));
converter2.setObjectMapper(mapper);
// required to understand SSE events
MappingJackson2HttpMessageConverter converter3 = new MappingJackson2HttpMessageConverter();
converter3.setSupportedMediaTypes(MediaType.parseMediaTypes("text/event-stream"));
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(converter);
converters.add(converter2);
converters.add(converter3);
// probably wrong template
RestTemplate restTemplate = new RestTemplate();
restTemplate = new RestTemplate(converters);
// this does not work as I receive events and no a single object
Greeting greeting = restTemplate.getForObject("http://localhost:8080/greeting/?name=Kraal", Greeting.class);
log.info(greeting.toString());
私が取得エラーメッセージは次のとおりです。
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'data': was expecting ('true', 'false' or 'null')
実際に各イベントはSSEイベントであり、データ」で始まります:」...
だから質問はです:
- JacksonとSSEをマッピングするには、どのObjectMapperモジュールを登録する必要がありますか?
- Springを使用して着信SSEイベント(オブザーバーパターン)を購読するにはどうすればよいですか?
ありがとうございます。
サイドノート:私はSpringを使ってやっているので、私はJersey SSEサポートを次のように使ってやろうとしました。予想通り、私はイベントを受け取るジャージーを使用して、その後私はGreeting
クラスにそれらをキャストすることはできません(私は右コンバータモジュールを持っていないことであると思います上記と同じ理由。):
Client client = ClientBuilder.newBuilder().register(converter).register(SseFeature.class).build();
WebTarget target = client.target("http://localhost:8080/greeting/?name=Kraal");
EventInput eventInput = target.request().get(EventInput.class);
while (!eventInput.isClosed()) {
final InboundEvent inboundEvent = eventInput.read();
if (inboundEvent == null) {
// connection has been closed
break;
}
// this works fine and prints out events as they are incoming
System.out.println(inboundEvent.readData(String.class));
// but this doesn't as no proper way to deserialize the
// class with HATEOAS links can be found
// Greeting greeting = inboundEvent.readData(Greeting.class);
// System.out.println(greeting.toString());
}
サーバー送信されたイベントは、ブラウザへの通信サーバー用に設計された使用することができます。あなたの状況は、より多くのサーバー間通信を必要とします。 私のアプリケーションでは、Redisが提供するpubサブメッセージキューを使用しましたが、他のものを使用することはできます。アイデアは、任意の数のSpringアプリケーションがキューに公開でき、それをサブスクライブしているすべてのアプリケーションがメッセージを受信することです。 https://spring.io/guides/gs/messaging-redis/ –