0

we want to integrate third party library(Eclipse XText LSP) into our SpringBoot webapp.

This library works "interactively" with the user (like chat). XText API requires input and output stream to work. We want to use WebSocket to let users interact with this library smoothly (send/retrieve json messages).

We have a problem with SpringBoot because SpringBoot support for WebSocket doesn't expose input/output streams. We wrote custom TextWebSocketHandler (subclass) but none of it's methods provide access to in/out streams.

We also tried with HandshakeInterceptor (to obtain in/out streams after handshake ) but with no success.

Can we use SpringBoot WebSocket API in this scenario or should we use some lower level (Servlet?) API ?

Regards Daniel

  • Websockets do not stream. The APIs support input and output streams but only per WS message. Under the hood you are still writing framed messages. – user207421 Jun 18 '18 at 22:21

1 Answers1

1

I am not sure if this will fit your architecture or not, but I have achieved this by using Spring Boot's STOMP support and wiring it into a custom org.eclipse.lsp4j.jsonrpc.RemoteEndpoint, rather than using a lower level API.

The approach was inspired by reading through the code provided in org.eclipse.lsp4j.launch.LSPLauncher.

JSON handler

Marhalling and unmarshalling the JSON needs to be done with the API provided with the xtext language server, rather than Jackson (which would be used by the Spring STOMP integration)

Map<String, JsonRpcMethod> supportedMethods = new LinkedHashMap<String, JsonRpcMethod>();
supportedMethods.putAll(ServiceEndpoints.getSupportedMethods(LanguageClient.class));
supportedMethods.putAll(languageServer.supportedMethods());
jsonHandler = new MessageJsonHandler(supportedMethods);
jsonHandler.setMethodProvider(remoteEndpoint);

Response / notifications

Responses and notifications are sent by a message consumer which is passed to the remoteEndpoint when constructed. The message must be marshalled by the jsonHandler so as to prevent Jackson doing it.

remoteEndpoint = new RemoteEndpoint(new MessageConsumer() {
    @Override
    public void consume(Message message) { 
        simpMessagingTemplate.convertAndSendToUser('user', '/lang/message',
            jsonHandler.serialize(message));
    }
}, ServiceEndpoints.toEndpoint(languageServer));

Requests

Requests can be received by using a @MessageMapping method that takes the whole @Payload as a String to avoid Jackson unmarshalling it. You can then unmarshall yourself and pass the message to the remoteEndpoint.

@MessageMapping("/lang/message")
public void incoming(@Payload String message) {
   remoteEndpoint.consume(jsonHandler.parseMessage(message));
}

There may be a better way to do this, and I'll watch this question with interest, but this is an approach that I have found to work.

user2455157
  • 137
  • 1
  • 2
  • 11