Browse Source

Move Californium server construction into init try/catch and guard CoAP shutdown

Construct CoapServer and the LwM2M bootstrap server inside the init try block so a failure in the constructor or build() is cleaned up by the existing catch. Guard CoAP shutdown() against a null server. Add a DTLS-enabled CoAP test that covers the dtlsSessionsExecutor shutdown branch.
pull/15651/head
Oleksandra Matviienko 4 months ago
parent
commit
a0f5b3bc5b
  1. 6
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java
  2. 66
      common/coap-server/src/test/java/org/thingsboard/server/coapserver/DefaultCoapServerServiceTest.java
  3. 7
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

6
common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java

@ -85,7 +85,9 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial
dtlsSessionsExecutor.shutdownNow(); dtlsSessionsExecutor.shutdownNow();
} }
log.info("Stopping CoAP server!"); log.info("Stopping CoAP server!");
server.destroy(); if (server != null) {
server.destroy();
}
log.info("CoAP server stopped!"); log.info("CoAP server stopped!");
} }
@ -105,8 +107,8 @@ public class DefaultCoapServerService implements CoapServerService, SmartInitial
private CoapServer createCoapServer() throws UnknownHostException { private CoapServer createCoapServer() throws UnknownHostException {
Configuration networkConfig = createNetworkConfiguration(); Configuration networkConfig = createNetworkConfiguration();
server = new CoapServer(networkConfig);
try { try {
server = new CoapServer(networkConfig);
CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder(); CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder();
InetAddress addr = InetAddress.getByName(coapServerContext.getHost()); InetAddress addr = InetAddress.getByName(coapServerContext.getHost());
InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort());

66
common/coap-server/src/test/java/org/thingsboard/server/coapserver/DefaultCoapServerServiceTest.java

@ -15,20 +15,36 @@
*/ */
package org.thingsboard.server.coapserver; package org.thingsboard.server.coapserver;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.thingsboard.common.util.ThingsBoardExecutors;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.ScheduledExecutorService;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -76,4 +92,54 @@ public class DefaultCoapServerServiceTest {
assertThat(ReflectionTestUtils.getField(service, "tbDtlsCertificateVerifier")).isNull(); assertThat(ReflectionTestUtils.getField(service, "tbDtlsCertificateVerifier")).isNull();
} }
@Test
public void whenDtlsEnabledAndStartFails_thenInitShutsDownDtlsExecutorAndReleasesCoapServer() throws Exception {
// DTLS enabled: the DTLS endpoint is created and dtlsSessionsExecutor is scheduled before server.start().
// This exercises the catch's dtlsSessionsExecutor.shutdownNow() branch, which the plain-bind test does not.
TbCoapDtlsSettings mockDtlsSettings = mock(TbCoapDtlsSettings.class);
when(mockCoapServerContext.getDtlsSettings()).thenReturn(mockDtlsSettings);
DtlsConnectorConfig mockDtlsConfig = mock(DtlsConnectorConfig.class);
when(mockDtlsConfig.getAddress()).thenReturn(new InetSocketAddress(InetAddress.getByName(HOST), occupiedPort + 1));
TbCoapDtlsCertificateVerifier mockVerifier = mock(TbCoapDtlsCertificateVerifier.class);
when(mockVerifier.getDtlsSessionReportTimeout()).thenReturn(1800000L);
when(mockDtlsConfig.getAdvancedCertificateVerifier()).thenReturn(mockVerifier);
when(mockDtlsSettings.dtlsConnectorConfig(any())).thenReturn(mockDtlsConfig);
ScheduledExecutorService mockExecutor = mock(ScheduledExecutorService.class);
Resource mockRoot = mock(Resource.class);
try (MockedStatic<ThingsBoardExecutors> executorsStatic = mockStatic(ThingsBoardExecutors.class);
MockedConstruction<CoapServer> serverMock = mockConstruction(CoapServer.class, (server, ctx) -> {
when(server.getRoot()).thenReturn(mockRoot);
doThrow(new IllegalStateException("None of the server endpoints could be started")).when(server).start();
});
MockedConstruction<DTLSConnector> dtlsMock = mockConstruction(DTLSConnector.class);
MockedConstruction<CoapEndpoint.Builder> builderMock = mockConstruction(CoapEndpoint.Builder.class, (builder, ctx) -> {
when(builder.setInetSocketAddress(any())).thenReturn(builder);
when(builder.setConfiguration(any())).thenReturn(builder);
when(builder.setConnector(any(DTLSConnector.class))).thenReturn(builder);
when(builder.build()).thenReturn(mock(CoapEndpoint.class));
})) {
executorsStatic.when(() -> ThingsBoardExecutors.newSingleThreadScheduledExecutor(anyString())).thenReturn(mockExecutor);
assertThatThrownBy(() -> service.init())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("None of the server endpoints could be started");
// DTLS branch was actually entered and the executor was created...
verify(mockDtlsSettings).dtlsConnectorConfig(any());
// ...and the cleanup branch shut it down and destroyed the server.
verify(mockExecutor).shutdownNow();
verify(serverMock.constructed().get(0)).destroy();
}
assertThat(ReflectionTestUtils.getField(service, "server")).isNull();
assertThat(ReflectionTestUtils.getField(service, "dtlsSessionsExecutor")).isNull();
assertThat(ReflectionTestUtils.getField(service, "dtlsConnector")).isNull();
assertThat(ReflectionTestUtils.getField(service, "dtlsCoapEndpoint")).isNull();
assertThat(ReflectionTestUtils.getField(service, "tbDtlsCertificateVerifier")).isNull();
}
} }

7
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

@ -82,15 +82,18 @@ public class LwM2MTransportBootstrapService implements SmartInitializingSingleto
@PostConstruct @PostConstruct
public void init() { public void init() {
log.info("Starting LwM2M transport bootstrap server..."); log.info("Starting LwM2M transport bootstrap server...");
LeshanBootstrapServer bootstrapServer = getLhBootstrapServer(); LeshanBootstrapServer bootstrapServer = null;
try { try {
bootstrapServer = getLhBootstrapServer();
this.server = bootstrapServer; this.server = bootstrapServer;
bootstrapServer.start(); bootstrapServer.start();
log.info("Started LwM2M transport bootstrap server."); log.info("Started LwM2M transport bootstrap server.");
} catch (RuntimeException e) { } catch (RuntimeException e) {
log.error("Failed to start LwM2M transport bootstrap server, releasing resources", e); log.error("Failed to start LwM2M transport bootstrap server, releasing resources", e);
try { try {
bootstrapServer.destroy(); if (bootstrapServer != null) {
bootstrapServer.destroy();
}
} catch (Exception suppressed) { } catch (Exception suppressed) {
e.addSuppressed(suppressed); e.addSuppressed(suppressed);
} finally { } finally {

Loading…
Cancel
Save