[Java] Catch errors from user callbacks (#25513)

* [Java] Catch errors from user callbacks

* test logger

* rebase

* fb
This commit is contained in:
Brennan 2020-09-11 11:44:00 -07:00 committed by GitHub
parent 83e31342a1
commit 9eb9de6c8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 2 deletions

View File

@ -223,7 +223,11 @@ public class HubConnection implements AutoCloseable {
List<InvocationHandler> handlers = this.handlers.get(invocationMessage.getTarget());
if (handlers != null) {
for (InvocationHandler handler : handlers) {
handler.getAction().invoke(invocationMessage.getArguments());
try {
handler.getAction().invoke(invocationMessage.getArguments());
} catch (Exception e) {
logger.error("Invoking client side method '{}' failed:", invocationMessage.getTarget(), e);
}
}
} else {
logger.warn("Failed to find handler for '{}' method.", invocationMessage.getTarget());

View File

@ -5,7 +5,7 @@ dependencies {
compile 'org.junit.jupiter:junit-jupiter-params:5.3.1'
runtime 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
implementation 'com.google.code.gson:gson:2.8.5'
testCompile 'org.slf4j:slf4j-jdk14:1.7.25'
compile 'ch.qos.logback:logback-classic:1.2.3'
implementation project(':core')
implementation project(':messagepack')
compile project(':messagepack')

View File

@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
import ch.qos.logback.classic.spi.ILoggingEvent;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Single;
@ -2565,6 +2566,37 @@ class HubConnectionTest {
assertEquals((short) 5, (short) person.getT());
}
@Test
public void throwFromOnHandlerRunsAllHandlers() {
AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>();
try (TestLogger logger = new TestLogger()) {
MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
hubConnection.on("inc", (param1) -> {
value1.set(param1);
throw new RuntimeException("throw from on handler");
}, String.class);
hubConnection.on("inc", (param1) -> {
value2.set(param1);
}, String.class);
hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait();
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"Hello World\"]}" + RECORD_SEPARATOR);
// Confirming that our handler was called and the correct message was passed in.
assertEquals("Hello World", value1.get());
assertEquals("Hello World", value2.get());
hubConnection.stop().timeout(1, TimeUnit.SECONDS).blockingAwait();
ILoggingEvent log = logger.assertLog("Invoking client side method 'inc' failed:");
assertEquals("throw from on handler", log.getThrowableProxy().getMessage());
}
}
@Test
public void receiveHandshakeResponseAndMessage() {
AtomicReference<Double> value = new AtomicReference<Double>(0.0);

View File

@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
package com.microsoft.signalr;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
class TestLogger implements AutoCloseable {
private Logger logger;
private ListAppender<ILoggingEvent> appender;
public TestLogger() {
this("com.microsoft.signalr.HubConnection");
}
public TestLogger(String category) {
this.logger = (Logger)LoggerFactory.getLogger(category);
this.appender = new ListAppender<ILoggingEvent>();
this.appender.start();
this.logger.addAppender(this.appender);
}
public ILoggingEvent assertLog(String logMessage) {
for (ILoggingEvent log : appender.list) {
if (log.getFormattedMessage().startsWith(logMessage)) {
return log;
}
}
assertTrue(false, String.format("Log message '%s' not found", logMessage));
return null;
}
@Override
public void close() {
this.logger.detachAppender(this.appender);
}
}