2015-12-21 9 views
5

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()); 
} 
+0

サーバー送信されたイベントは、ブラウザへの通信サーバー用に設計された使用することができます。あなたの状況は、より多くのサーバー間通信を必要とします。 私のアプリケーションでは、Redisが提供するpubサブメッセージキューを使用しましたが、他のものを使用することはできます。アイデアは、任意の数のSpringアプリケーションがキューに公開でき、それをサブスクライブしているすべてのアプリケーションがメッセージを受信することです。 https://spring.io/guides/gs/messaging-redis/ –

答えて

0

documentation

ごとに、あなたは私の見解でinboundEvent.readData(Class<T> type)

+0

ドキュメントのリンクが壊れているかどうかを確認してください – xetra11

+0

このリンクを使用してくださいhttps://jersey.github.io/apidocs/2.26/jersey/org/glassfish/ jersey/media/sse/InboundEvent.html –

関連する問題