how to send binaryMessage in Spring WebSocketHandler? - spring

I am using WebFlux.
No matter what the data client sends, the WebSocket Broker just need to send it back. The logic I want is easy.
I tried to use WebSocketSession.binaryMessage(). However I don't know how to make it work.
I searched on Google and GitHub, but I failed to find the answer. Here's my incorrect code:
public class ReactiveWebSocketHandler implements WebSocketHandler {
#Override
public Mono<Void> handle(WebSocketSession serverSession) {
return serverSession.send(
serverSession.receive());
}
}
When running the code above, I got Error:
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
at io.netty.util.internal.ReferenceCountUpdater.toLiveRealRefCnt(ReferenceCountUpdater.java:83)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException
Could you tell me how to write the code to implement the logic?

Related

Feign Client throws HystrixTimeoutException even though the underlying request is successful

I have a feign client like this with endpoints to two APIs from PROJECT-SERVICE
#FeignClient(name = "PROJECT-SERVICE", fallbackFactory = ProjectServiceFallbackFactory.class)
public interface ProjectServiceClient {
#GetMapping("/api/projects/{projectKey}")
public ResponseEntity<Project> getProjectDetails(#PathVariable("projectKey") String projectKey);
#PostMapping("/api/projects")
public ResponseEntity<Project> createProject(#RequestBody Project project);
}
I'm using those clients like this:
#Service
public class MyService {
#Autowired
private ProjectServiceClient projectServiceClient;
public void doSomething() {
// Some code
ResponseEntity<Project> projectResponse = projectServiceClient.getProjectDetails(projectKey);
// Some more code
}
public void doSomethingElse() {
// Some code
ResponseEntity<Project> projectResponse = projectServiceClient.createProject(Project projectToBeCreated);
// Some more code
}
}
My problem is, most of the times (around 60% of the time), either one of these Feign calls result in a HystrixTimeoutException.
I initially thought there could be a problem in the downstream micro service (PROJECT-SERVICE in this case), but that is not the case. In fact, when getProjectDetails() or createProject() is called, the PROJECT-SERVICE actually does the job and returns a ResponseEntity<Project> with status 200 and 201 respectively, but my fallback is activated with the HystrixTimeoutException.
I'm trying in vain to find what might be causing this issue.
I, however, have this in my main application configuration:
feign.hystrix.enabled=true
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=60000
Can anyone point me towards a solution?
Thanks,
Sriram Sridharan
Hystrix's timeout is not tied to that of Feign. There is a default 1 second execution timeout enabled for Hystrix. You need to configure this timeout to be slightly longer than Feign's, to avoid HystrixTimeoutException getting thrown earlier than desired timeout. Like so:
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=5000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
Doing so would allow FeignException, caused by timeout after 5 seconds, to be thrown first, and then wrapped in a HystrixTimeoutException

Sending async message from micronaut ServerWebSocket

I am trying to implement simple WebSocket server using Micronaut (and Groovy). I am new to Micronaut, so learning it as I go, and with it some RxJava seems required. So learning that as well.
I can make the basic example work:
#CompileStatic
#ServerWebSocket("/ws")
class TimeseriesWebSocket {
#OnOpen
public Publisher<String> onOpen(WebSocketSession session) {
println("opening connection")
return session.send("Hello")
}
#OnMessage
public Publisher<String> onMessage(String message, WebSocketSession session) {
println("message received")
return session.send("Thanks for the message")
}
#OnClose
public Publisher<String> onClose(WebSocketSession session) {
println("Closing")
//this seems not to be delivered, I guess due to session being closed already
return session.send("OK")
}
}
So this prints out all the messages I put there and works fine with a client connecting. The client also sees the "Hello" and "Thanks for the message" messages that are return with session.send(..).
Now my problem is, I am trying to send a message outside these methods. Like this:
#CompileStatic
#ServerWebSocket("/ws")
class MyWebSocket {
#OnOpen
public Publisher<String> onOpen(WebSocketSession session) {
println("opening connection")
startPing()
return session.send("Hello")
}
//...(same as above)
public void startPing(WebSocketSession session) {
PingPing ping = new PingPing(session)
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(ping, 0, 1, TimeUnit.SECONDS);
}
}
class PingPing {
WebSocketSession session
public PingPing(WebSocketSession session) {
this.session = session
}
#Override
void run() {
println("pinging..")
try {
session.send("Ping...")
} catch (Exception e) {
e.printStackTrace()
}
}
}
This executes but nothing shows up on the client end. Now if I change session.send() to session.sendSync() it works fine. The ping is delivered.
The send() signature is actually
default <T> Publisher<T> send(T message)
I figured at first I should provide the Publisher to some other source to have it sent. But I guess this is not the case. I realized it is sort of a Future object, so if I subscribe to it myself like this:
def publisher = session.send("Ping...")
publisher.subscribe(new Subscriber<GString>() {
#Override
void onSubscribe(Subscription s) {
println("subscription")
}
#Override
void onNext(GString gString) {
println("next")
}
#Override
void onError(Throwable t) {
println("error")
t.printStackTrace()
}
#Override
void onComplete() {
println("complete")
}
})
println("publisher: ${publisher}")
Running the above piece of code (with subscribe), I guess it triggers the session.send() on the current thread and returns a result. But where should I actually call this? On what thread? I looked at the RxJava schedulers but could not quite understand where to call it from.
Further, the result of running the above actually delivers the message to the client, but also throws an error:
error
io.reactivex.exceptions.MissingBackpressureException: create: could not emit value due to lack of requests
at io.reactivex.internal.operators.flowable.FlowableCreate$ErrorAsyncEmitter.onOverflow(FlowableCreate.java:438)
at io.reactivex.internal.operators.flowable.FlowableCreate$NoOverflowBaseAsyncEmitter.onNext(FlowableCreate.java:406)
at io.micronaut.http.netty.websocket.NettyRxWebSocketSession.lambda$null$2(NettyRxWebSocketSession.java:191)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
What is the backpressure and values / requests this referring to, and what is the actual way I should process an async send of the message? I expect it to just send the single item I am trying to send..
The Micronaut API documentation mentions following the javax.websocket API closely but
the javax.websocket async API seems to make more sense in just providing a Future to listen to.
So the short question is, how to use the Micronaut Websocket API to send message with the async mode outside the Micronaut provided functions? Or am I doing it all wrong?
It seems like I might be making some general wrong assumption about this API, but cannot figure it out from docs and cannot find example.
I had the same problem with micronaut v1.3.2.
I managed to get it working with sendAsync/sendSync instead of send.
I had a look at the implementation of NettyRxWebSocketSession and it looks like send is implemented somewhat differently than sendAsync. Not clear if its due to configuration or just problem with the implementation of send.
I hope this helps.

SpringBoot/Kotlin: Multipart MaxUploadSizeExceededException handler does not fire

Please excuse any horrible mistakes, I literally picked up Kotlin fully and some Spring about a week ago, due to a project requirement. I am trying to create a simple RESTful API with an endpoint that can accept a file via Multipart. Later on, there will be a few pages outside the API that will display HTML, I am using Koreander for that. From what I can see, and based on Java tutorials, exception handling should work like this.
For the API, my defined exception handler for MaxUploadSizeExceededException, however, does not fire at all. My application.kt:
#SpringBootApplication
#EnableConfigurationProperties(StorageProperties::class)
class JaApplication {
#Bean fun koreanderViewResolver(): ViewResolver = KoreanderViewResolver()
}
fun main(args: Array<String>) {
runApplication<JaApplication>(*args)
}
My controller:
#RestController
#RequestMapping("/api")
#EnableAutoConfiguration
class APIController {
#PostMapping(
value = "/convert",
produces = arrayOf(MediaType.APPLICATION_JSON_VALUE)
)
fun convert(#RequestParam("file") multipartFile: MultipartFile): Result {
return Result(status = 0, message = "You have uploaded: ${multipartFile.getOriginalFilename()}.")
}
}
#ControllerAdvice
class ExceptionHandlers {
#ExceptionHandler(MultipartException::class)
fun handleException(e: MultipartException): String = Result(status = 1, message = "File is too large.")
}
}
When I am attempting to post a large file via curl, I get a JSON reply:
curl -F 'file=#path-to-large-file' http://localhost:8080/api/convert
{"timestamp":"2018-11-27T15:47:31.907+0000","status":500,"error":"Internal Serve
r Error","message":"Maximum upload size exceeded; nested exception is java.lang.
IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$Siz
eLimitExceededException: the request was rejected because its size (4294967496)
exceeds the configured maximum (529530880)","path":"/api/convert"}
Is it possible that Tomcat does not pass this exception to Spring? If yes, how may I go about catching this? It also works if I can set the size to unlimited and check for file size at upload time, though I would need to do that before the server starts receiving the file, and I assume by the time I get to the /api/convert endpoint, it is too late.
Thanks for any help.
Found the issue. I'm posting it for anyone else who might have the same issue in the future.
#ControllerAdvice
class ExceptionHandlers():ResponseEntityExceptionHandler() {
#ExceptionHandler(MaxUploadSizeExceededException::class)
fun handleException(e: MultipartException, request:WebRequest): ResponseEntity<Any> {
return handleExceptionInternal(e, Result(message = "File is too large."), HttpHeaders(), HttpStatus.PAYLOAD_TOO_LARGE, request)
}
}

how to extend MockWebServiceServer to perform other assertions?

I have a flow that ends with sending a SOAP request. I'd like to write some kind of integration tests, for which I give 10 elements in input, and after going through the flow, I validate that 4 requests were fired for the 4 elements I expect (the 6 others got filtered and didn't make it through).
I'm using WebServiceTemplate, and I've read about MockWebServiceServer, but I am not sure it allows to do it out of the box. I'd like to maybe extend it, so that all sent requests are saved in a List that I can access to perform the assertions. I've looked at the source code, of MockWebServiceServer / MockWebServiceMessageSender but I don't see where I would do it.
Any ideas of how to achieve this ?
Thanks
One way of doing this is to extend RequestMatcher, not MockWebServiceServer. Here's an example of the class :
public class NeverFailingRequestMatcherWithMemory implements RequestMatcher {
List<WebServiceMessage> sentRequests=new ArrayList<WebServiceMessage>();
#Override
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
sentRequests.add(request);
}
public void clearMemory(){
sentRequests.clear();
}
public List<WebServiceMessage> getSentRequests(){
return sentRequests;
}
}
And you use it like this in your tests :
NeverFailingRequestMatcherWithMemory matcherWithMemory=new NeverFailingRequestMatcherWithMemory();
#Before
public void configureMockWsServer() {
WebServiceTemplate usedWebServiceTemplate = appCtx.getBean(WebServiceTemplate.class);
mockServer = MockWebServiceServer.createServer(usedWebServiceTemplate);
matcherWithMemory.clearMemory();
}
and later in your tests, something like :
mockServer.expect(matcherWithMemory).andRespond(withPayload(someResponsePayload));
assertThat(matcherWithMemory.getSentRequests()).hasSize(1);
Then you have access to the requests that were sent and can parse them the way you want.

How can I send a message on connect event (SockJS, STOMP, Spring)?

I am connection through SockJS over STOMP to my Spring backend. Everything work fine, the configuration works well for all browsers etc. However, I cannot find a way to send an initial message. The scenario would be as follows:
The client connects to the topic
function connect() {
var socket = new SockJS('http://localhost:8080/myEndpoint');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/notify', function(message){
showMessage(JSON.parse(message.body).content);
});
});
}
and the backend config looks more or less like this:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketAppConfig extends AbstractWebSocketMessageBrokerConfigurer {
...
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/myEndpoint").withSockJS();
}
I want to send to the client an automatic reply from the backend (on the connection event) so that I can already provide him with some dataset (e.g. read sth from the db) without the need for him (the client) to send a GET request (or any other). So to sum up, I just want to send him a message on the topic with the SimMessagingTemplate object just after he connected.
Usually I do it the following way, e.g. in a REST controller, when the template is already autowired:
#Autowired
private SimpMessagingTemplate template;
...
template.convertAndSend(TOPIC, new Message("it works!"));
How to achieve this on connect event?
UPDATE
I have managed to make it work. However, I am still a bit confused with the configuration. I will show here 2 configurations how the initial message can be sent:
1) First solution
JS part
stompClient.subscribe('/app/pending', function(message){
showMessage(JSON.parse(message.body).content);
});
stompClient.subscribe('/topic/incoming', function(message){
showMessage(JSON.parse(message.body).content);
});
Java part
#Controller
public class WebSocketBusController {
#SubscribeMapping("/pending")
Configuration
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
... and other calls
template.convertAndSend("/topic/incoming", outgoingMessage);
2) Second solution
JS part
stompClient.subscribe('/topic/incoming', function(message){
showMessage(JSON.parse(message.body).content);
})
Java part
#Controller
public class WebSocketBusController {
#SubscribeMapping("/topic/incoming")
Configuration
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
// NO APPLICATION PREFIX HERE
}
... and other calls
template.convertAndSend("/topic/incoming", outgoingMessage);
SUMMARY:
The first case uses two subscriptions - this I wanted to avoid and thought this can be managed with one only.
The second one however has no prefix for application. But at least I can have a single subscription to listen on the provided topic as well as send initial message.
If you just want to send a message to the client upon connection, use an appropriate ApplicationListener:
#Component
public class StompConnectedEvent implements ApplicationListener<SessionConnectedEvent> {
private static final Logger log = Logger.getLogger(StompConnectedEvent.class);
#Autowired
private Controller controller;
#Override
public void onApplicationEvent(SessionConnectedEvent event) {
log.debug("Client connected.");
// you can use a controller to send your msg here
}
}
You can't do that on connect, however the #SubscribeMapping does the stuff in that case.
You just need to mark the service method with that annotation and it returns a result to the subscribe function.
From Spring Reference Manual:
An #SubscribeMapping annotation can also be used to map subscription requests to #Controller methods. It is supported on the method level, but can also be combined with a type level #MessageMapping annotation that expresses shared mappings across all message handling methods within the same controller.
By default the return value from an #SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker. This is useful for implementing request-reply message interactions; for example, to fetch application data when the application UI is being initialized. Or alternatively an #SubscribeMapping method can be annotated with #SendTo in which case the resulting message is sent to the "brokerChannel" using the specified target destination.
UPDATE
Referring to this example: https://github.com/revelfire/spring4Test how would that be possible to send anything when the line 24 of the index.html is invoked: stompClient.subscribe('/user/queue/socket/responses' ... from the spring controllers?
Well, look like this:
#SubscribeMapping("/queue/socket/responses")
public List<Employee> list() {
return getEmployees();
}
The Stomp client part remains the same.

Resources