X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Fconnect%2Fnetconf%2FNetconfDevice.java;h=bf7a7931b656de72ec850a601800cbd70bf73702;hb=0cf6d180df77a544362e2480beb4b9119b50a8c6;hp=ecbee257ce6b93c7522af64239fda1a52409ef6a;hpb=98a820a6085dc303a39b0c1d9098abfde963416a;p=netconf.git diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java index ecbee257ce..bf7a7931b6 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java @@ -13,6 +13,8 @@ import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTr import com.google.common.base.Predicates; import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -20,20 +22,19 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.netty.util.concurrent.EventExecutor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Optional; +import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.checkerframework.checker.lock.qual.GuardedBy; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.dom.api.DOMRpcResult; import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.netconf.api.NetconfMessage; @@ -54,12 +55,13 @@ import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessag import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapabilityBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability; -import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability.FailureReason; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; +import org.opendaylight.yangtools.rfc8528.data.util.EmptyMountPointContext; import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; @@ -75,9 +77,6 @@ import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,12 +84,8 @@ import org.slf4j.LoggerFactory; /** * This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade. */ -public class NetconfDevice - implements RemoteDevice { - - @SuppressFBWarnings(value = "SLF4J_LOGGER_SHOULD_BE_PRIVATE", - justification = "Needed for common logging of related classes") - static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class); +public class NetconfDevice implements RemoteDevice { + private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class); private static final QName RFC8528_SCHEMA_MOUNTS_QNAME = QName.create( SchemaMountConstants.RFC8528_MODULE, "schema-mounts").intern(); @@ -102,51 +97,43 @@ public class NetconfDevice protected final SchemaSourceRegistry schemaRegistry; protected final SchemaRepository schemaRepository; - protected final List> sourceRegistrations = new ArrayList<>(); + protected final List sourceRegistrations = new ArrayList<>(); - private final RemoteDeviceHandler salFacade; + private final RemoteDeviceHandler salFacade; private final ListeningExecutorService processingExecutor; private final DeviceActionFactory deviceActionFactory; private final NetconfDeviceSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; private final boolean reconnectOnSchemasChange; private final BaseNetconfSchemas baseSchemas; - private final NetconfNode node; - private final EventExecutor eventExecutor; - private final NetconfNodeAugmentedOptional nodeOptional; @GuardedBy("this") private boolean connected = false; // Message transformer is constructed once the schemas are available - private MessageTransformer messageTransformer; + private MessageTransformer messageTransformer; public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final BaseNetconfSchemas baseSchemas, - final RemoteDeviceId id, final RemoteDeviceHandler salFacade, + final RemoteDeviceId id, final RemoteDeviceHandler salFacade, final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) { - this(schemaResourcesDTO, baseSchemas, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null, - null, null, null); + this(schemaResourcesDTO, baseSchemas, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null); } public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final BaseNetconfSchemas baseSchemas, - final RemoteDeviceId id, final RemoteDeviceHandler salFacade, + final RemoteDeviceId id, final RemoteDeviceHandler salFacade, final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange, - final DeviceActionFactory deviceActionFactory, final NetconfNode node, final EventExecutor eventExecutor, - final NetconfNodeAugmentedOptional nodeOptional) { + final DeviceActionFactory deviceActionFactory) { this.baseSchemas = requireNonNull(baseSchemas); this.id = id; this.reconnectOnSchemasChange = reconnectOnSchemasChange; this.deviceActionFactory = deviceActionFactory; - this.node = node; - this.eventExecutor = eventExecutor; - this.nodeOptional = nodeOptional; - this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry(); - this.schemaRepository = schemaResourcesDTO.getSchemaRepository(); - this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory(); + schemaRegistry = schemaResourcesDTO.getSchemaRegistry(); + schemaRepository = schemaResourcesDTO.getSchemaRepository(); + schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory(); this.salFacade = salFacade; - this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver(); - this.processingExecutor = requireNonNull(globalProcessingExecutor); - this.notificationHandler = new NotificationHandler(salFacade, id); + stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver(); + processingExecutor = requireNonNull(globalProcessingExecutor); + notificationHandler = new NotificationHandler(salFacade, id); } @Override @@ -171,39 +158,27 @@ public class NetconfDevice } // Set up the SchemaContext for the device - final ListenableFuture futureSchema = Futures.transformAsync(sourceResolverFuture, + final ListenableFuture futureSchema = Futures.transformAsync(sourceResolverFuture, deviceSources -> assembleSchemaContext(deviceSources, remoteSessionCapabilities), processingExecutor); // Potentially acquire mount point list and interpret it - final ListenableFuture futureContext = Futures.transformAsync(futureSchema, - schemaContext -> createMountPointContext(schemaContext, baseSchema, listener), processingExecutor); + final ListenableFuture futureContext = Futures.transformAsync(futureSchema, + result -> Futures.transform(createMountPointContext(result.modelContext(), baseSchema, listener), + mount -> new NetconfDeviceSchema(result.capabilities(), mount), processingExecutor), + processingExecutor); - Futures.addCallback(futureContext, new FutureCallback() { + Futures.addCallback(futureContext, new FutureCallback<>() { @Override - public void onSuccess(final MountPointContext result) { + public void onSuccess(final NetconfDeviceSchema result) { handleSalInitializationSuccess(result, remoteSessionCapabilities, - getDeviceSpecificRpc(result, listener, baseSchema), listener); + getDeviceSpecificRpc(result.mountContext(), listener, baseSchema), listener); } @Override public void onFailure(final Throwable cause) { 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()) { - eventExecutor.schedule(() -> { - LOG.warn("Reconnection is allowed! This can lead to unexpected errors at runtime."); - LOG.warn("{} : No more sources for schema context.", id); - LOG.info("{} : Try to remount device.", id); - onRemoteSessionDown(); - salFacade.onDeviceReconnected(remoteSessionCapabilities, node); - }, nodeOptional.getIgnoreMissingSchemaSources().getReconnectTime().toJava(), - TimeUnit.MILLISECONDS); - return; - } - } - + // FIXME: this causes salFacade to see onDeviceDisconnected() and then onDeviceFailed(), which is quite + // weird handleSalInitializationFailure(cause, listener); salFacade.onDeviceFailed(cause); } @@ -223,14 +198,14 @@ public class NetconfDevice @Override public void onSuccess(final DOMRpcResult domRpcResult) { notificationHandler.addNotificationFilter(notification -> { - if (NetconfCapabilityChange.QNAME.equals(notification.getBody().getNodeType())) { + if (NetconfCapabilityChange.QNAME.equals(notification.getBody().getIdentifier().getNodeType())) { LOG.info("{}: Schemas change detected, reconnecting", id); // Only disconnect is enough, // the reconnecting nature of the connector will take care of reconnecting listener.disconnect(); - return Optional.empty(); + return false; } - return Optional.of(notification); + return true; }); } @@ -246,23 +221,21 @@ public class NetconfDevice return remoteSessionCapabilities.isNotificationsSupported() && reconnectOnSchemasChange; } - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private synchronized void handleSalInitializationSuccess(final MountPointContext result, - final NetconfSessionPreferences remoteSessionCapabilities, - final DOMRpcService deviceRpc, - final RemoteDeviceCommunicator listener) { + private synchronized void handleSalInitializationSuccess(final NetconfDeviceSchema deviceSchema, + final NetconfSessionPreferences remoteSessionCapabilities, final DOMRpcService deviceRpc, + final RemoteDeviceCommunicator listener) { //NetconfDevice.SchemaSetup can complete after NetconfDeviceCommunicator was closed. In that case do nothing, //since salFacade.onDeviceDisconnected was already called. if (connected) { - this.messageTransformer = new NetconfMessageTransformer(result, true, + final var mount = deviceSchema.mountContext(); + messageTransformer = new NetconfMessageTransformer(mount, true, resolveBaseSchema(remoteSessionCapabilities.isNotificationsSupported())); // salFacade.onDeviceConnected has to be called before the notification handler is initialized - this.salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc, - this.deviceActionFactory == null ? null : this.deviceActionFactory.createDeviceAction( - this.messageTransformer, listener, result.getEffectiveModelContext())); - this.notificationHandler.onRemoteSchemaUp(this.messageTransformer); + salFacade.onDeviceConnected(deviceSchema, remoteSessionCapabilities, deviceRpc, + deviceActionFactory == null ? null : deviceActionFactory.createDeviceAction( + messageTransformer, listener, mount.getEffectiveModelContext())); + notificationHandler.onRemoteSchemaUp(messageTransformer); LOG.info("{}: Netconf connector initialized successfully", id); } else { @@ -270,10 +243,7 @@ public class NetconfDevice } } - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private void handleSalInitializationFailure(final Throwable throwable, - final RemoteDeviceCommunicator listener) { + private void handleSalInitializationFailure(final Throwable throwable, final RemoteDeviceCommunicator listener) { LOG.error("{}: Initialization in sal failed, disconnecting from device", id, throwable); listener.close(); onRemoteSessionDown(); @@ -287,7 +257,7 @@ public class NetconfDevice updateTransformer(null); } - private synchronized void updateTransformer(final MessageTransformer transformer) { + private synchronized void updateTransformer(final MessageTransformer transformer) { messageTransformer = transformer; } @@ -295,23 +265,18 @@ public class NetconfDevice this.connected = connected; } - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private ListenableFuture assembleSchemaContext(final DeviceSources deviceSources, + private ListenableFuture assembleSchemaContext(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities) { LOG.debug("{}: Resolved device sources to {}", id, deviceSources); - final SchemaSourceProvider yangProvider = deviceSources.getSourceProvider(); - for (final SourceIdentifier sourceId : deviceSources.getProvidedSources()) { - sourceRegistrations.add(schemaRegistry.registerSchemaSource(yangProvider, - PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, - PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); - } + + sourceRegistrations.addAll(deviceSources.register(schemaRegistry)); return new SchemaSetup(deviceSources, remoteSessionCapabilities).startResolution(); } - private ListenableFuture createMountPointContext(final EffectiveModelContext schemaContext, - final BaseSchema baseSchema, final NetconfDeviceCommunicator listener) { + private ListenableFuture<@NonNull MountPointContext> createMountPointContext( + final EffectiveModelContext schemaContext, final BaseSchema baseSchema, + final NetconfDeviceCommunicator listener) { final MountPointContext emptyContext = new EmptyMountPointContext(schemaContext); if (schemaContext.findModule(SchemaMountConstants.RFC8528_MODULE).isEmpty()) { return Futures.immediateFuture(emptyContext); @@ -333,7 +298,7 @@ public class NetconfDevice if (!errors.isEmpty()) { LOG.warn("{}: Schema-mounts acquisition resulted in errors {}", id, errors); } - final NormalizedNode schemaMounts = rpcResult.getResult(); + final NormalizedNode schemaMounts = rpcResult.getResult(); if (schemaMounts == null) { LOG.debug("{}: device does not define any schema mounts", id); return emptyContext; @@ -352,7 +317,7 @@ public class NetconfDevice notificationHandler.onRemoteSchemaDown(); salFacade.onDeviceDisconnected(); - sourceRegistrations.forEach(SchemaSourceRegistration::close); + sourceRegistrations.forEach(Registration::close); sourceRegistrations.clear(); resetMessageTransformer(); } @@ -373,7 +338,7 @@ public class NetconfDevice } protected NetconfDeviceRpc getDeviceSpecificRpc(final MountPointContext result, - final RemoteDeviceCommunicator listener, final BaseSchema schema) { + final RemoteDeviceCommunicator listener, final BaseSchema schema) { return new NetconfDeviceRpc(result.getEffectiveModelContext(), listener, new NetconfMessageTransformer(result, true, schema)); } @@ -394,7 +359,7 @@ public class NetconfDevice this.schemaRegistry = requireNonNull(schemaRegistry); this.schemaRepository = requireNonNull(schemaRepository); this.schemaContextFactory = requireNonNull(schemaContextFactory); - this.stateSchemasResolver = requireNonNull(deviceSchemasResolver); + stateSchemasResolver = requireNonNull(deviceSchemasResolver); } public SchemaSourceRegistry getSchemaRegistry() { @@ -415,34 +380,47 @@ public class NetconfDevice } /** - * A dedicated exception to indicate when we fail to setup a SchemaContext. - * - * @author Robert Varga + * A dedicated exception to indicate when we fail to setup an {@link EffectiveModelContext}. */ - private static final class EmptySchemaContextException extends Exception { + public static final class EmptySchemaContextException extends Exception { private static final long serialVersionUID = 1L; - EmptySchemaContextException(final String message) { + public EmptySchemaContextException(final String message) { super(message); } } + /** + * {@link NetconfDeviceCapabilities} and {@link EffectiveModelContext}. + */ + private record SchemaResult( + @NonNull NetconfDeviceCapabilities capabilities, + @NonNull EffectiveModelContext modelContext) { + + SchemaResult { + requireNonNull(capabilities); + requireNonNull(modelContext); + } + } + /** * Schema builder that tries to build schema context from provided sources or biggest subset of it. */ private final class SchemaSetup implements FutureCallback { - private final SettableFuture resultFuture = SettableFuture.create(); + private final SettableFuture resultFuture = SettableFuture.create(); + + private final Set nonModuleBasedCapabilities = new HashSet<>(); + private final Map unresolvedCapabilites = new HashMap<>(); + private final Set resolvedCapabilities = new HashSet<>(); private final DeviceSources deviceSources; private final NetconfSessionPreferences remoteSessionCapabilities; - private final NetconfDeviceCapabilities capabilities; private Collection requiredSources; SchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities) { this.deviceSources = deviceSources; this.remoteSessionCapabilities = remoteSessionCapabilities; - this.capabilities = remoteSessionCapabilities.getNetconfDeviceCapabilities(); // If device supports notifications and does not contain necessary modules, add them automatically if (remoteSessionCapabilities.containsNonModuleCapability( @@ -460,12 +438,12 @@ public class NetconfDevice requiredSources = deviceSources.getRequiredSources(); final Collection missingSources = filterMissingSources(requiredSources); - capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(missingSources), - UnavailableCapability.FailureReason.MissingSource); + addUnresolvedCapabilities(getQNameFromSourceIdentifiers(missingSources), + UnavailableCapability.FailureReason.MissingSource); requiredSources.removeAll(missingSources); } - ListenableFuture startResolution() { + ListenableFuture startResolution() { trySetupSchema(); return resultFuture; } @@ -475,19 +453,24 @@ public class NetconfDevice LOG.debug("{}: Schema context built successfully from {}", id, requiredSources); final Collection filteredQNames = Sets.difference(deviceSources.getRequiredSourcesQName(), - capabilities.getUnresolvedCapabilites().keySet()); - capabilities.addCapabilities(filteredQNames.stream().map(entry -> new AvailableCapabilityBuilder() - .setCapability(entry.toString()).setCapabilityOrigin( - remoteSessionCapabilities.getModuleBasedCapsOrigin().get(entry)).build()) - .collect(Collectors.toList())); - - capabilities.addNonModuleBasedCapabilities(remoteSessionCapabilities - .getNonModuleCaps().stream().map(entry -> new AvailableCapabilityBuilder() - .setCapability(entry).setCapabilityOrigin( - remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build()) - .collect(Collectors.toList())); - - resultFuture.set(result); + unresolvedCapabilites.keySet()); + resolvedCapabilities.addAll(filteredQNames.stream() + .map(capability -> new AvailableCapabilityBuilder() + .setCapability(capability.toString()) + .setCapabilityOrigin(remoteSessionCapabilities.capabilityOrigin(capability)) + .build()) + .collect(Collectors.toList())); + + nonModuleBasedCapabilities.addAll(remoteSessionCapabilities.getNonModuleCaps().stream() + .map(capability -> new AvailableCapabilityBuilder() + .setCapability(capability) + .setCapabilityOrigin(remoteSessionCapabilities.capabilityOrigin(capability)) + .build()) + .collect(Collectors.toList())); + + + resultFuture.set(new SchemaResult(new NetconfDeviceCapabilities(ImmutableMap.copyOf(unresolvedCapabilites), + ImmutableSet.copyOf(resolvedCapabilities), ImmutableSet.copyOf(nonModuleBasedCapabilities)), result)); } @Override @@ -521,7 +504,7 @@ public class NetconfDevice } } - private Collection filterMissingSources(final Collection origSources) { + private List filterMissingSources(final Collection origSources) { return origSources.parallelStream().filter(sourceIdentifier -> { try { schemaRepository.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class).get(); @@ -532,7 +515,13 @@ public class NetconfDevice }).collect(Collectors.toList()); } - private Collection handleMissingSchemaSourceException( + private void addUnresolvedCapabilities(final Collection capabilities, final FailureReason reason) { + for (QName s : capabilities) { + unresolvedCapabilites.put(s, reason); + } + } + + private List handleMissingSchemaSourceException( final MissingSchemaSourceException exception) { // In case source missing, try without it final SourceIdentifier missingSource = exception.getSourceId(); @@ -540,11 +529,9 @@ public class NetconfDevice id, missingSource); LOG.debug("{}: Unable to build schema context, missing source {}, will reattempt without it", id, missingSource, exception); - final Collection qNameOfMissingSource = - getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource)); + final var qNameOfMissingSource = getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource)); if (!qNameOfMissingSource.isEmpty()) { - capabilities.addUnresolvedCapabilities( - qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource); + addUnresolvedCapabilities(qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource); } return stripUnavailableSource(missingSource); } @@ -561,14 +548,13 @@ public class NetconfDevice id, failedSourceId); LOG.warn("{}: Unable to build schema context, failed to resolve source {}, will reattempt without it", id, failedSourceId, resolutionException); - capabilities.addUnresolvedCapabilities( - getQNameFromSourceIdentifiers(Collections.singleton(failedSourceId)), + addUnresolvedCapabilities(getQNameFromSourceIdentifiers(List.of(failedSourceId)), UnavailableCapability.FailureReason.UnableToResolve); return stripUnavailableSource(resolutionException.getFailedSource()); } // unsatisfied imports - final Set unresolvedSources = resolutionException.getUnsatisfiedImports().keySet(); - capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), + addUnresolvedCapabilities( + getQNameFromSourceIdentifiers(resolutionException.getUnsatisfiedImports().keySet()), UnavailableCapability.FailureReason.UnableToResolve); LOG.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports()); @@ -577,11 +563,11 @@ public class NetconfDevice return resolutionException.getResolvedSources(); } - private Collection stripUnavailableSource(final SourceIdentifier sourceIdToRemove) { - final LinkedList sourceIdentifiers = new LinkedList<>(requiredSources); - checkState(sourceIdentifiers.remove(sourceIdToRemove), - "%s: Trying to remove %s from %s failed", id, sourceIdToRemove, requiredSources); - return sourceIdentifiers; + private List stripUnavailableSource(final SourceIdentifier sourceIdToRemove) { + final var tmp = new ArrayList<>(requiredSources); + checkState(tmp.remove(sourceIdToRemove), "%s: Trying to remove %s from %s failed", id, sourceIdToRemove, + requiredSources); + return tmp; } private Collection getQNameFromSourceIdentifiers(final Collection identifiers) { @@ -597,11 +583,11 @@ public class NetconfDevice private QName getQNameFromSourceIdentifier(final SourceIdentifier identifier) { // Required sources are all required and provided merged in DeviceSourcesResolver for (final QName qname : deviceSources.getRequiredSourcesQName()) { - if (!qname.getLocalName().equals(identifier.getName())) { + if (!qname.getLocalName().equals(identifier.name().getLocalName())) { continue; } - if (identifier.getRevision().equals(qname.getRevision())) { + if (Objects.equals(identifier.revision(), qname.getRevision().orElse(null))) { return qname; } }