import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
private final EventExecutor eventExecutor;
private final NetconfNodeAugmentedOptional nodeOptional;
+ @GuardedBy("this")
+ private ListenableFuture<List<Object>> schemaFuturesList;
@GuardedBy("this")
private boolean connected = false;
}
@Override
- public void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities,
- final NetconfDeviceCommunicator listener) {
+ public synchronized void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities,
+ final NetconfDeviceCommunicator listener) {
// SchemaContext setup has to be performed in a dedicated thread since
// we are in a netty thread in this method
// Yang models are being downloaded in this method and it would cause a
// Potentially acquire mount point list and interpret it
final ListenableFuture<MountPointContext> futureContext = Futures.transformAsync(futureSchema,
schemaContext -> createMountPointContext(schemaContext, baseSchema, listener), processingExecutor);
+ schemaFuturesList = Futures.allAsList(sourceResolverFuture, futureSchema, futureContext);
Futures.addCallback(futureContext, new FutureCallback<MountPointContext>() {
@Override
@Override
public void onFailure(final Throwable cause) {
- LOG.warn("{}: Unexpected error resolving device sources", id, cause);
+ if (cause instanceof CancellationException) {
+ LOG.warn("{}: Device communicator was tear down since the schema setup started", id);
+ return;
+ }
+ LOG.warn("{}: Unexpected error resolving device sources", id, cause);
// No more sources, fail or try to reconnect
if (cause instanceof EmptySchemaContextException) {
if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().getAllowed()) {
}
@Override
- public void onRemoteSessionDown() {
+ public synchronized void onRemoteSessionDown() {
setConnected(false);
+ if (schemaFuturesList != null && !schemaFuturesList.isDone()) {
+ if (!schemaFuturesList.cancel(true)) {
+ LOG.warn("The cleanup of Schema Futures for device {} was unsuccessful.", id);
+ }
+ }
notificationHandler.onRemoteSchemaDown();
-
salFacade.onDeviceDisconnected();
sourceRegistrations.forEach(SchemaSourceRegistration::close);
sourceRegistrations.clear();
verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any(), any(DOMActionService.class));
}
+ @Test
+ public void testNetconfDeviceReconnectBeforeSchemaSetup() throws Exception {
+ final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
+
+ final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
+ final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
+ doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
+
+ final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
+ getSchemaRegistry(), getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
+ final NetconfDevice device = new NetconfDeviceBuilder()
+ .setReconnectOnSchemasChange(true)
+ .setSchemaResourcesDTO(schemaResourcesDTO)
+ .setGlobalProcessingExecutor(getExecutor())
+ .setId(getId())
+ .setSalFacade(facade)
+ .setBaseSchemas(BASE_SCHEMAS)
+ .build();
+ final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
+ List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION));
+
+ final NetconfDeviceCommunicator listener = getListener();
+ // session up, start schema resolution
+ device.onRemoteSessionUp(sessionCaps, listener);
+ // session down
+ device.onRemoteSessionDown();
+ verify(facade, timeout(5000)).onDeviceDisconnected();
+ // session back up, start another schema resolution
+ device.onRemoteSessionUp(sessionCaps, listener);
+ // complete schema setup
+ schemaFuture.set(SCHEMA_CONTEXT);
+ // schema setup performed twice
+ verify(schemaContextProviderFactory, timeout(5000)).createEffectiveModelContext(anyCollection());
+ // onDeviceConnected called once
+ verify(facade, timeout(5000)).onDeviceConnected(
+ any(MountPointContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class),
+ isNull());
+ }
+
@Test
public void testNetconfDeviceAvailableCapabilitiesBuilding() throws Exception {
final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();