Merge "BUG-997 Use shared schema context factory in netconf-connector"
authorEd Warnicke <eaw@cisco.com>
Wed, 13 Aug 2014 13:43:31 +0000 (13:43 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 13 Aug 2014 13:43:31 +0000 (13:43 +0000)
16 files changed:
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModuleFactory.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/NetconfDeviceConnectionHandler.java
opendaylight/netconf/pom.xml

index b75df80f4ef4b3091975e3b5e0f50a70ed9bc71c..bca47af5c0b3deddaeb1bacd1c86346892eaeafa 100644 (file)
@@ -10,12 +10,10 @@ package org.opendaylight.controller.config.yang.md.sal.connector.netconf;
 import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkCondition;
 import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkNotNull;
 
-import java.io.File;
-import java.io.InputStream;
+import com.google.common.base.Optional;
 import java.net.InetSocketAddress;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
-
 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
@@ -25,9 +23,11 @@ import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.controller.sal.connect.netconf.NetconfStateSchemas;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade;
+import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
@@ -35,16 +35,12 @@ import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
-import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-
 /**
  *
  */
@@ -52,9 +48,10 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
 {
     private static final Logger logger = LoggerFactory.getLogger(NetconfConnectorModule.class);
 
-    private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
     private BundleContext bundleContext;
     private Optional<NetconfSessionCapabilities> userCapabilities;
+    private SchemaSourceRegistry schemaRegistry;
+    private SchemaContextFactory schemaContextFactory;
 
     public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
@@ -108,8 +105,12 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
 
         final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade
                 = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
+
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
+                new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+
         final NetconfDevice device =
-                NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade);
+                new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer());
 
         final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
                 new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
@@ -148,17 +149,6 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         return Optional.of(parsedOverrideCapabilities);
     }
 
-    private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider() {
-        if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
-            final String storageFile = "cache/schema";
-            //            File directory = bundleContext.getDataFile(storageFile);
-            final File directory = new File(storageFile);
-            final SchemaSourceProvider<String> defaultProvider = SchemaSourceProviders.noopProvider();
-            GLOBAL_NETCONF_SOURCE_PROVIDER = FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory);
-        }
-        return GLOBAL_NETCONF_SOURCE_PROVIDER;
-    }
-
     public void setBundleContext(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
     }
@@ -212,4 +202,12 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
             return new InetSocketAddress(ip, getPort().getValue());
         }
     }
+
+    public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
+        this.schemaRegistry = schemaRegistry;
+    }
+
+    public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
+        this.schemaContextFactory = schemaContextFactory;
+    }
 }
index 9842139dab7cfaa311b8dc222fd1f41b8f2554c6..b6299697cc5f08d91dd6f9588f03e5af1a340e5e 100644 (file)
@@ -7,9 +7,17 @@
  */
 package org.opendaylight.controller.config.yang.md.sal.connector.netconf;
 
+import java.io.File;
+
 import org.opendaylight.controller.config.api.DependencyResolver;
 import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
 import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
 import org.osgi.framework.BundleContext;
 
 /**
@@ -18,20 +26,38 @@ import org.osgi.framework.BundleContext;
 public class NetconfConnectorModuleFactory extends
         org.opendaylight.controller.config.yang.md.sal.connector.netconf.AbstractNetconfConnectorModuleFactory {
 
+    // TODO this should be injected
+    // Netconf devices have separated schema registry + factory from controller
+    private final SharedSchemaRepository repository = new SharedSchemaRepository(NAME);
+    private final SchemaContextFactory schemaContextFactory
+            = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+
+    public NetconfConnectorModuleFactory() {
+        // Start cache and Text to AST transformer
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema"));
+        repository.registerSchemaSourceListener(cache);
+        repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
+    }
+
     @Override
-    public Module createModule(String instanceName, DependencyResolver dependencyResolver,
-            DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception {
-        NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
+    public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
+            final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
+        final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
                 old, bundleContext);
+
         module.setBundleContext(bundleContext);
+        module.setSchemaRegistry(repository);
+        module.setSchemaContextFactory(schemaContextFactory);
         return module;
     }
 
     @Override
-    public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
-        NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
+    public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+        final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
                 bundleContext);
         module.setBundleContext(bundleContext);
+        module.setSchemaRegistry(repository);
+        module.setSchemaContextFactory(schemaContextFactory);
         return module;
     }
 }
index b2845d5533358a059bd7ecc4014ebae5717f0ee6..269c4af82fcb88c893cc6087c5ca63df3451738b 100644 (file)
@@ -9,11 +9,11 @@ package org.opendaylight.controller.sal.connect.api;
 
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public interface RemoteDeviceHandler<PREF> extends AutoCloseable {
 
-    void onDeviceConnected(SchemaContextProvider remoteSchemaContextProvider,
+    void onDeviceConnected(SchemaContext remoteSchemaContext,
                            PREF netconfSessionPreferences, RpcImplementation deviceRpc);
 
     void onDeviceDisconnected();
index 350132cf99a5dfb05681b0732912e7a4220335d2..cc9eb5a851271c8ed221d94038b9db5e35f92059 100644 (file)
@@ -7,41 +7,47 @@
  */
 package org.opendaylight.controller.sal.connect.netconf;
 
-import java.io.InputStream;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
-
 import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDevice;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
-import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory;
-import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
-import org.opendaylight.controller.sal.connect.netconf.schema.NetconfDeviceSchemaProviderFactory;
-import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaSourceProvider;
-import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
+import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+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.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-
 /**
  *  This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade
  */
@@ -49,51 +55,33 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionCapabilit
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class);
 
+    public static final Function<QName, SourceIdentifier> QNAME_TO_SOURCE_ID_FUNCTION = new Function<QName, SourceIdentifier>() {
+        @Override
+        public SourceIdentifier apply(final QName input) {
+            return new SourceIdentifier(input.getLocalName(), Optional.fromNullable(input.getFormattedRevision()));
+        }
+    };
+
     private final RemoteDeviceId id;
 
+    private final SchemaContextFactory schemaContextFactory;
     private final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade;
     private final ListeningExecutorService processingExecutor;
+    private final SchemaSourceRegistry schemaRegistry;
     private final MessageTransformer<NetconfMessage> messageTransformer;
-    private final SchemaContextProviderFactory schemaContextProviderFactory;
-    private final SchemaSourceProviderFactory<InputStream> sourceProviderFactory;
     private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
     private final NotificationHandler notificationHandler;
+    private final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations = Lists.newArrayList();
 
-    public static NetconfDevice createNetconfDevice(final RemoteDeviceId id,
-            final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider,
-            final ExecutorService executor, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade) {
-        return createNetconfDevice(id, schemaSourceProvider, executor, salFacade, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
-    }
-
-    @VisibleForTesting
-    protected static NetconfDevice createNetconfDevice(final RemoteDeviceId id,
-            final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider,
-            final ExecutorService executor, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
-            final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
-
-        return new NetconfDevice(id, salFacade, executor, new NetconfMessageTransformer(),
-                new NetconfDeviceSchemaProviderFactory(id), new SchemaSourceProviderFactory<InputStream>() {
-                    @Override
-                    public SchemaSourceProvider<InputStream> createSourceProvider(final RpcImplementation deviceRpc) {
-                        return schemaSourceProvider.createInstanceFor(new NetconfRemoteSchemaSourceProvider(id,
-                                deviceRpc));
-                    }
-                }, stateSchemasResolver);
-    }
-
-    @VisibleForTesting
-    protected NetconfDevice(final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
-                            final ExecutorService processingExecutor, final MessageTransformer<NetconfMessage> messageTransformer,
-                            final SchemaContextProviderFactory schemaContextProviderFactory,
-                            final SchemaSourceProviderFactory<InputStream> sourceProviderFactory,
-                            final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
+    public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
+                         final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> messageTransformer) {
         this.id = id;
+        this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
         this.messageTransformer = messageTransformer;
+        this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
         this.salFacade = salFacade;
-        this.sourceProviderFactory = sourceProviderFactory;
-        this.stateSchemasResolver = stateSchemasResolver;
-        this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor);
-        this.schemaContextProviderFactory = schemaContextProviderFactory;
+        this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver();
+        this.processingExecutor = MoreExecutors.listeningDecorator(globalProcessingExecutor);
         this.notificationHandler = new NotificationHandler(salFacade, messageTransformer, id);
     }
 
@@ -107,60 +95,73 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionCapabilit
         // http://netty.io/wiki/thread-model.html
         logger.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities);
 
-        final ListenableFuture<?> salInitializationFuture = processingExecutor.submit(new Runnable() {
+        final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(listener);
+
+        final DeviceSourcesResolver task = new DeviceSourcesResolver(deviceRpc, remoteSessionCapabilities, id, stateSchemasResolver);
+        final ListenableFuture<DeviceSources> sourceResolverFuture = processingExecutor.submit(task);
+
+        final FutureCallback<DeviceSources> resolvedSourceCallback = new FutureCallback<DeviceSources>() {
             @Override
-            public void run() {
-                final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(remoteSessionCapabilities, listener);
-
-                final NetconfStateSchemas availableSchemas = stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id);
-                logger.warn("{}: Schemas exposed by ietf-netconf-monitoring: {}", id, availableSchemas.getAvailableYangSchemasQNames());
-                // TODO use this for shared schema context
-
-                final SchemaSourceProvider<InputStream> delegate = sourceProviderFactory.createSourceProvider(deviceRpc);
-                final SchemaContextProvider schemaContextProvider = setUpSchemaContext(delegate, remoteSessionCapabilities);
-                updateMessageTransformer(schemaContextProvider);
-                salFacade.onDeviceConnected(schemaContextProvider, remoteSessionCapabilities, deviceRpc);
-                notificationHandler.onRemoteSchemaUp();
+            public void onSuccess(final DeviceSources result) {
+                addProvidedSourcesToSchemaRegistry(deviceRpc, result);
+                setUpSchema(result);
             }
-        });
 
-        Futures.addCallback(salInitializationFuture, new FutureCallback<Object>() {
-            @Override
-            public void onSuccess(final Object result) {
-                logger.debug("{}: Initialization in sal successful", id);
-                logger.info("{}: Netconf connector initialized successfully", id);
+            private void setUpSchema(final DeviceSources result) {
+                processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, deviceRpc, listener));
             }
 
             @Override
             public void onFailure(final Throwable t) {
-                // Unable to initialize device, set as disconnected
-                logger.error("{}: Initialization failed", id, t);
-                salFacade.onDeviceDisconnected();
-                // TODO ssh connection is still open if sal initialization fails
+                logger.warn("{}: Unexpected error resolving device sources: {}", id, t);
+                handleSalInitializationFailure(t, listener);
             }
-        });
+        };
+
+        Futures.addCallback(sourceResolverFuture, resolvedSourceCallback);
+    }
+
+    private void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionCapabilities remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc) {
+        updateMessageTransformer(result);
+        salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
+        notificationHandler.onRemoteSchemaUp();
+
+        logger.debug("{}: Initialization in sal successful", id);
+        logger.info("{}: Netconf connector initialized successfully", id);
+    }
+
+    private void handleSalInitializationFailure(final Throwable t, final RemoteDeviceCommunicator<NetconfMessage> listener) {
+        logger.error("{}: Initialization in sal failed, disconnecting from device", id, t);
+        listener.close();
+        onRemoteSessionDown();
     }
 
     /**
      * Update initial message transformer to use retrieved schema
+     * @param currentSchemaContext
      */
-    private void updateMessageTransformer(final SchemaContextProvider schemaContextProvider) {
-        messageTransformer.onGlobalContextUpdated(schemaContextProvider.getSchemaContext());
+    private void updateMessageTransformer(final SchemaContext currentSchemaContext) {
+        messageTransformer.onGlobalContextUpdated(currentSchemaContext);
     }
 
-    private SchemaContextProvider setUpSchemaContext(final SchemaSourceProvider<InputStream> sourceProvider, final NetconfSessionCapabilities capabilities) {
-        return schemaContextProviderFactory.createContextProvider(capabilities.getModuleBasedCaps(), sourceProvider);
+    private void addProvidedSourcesToSchemaRegistry(final NetconfDeviceRpc deviceRpc, final DeviceSources deviceSources) {
+        final NetconfRemoteSchemaYangSourceProvider yangProvider = new NetconfRemoteSchemaYangSourceProvider(id, deviceRpc);
+        for (final SourceIdentifier sourceId : deviceSources.getProvidedSources()) {
+            sourceRegistrations.add(schemaRegistry.registerSchemaSource(yangProvider,
+                    PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
+        }
     }
 
-    private NetconfDeviceRpc setUpDeviceRpc(final NetconfSessionCapabilities capHolder, final RemoteDeviceCommunicator<NetconfMessage> listener) {
-        Preconditions.checkArgument(capHolder.isMonitoringSupported(),
-                "%s: Netconf device does not support netconf monitoring, yang schemas cannot be acquired. Netconf device capabilities", capHolder);
-        return new NetconfDeviceRpc(listener, messageTransformer);
+    private NetconfDeviceRpc setUpDeviceRpc(final RemoteDeviceCommunicator<NetconfMessage> listener) {
+       return new NetconfDeviceRpc(listener, messageTransformer);
     }
 
     @Override
     public void onRemoteSessionDown() {
         salFacade.onDeviceDisconnected();
+        for (final SchemaSourceRegistration<? extends SchemaSourceRepresentation> sourceRegistration : sourceRegistrations) {
+            sourceRegistration.close();
+        }
     }
 
     @Override
@@ -169,59 +170,181 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionCapabilit
     }
 
     /**
-     * Handles incoming notifications. Either caches them(until onRemoteSchemaUp is called) or passes to sal Facade.
+     * Just a transfer object containing schema related dependencies. Injected in constructor.
      */
-    private final static class NotificationHandler {
+    public static class SchemaResourcesDTO {
+        private final SchemaSourceRegistry schemaRegistry;
+        private final SchemaContextFactory schemaContextFactory;
+        private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
+
+        public SchemaResourcesDTO(final SchemaSourceRegistry schemaRegistry, final SchemaContextFactory schemaContextFactory, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
+            this.schemaRegistry = Preconditions.checkNotNull(schemaRegistry);
+            this.schemaContextFactory = Preconditions.checkNotNull(schemaContextFactory);
+            this.stateSchemasResolver = Preconditions.checkNotNull(stateSchemasResolver);
+        }
+
+        public SchemaSourceRegistry getSchemaRegistry() {
+            return schemaRegistry;
+        }
+
+        public SchemaContextFactory getSchemaContextFactory() {
+            return schemaContextFactory;
+        }
 
-        private final RemoteDeviceHandler<?> salFacade;
-        private final List<NetconfMessage> cache = new LinkedList<>();
-        private final MessageTransformer<NetconfMessage> messageTransformer;
-        private boolean passNotifications = false;
+        public NetconfStateSchemas.NetconfStateSchemasResolver getStateSchemasResolver() {
+            return stateSchemasResolver;
+        }
+    }
+
+    /**
+     * Schema building callable.
+     */
+    private static class DeviceSourcesResolver implements Callable<DeviceSources> {
+        private final NetconfDeviceRpc deviceRpc;
+        private final NetconfSessionCapabilities remoteSessionCapabilities;
         private final RemoteDeviceId id;
+        private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
 
-        NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> messageTransformer, final RemoteDeviceId id) {
-            this.salFacade = salFacade;
-            this.messageTransformer = messageTransformer;
+        public DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionCapabilities remoteSessionCapabilities, final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
+            this.deviceRpc = deviceRpc;
+            this.remoteSessionCapabilities = remoteSessionCapabilities;
             this.id = id;
+            this.stateSchemasResolver = stateSchemasResolver;
         }
 
-        synchronized void handleNotification(final NetconfMessage notification) {
-            if(passNotifications) {
-                passNotification(messageTransformer.toNotification(notification));
-            } else {
-                cacheNotification(notification);
+        @Override
+        public DeviceSources call() throws Exception {
+
+            final Set<SourceIdentifier> requiredSources = Sets.newHashSet(Collections2.transform(
+                    remoteSessionCapabilities.getModuleBasedCaps(), QNAME_TO_SOURCE_ID_FUNCTION));
+
+            // If monitoring is not supported, we will still attempt to create schema, sources might be already provided
+            final NetconfStateSchemas availableSchemas = stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id);
+            logger.debug("{}: Schemas exposed by ietf-netconf-monitoring: {}", id, availableSchemas.getAvailableYangSchemasQNames());
+
+            final Set<SourceIdentifier> providedSources = Sets.newHashSet(Collections2.transform(
+                    availableSchemas.getAvailableYangSchemasQNames(), QNAME_TO_SOURCE_ID_FUNCTION));
+
+            final Set<SourceIdentifier> requiredSourcesNotProvided = Sets.difference(requiredSources, providedSources);
+
+            if (!requiredSourcesNotProvided.isEmpty()) {
+                logger.warn("{}: Netconf device does not provide all yang models reported in hello message capabilities, required but not provided: {}",
+                        id, requiredSourcesNotProvided);
+                logger.warn("{}: Attempting to build schema context from required sources", id);
             }
-        }
 
-        /**
-         * Forward all cached notifications and pass all notifications from this point directly to sal facade.
-         */
-        synchronized void onRemoteSchemaUp() {
-            passNotifications = true;
 
-            for (final NetconfMessage cachedNotification : cache) {
-                passNotification(messageTransformer.toNotification(cachedNotification));
+            // TODO should we perform this ? We have a mechanism to fix initialization of devices not reporting or required modules in hello
+            // That is overriding capabilities in configuration using attribute yang-module-capabilities
+            // This is more user friendly even though it clashes with attribute yang-module-capabilities
+            // Some devices do not report all required models in hello message, but provide them
+            final Set<SourceIdentifier> providedSourcesNotRequired = Sets.difference(providedSources, requiredSources);
+            if (!providedSourcesNotRequired.isEmpty()) {
+                logger.warn("{}: Netconf device provides additional yang models not reported in hello message capabilities: {}",
+                        id, providedSourcesNotRequired);
+                logger.warn("{}: Adding provided but not required sources as required to prevent failures", id);
+                requiredSources.addAll(providedSourcesNotRequired);
             }
 
-            cache.clear();
+            return new DeviceSources(requiredSources, providedSources);
         }
+    }
 
-        private void cacheNotification(final NetconfMessage notification) {
-            Preconditions.checkState(passNotifications == false);
+    /**
+     * Contains RequiredSources - sources from capabilities.
+     *
+     */
+    private static final class DeviceSources {
+        private final Collection<SourceIdentifier> requiredSources;
+        private final Collection<SourceIdentifier> providedSources;
 
-            logger.debug("{}: Caching notification {}, remote schema not yet fully built", id, notification);
-            if(logger.isTraceEnabled()) {
-                logger.trace("{}: Caching notification {}", id, XmlUtil.toString(notification.getDocument()));
-            }
+        public DeviceSources(final Collection<SourceIdentifier> requiredSources, final Collection<SourceIdentifier> providedSources) {
+            this.requiredSources = requiredSources;
+            this.providedSources = providedSources;
+        }
 
-            cache.add(notification);
+        public Collection<SourceIdentifier> getRequiredSources() {
+            return requiredSources;
         }
 
-        private void passNotification(final CompositeNode parsedNotification) {
-            logger.debug("{}: Forwarding notification {}", id, parsedNotification);
-            Preconditions.checkNotNull(parsedNotification);
-            salFacade.onNotification(parsedNotification);
+        public Collection<SourceIdentifier> getProvidedSources() {
+            return providedSources;
         }
+
     }
 
+    /**
+     * Schema builder that tries to build schema context from provided sources or biggest subset of it.
+     */
+    private final class RecursiveSchemaSetup implements Runnable {
+        private final DeviceSources deviceSources;
+        private final NetconfSessionCapabilities remoteSessionCapabilities;
+        private final NetconfDeviceRpc deviceRpc;
+        private final RemoteDeviceCommunicator<NetconfMessage> listener;
+
+        public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionCapabilities remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc, final RemoteDeviceCommunicator<NetconfMessage> listener) {
+            this.deviceSources = deviceSources;
+            this.remoteSessionCapabilities = remoteSessionCapabilities;
+            this.deviceRpc = deviceRpc;
+            this.listener = listener;
+        }
+
+        @Override
+        public void run() {
+            setUpSchema(deviceSources.getRequiredSources());
+        }
+
+        /**
+         * Recursively build schema context, in case of success or final failure notify device
+         */
+        private void setUpSchema(final Collection<SourceIdentifier> requiredSources) {
+            logger.trace("{}: Trying to build schema context from {}", id, requiredSources);
+
+            // If no more sources, fail
+            if(requiredSources.isEmpty()) {
+                handleSalInitializationFailure(new IllegalStateException(id + ": No more sources for schema context"), listener);
+                return;
+            }
+
+            final CheckedFuture<SchemaContext, SchemaResolutionException> schemaBuilderFuture = schemaContextFactory.createSchemaContext(requiredSources);
+
+            final FutureCallback<SchemaContext> RecursiveSchemaBuilderCallback = new FutureCallback<SchemaContext>() {
+
+                @Override
+                public void onSuccess(final SchemaContext result) {
+                    logger.debug("{}: Schema context built successfully from {}", id, requiredSources);
+                    handleSalInitializationSuccess(result, remoteSessionCapabilities, deviceRpc);
+                }
+
+                @Override
+                public void onFailure(final Throwable t) {
+                    // In case source missing, try without it
+                    if (t instanceof MissingSchemaSourceException) {
+                        final SourceIdentifier missingSource = ((MissingSchemaSourceException) t).getSourceId();
+                        logger.warn("{}: Unable to build schema context, missing source {}, will reattempt without it", id, missingSource);
+                        setUpSchema(stripMissingSource(requiredSources, missingSource));
+
+                    // In case resolution error, try only with resolved sources
+                    } else if (t instanceof SchemaResolutionException) {
+                        // TODO check for infinite loop
+                        final SchemaResolutionException resolutionException = (SchemaResolutionException) t;
+                        logger.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports());
+                        setUpSchema(resolutionException.getResolvedSources());
+                    // unknown error, fail
+                    } else {
+                        handleSalInitializationFailure(t, listener);
+                    }
+                }
+            };
+
+            Futures.addCallback(schemaBuilderFuture, RecursiveSchemaBuilderCallback);
+        }
+
+        private Collection<SourceIdentifier> stripMissingSource(final Collection<SourceIdentifier> requiredSources, final SourceIdentifier sIdToRemove) {
+            final LinkedList<SourceIdentifier> sourceIdentifiers = Lists.newLinkedList(requiredSources);
+            final boolean removed = sourceIdentifiers.remove(sIdToRemove);
+            Preconditions.checkState(removed, "{}: Trying to remove {} from {} failed", id, sIdToRemove, requiredSources);
+            return sourceIdentifiers;
+        }
+    }
 }
index b5400347e7961c1a7c9b4bd621a47b6e7ff495e7..77e342641e060b67892f47f3083dbc4bf874ae66 100644 (file)
@@ -93,7 +93,7 @@ public final class NetconfStateSchemas {
      */
     private static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionCapabilities remoteSessionCapabilities, final RemoteDeviceId id) {
         if(remoteSessionCapabilities.isMonitoringSupported() == false) {
-            logger.warn("{}: Netconf monitoring not supported on device, cannot detect available schemas");
+            logger.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas");
             return EMPTY;
         }
 
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java
new file mode 100644 (file)
index 0000000..cc8960f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.netconf;
+
+import com.google.common.base.Preconditions;
+import java.util.LinkedList;
+import java.util.List;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.sal.connect.api.MessageTransformer;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handles incoming notifications. Either caches them(until onRemoteSchemaUp is called) or passes to sal Facade.
+ */
+final class NotificationHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(NotificationHandler.class);
+
+    private final RemoteDeviceHandler<?> salFacade;
+    private final List<NetconfMessage> queue = new LinkedList<>();
+    private final MessageTransformer<NetconfMessage> messageTransformer;
+    private final RemoteDeviceId id;
+    private boolean passNotifications = false;
+
+    NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> messageTransformer, final RemoteDeviceId id) {
+        this.salFacade = Preconditions.checkNotNull(salFacade);
+        this.messageTransformer = Preconditions.checkNotNull(messageTransformer);
+        this.id = Preconditions.checkNotNull(id);
+    }
+
+    synchronized void handleNotification(final NetconfMessage notification) {
+        if(passNotifications) {
+            passNotification(messageTransformer.toNotification(notification));
+        } else {
+            queueNotification(notification);
+        }
+    }
+
+    /**
+     * Forward all cached notifications and pass all notifications from this point directly to sal facade.
+     */
+    synchronized void onRemoteSchemaUp() {
+        passNotifications = true;
+
+        for (final NetconfMessage cachedNotification : queue) {
+            passNotification(messageTransformer.toNotification(cachedNotification));
+        }
+
+        queue.clear();
+    }
+
+    private void queueNotification(final NetconfMessage notification) {
+        Preconditions.checkState(passNotifications == false);
+
+        logger.debug("{}: Caching notification {}, remote schema not yet fully built", id, notification);
+        if(logger.isTraceEnabled()) {
+            logger.trace("{}: Caching notification {}", id, XmlUtil.toString(notification.getDocument()));
+        }
+
+        queue.add(notification);
+    }
+
+    private void passNotification(final CompositeNode parsedNotification) {
+        logger.debug("{}: Forwarding notification {}", id, parsedNotification);
+        Preconditions.checkNotNull(parsedNotification);
+        salFacade.onNotification(parsedNotification);
+    }
+}
index 2f24adcdbed9eee793b5833b04d108dafada12d6..aadb911f453a0613ed6b5470e5ee8cd7cba6e7cf 100644 (file)
@@ -51,8 +51,10 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
     private final RemoteDeviceId id;
     private final Lock sessionLock = new ReentrantLock();
 
+    // TODO implement concurrent message limit
     private final Queue<Request> requests = new ArrayDeque<>();
     private NetconfClientSession session;
+    private Future<?> initFuture;
 
     public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice,
             final NetconfSessionCapabilities netconfSessionCapabilities) {
@@ -97,9 +99,9 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
     public void initializeRemoteConnection(final NetconfClientDispatcher dispatch,
                                            final NetconfClientConfiguration config) {
         if(config instanceof NetconfReconnectingClientConfiguration) {
-            dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
+            initFuture = dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
         } else {
-            dispatch.createClient(config);
+            initFuture = dispatch.createClient(config);
         }
     }
 
@@ -172,7 +174,15 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
 
     @Override
     public void close() {
-        tearDown( String.format( "The netconf session to %1$s has been closed", id.getName() ) );
+        // Cancel reconnect if in progress
+        if(initFuture != null) {
+            initFuture.cancel(false);
+        }
+        // Disconnect from device
+        if(session != null) {
+            session.close();
+        }
+        tearDown(id + ": Netconf session closed");
     }
 
     @Override
@@ -191,12 +201,12 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
     private void processMessage(final NetconfMessage message) {
         Request request = null;
         sessionLock.lock();
+
         try {
             request = requests.peek();
-            if (request.future.isUncancellable()) {
+            if (request != null && request.future.isUncancellable()) {
                 requests.poll();
-            }
-            else {
+            } else {
                 request = null;
                 logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message));
             }
index dbef290197b91ea2e7eb55e19fbb9363cadace16..3cc513600dd709484f464fbae0530c7df67b4f29 100644 (file)
@@ -7,11 +7,12 @@
  */
 package org.opendaylight.controller.sal.connect.netconf.sal;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
-
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
@@ -30,14 +31,10 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
 public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDeviceHandler<NetconfSessionCapabilities> {
 
     private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceSalFacade.class);
@@ -64,11 +61,9 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
     }
 
     @Override
-    public synchronized void onDeviceConnected(final SchemaContextProvider remoteSchemaContextProvider,
+    public synchronized void onDeviceConnected(final SchemaContext schemaContext,
                                                final NetconfSessionCapabilities netconfSessionPreferences, final RpcImplementation deviceRpc) {
-        final SchemaContext schemaContext = remoteSchemaContextProvider.getSchemaContext();
 
-        // TODO remove deprecated SchemaContextProvider from SchemaAwareRpcBroker
         // TODO move SchemaAwareRpcBroker from sal-broker-impl, now we have depend on the whole sal-broker-impl
         final RpcProvisionRegistry rpcRegistry = new SchemaAwareRpcBroker(id.getPath().toString(), new org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider() {
             @Override
index 70d4f4336558d8d5897df9db297154f62d76cf09..6c46bed7626f27fd86d1f4b11c9462bbda395611 100644 (file)
@@ -57,7 +57,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
         final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
                 NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path)));
 
-        ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
             @Override
             public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
                 checkReadSuccess(result, path);
@@ -99,7 +99,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
             final YangInstanceIdentifier path) {
         final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(path)));
 
-        ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
             @Override
             public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
                 checkReadSuccess(result, path);
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java
deleted file mode 100644 (file)
index e7d6464..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.connect.netconf.schema;
-
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory;
-import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-
-public final class NetconfDeviceSchemaProviderFactory implements SchemaContextProviderFactory {
-
-    private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSchemaProviderFactory.class);
-
-    private final RemoteDeviceId id;
-
-    public NetconfDeviceSchemaProviderFactory(final RemoteDeviceId id) {
-        this.id = id;
-    }
-
-    @Override
-    public SchemaContextProvider createContextProvider(final Collection<QName> capabilities, final SchemaSourceProvider<InputStream> sourceProvider) {
-
-        final YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider);
-
-        if (sourceContext.getMissingSources().isEmpty() == false) {
-            logger.warn("{}: Sources for following models are missing {}", id, sourceContext.getMissingSources());
-        }
-
-        logger.debug("{}: Trying to create schema context from {}", id, sourceContext.getValidSources());
-        final List<InputStream> modelsToParse = YangSourceContext.getValidInputStreams(sourceContext);
-
-        Preconditions.checkState(sourceContext.getValidSources().isEmpty() == false,
-                "%s: Unable to create schema context, no sources provided by device", id);
-        try {
-            final SchemaContext schemaContext = tryToParseContext(modelsToParse);
-            logger.debug("{}: Schema context successfully created.", id);
-            return new NetconfSchemaContextProvider(schemaContext);
-        } catch (final RuntimeException e) {
-            logger.error("{}: Unable to create schema context, unexpected error", id, e);
-            throw new IllegalStateException(id + ": Unable to create schema context", e);
-        }
-    }
-
-    private static SchemaContext tryToParseContext(final List<InputStream> modelsToParse) {
-        final YangParserImpl parser = new YangParserImpl();
-        final Set<Module> models = parser.parseYangModelsFromStreams(modelsToParse);
-        return parser.resolveSchemaContext(models);
-    }
-
-    private static final class NetconfSchemaContextProvider implements SchemaContextProvider {
-        private final SchemaContext schemaContext;
-
-        public NetconfSchemaContextProvider(final SchemaContext schemaContext) {
-            this.schemaContext = schemaContext;
-        }
-
-        @Override
-        public SchemaContext getSchemaContext() {
-            return schemaContext;
-        }
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java
deleted file mode 100644 (file)
index 44ff2ef..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.connect.netconf.schema;
-
-import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
-import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
-public final class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider<String> {
-
-    public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING,
-            "get-schema");
-    public static final QName GET_DATA_QNAME = QName
-            .create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data");
-
-    private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaSourceProvider.class);
-
-    private final RpcImplementation rpc;
-    private final RemoteDeviceId id;
-
-    public NetconfRemoteSchemaSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) {
-        this.id = id;
-        this.rpc = Preconditions.checkNotNull(rpc);
-    }
-
-    @Override
-    public Optional<String> getSchemaSource(final String moduleName, final Optional<String> revision) {
-        final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision);
-
-        logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision);
-        try {
-            final RpcResult<CompositeNode> schemaReply = rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest).get();
-            if (schemaReply.isSuccessful()) {
-                final Optional<String> schemaBody = getSchemaFromRpc(id, schemaReply.getResult());
-                if (schemaBody.isPresent()) {
-                    logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision);
-                    return schemaBody;
-                }
-            } else {
-                logger.warn("{}: YANG schema was not successfully retrieved for {}:{}. Errors: {}", id, moduleName,
-                        revision, schemaReply.getErrors());
-            }
-            return Optional.absent();
-        } catch (final InterruptedException e){
-            Thread.currentThread().interrupt();
-            throw new IllegalStateException(e);
-        } catch (final Exception e) {
-            logger.error("{}: YANG schema was not successfully retrieved for {}:{}", id, moduleName, revision, e);
-            throw new IllegalStateException(e);
-        }
-    }
-
-    private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional<String> revision) {
-        final CompositeNodeBuilder<ImmutableCompositeNode> request = ImmutableCompositeNode.builder();
-        request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName);
-        if (revision.isPresent()) {
-            request.addLeaf("version", revision.get());
-        }
-        request.addLeaf("format", "yang");
-        return request.toInstance();
-    }
-
-    private static Optional<String> getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) {
-        if (result == null) {
-            return Optional.absent();
-        }
-        final SimpleNode<?> simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision());
-
-        Preconditions.checkNotNull(simpleNode,
-                "%s Unexpected response to get-schema, expected response with one child %s, but was %s",
-                id, GET_DATA_QNAME.withoutRevision(), result);
-
-        final Object potential = simpleNode.getValue();
-        return potential instanceof String ? Optional.of((String) potential) : Optional.<String>absent();
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java
new file mode 100644 (file)
index 0000000..dc90fd3
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.netconf.schema;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.ExecutionException;
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+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.SchemaSourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
+
+    public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING,"get-schema");
+    public static final QName GET_DATA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data");
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaYangSourceProvider.class);
+
+    private static final ExceptionMapper<SchemaSourceException> MAPPER = new ExceptionMapper<SchemaSourceException>(
+            "schemaDownload", SchemaSourceException.class) {
+        @Override
+        protected SchemaSourceException newWithCause(final String s, final Throwable throwable) {
+            return new SchemaSourceException(s, throwable);
+        }
+    };
+
+    private final RpcImplementation rpc;
+    private final RemoteDeviceId id;
+
+    public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) {
+        this.id = id;
+        this.rpc = Preconditions.checkNotNull(rpc);
+    }
+
+    private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional<String> revision) {
+        final CompositeNodeBuilder<ImmutableCompositeNode> request = ImmutableCompositeNode.builder();
+        request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName);
+        if (revision.isPresent()) {
+            request.addLeaf("version", revision.get());
+        }
+        request.addLeaf("format", "yang");
+        return request.toInstance();
+    }
+
+    private static Optional<String> getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) {
+        if (result == null) {
+            return Optional.absent();
+        }
+        final SimpleNode<?> simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision());
+
+        Preconditions.checkNotNull(simpleNode,
+                "%s Unexpected response to get-schema, expected response with one child %s, but was %s", id,
+                GET_DATA_QNAME.withoutRevision(), result);
+
+        final Object potential = simpleNode.getValue();
+        return potential instanceof String ? Optional.of((String) potential) : Optional.<String> absent();
+    }
+
+    @Override
+    public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final String moduleName = sourceIdentifier.getName();
+
+        // If formatted revision is SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION, we have to omit it from request
+        final String formattedRevision = sourceIdentifier.getRevision().equals(SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION) ? null : sourceIdentifier.getRevision();
+        final Optional<String> revision = Optional.fromNullable(formattedRevision);
+        final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision);
+
+        logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision);
+
+        final ListenableFuture<YangTextSchemaSource> transformed = Futures.transform(
+                rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest),
+                new ResultToYangSourceTransformer(id, sourceIdentifier, moduleName, revision));
+
+        // FIXME remove this get, it is only present to wait until source is retrieved
+        // (goal is to limit concurrent schema download, since NetconfDevice listener does not handle concurrent messages properly)
+        try {
+            logger.trace("{}: Blocking for {}", id, sourceIdentifier);
+            transformed.get();
+        } catch (final InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (final ExecutionException e) {
+           throw new IllegalStateException(id + ": Failed while getting source: " + sourceIdentifier, e);
+        }
+
+        return Futures.makeChecked(transformed, MAPPER);
+    }
+
+    /**
+     * Transform composite node to string schema representation and then to ASTSchemaSource
+     */
+    private static final class ResultToYangSourceTransformer implements
+            Function<RpcResult<CompositeNode>, YangTextSchemaSource> {
+
+        private final RemoteDeviceId id;
+        private final SourceIdentifier sourceIdentifier;
+        private final String moduleName;
+        private final Optional<String> revision;
+
+        public ResultToYangSourceTransformer(final RemoteDeviceId id, final SourceIdentifier sourceIdentifier,
+                final String moduleName, final Optional<String> revision) {
+            this.id = id;
+            this.sourceIdentifier = sourceIdentifier;
+            this.moduleName = moduleName;
+            this.revision = revision;
+        }
+
+        @Override
+        public YangTextSchemaSource apply(final RpcResult<CompositeNode> input) {
+
+            if (input.isSuccessful()) {
+
+                final Optional<String> schemaString = getSchemaFromRpc(id, input.getResult());
+
+                Preconditions.checkState(schemaString.isPresent(),
+                        "%s: Unexpected response to get-schema, schema not present in message for: %s", id, sourceIdentifier);
+
+                logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision);
+
+                return new NetconfYangTextSchemaSource(id, sourceIdentifier, schemaString);
+            }
+
+            logger.warn("{}: YANG schema was not successfully retrieved for {}. Errors: {}", id, sourceIdentifier,
+                    input.getErrors());
+
+            throw new IllegalStateException(String.format(
+                    "%s: YANG schema was not successfully retrieved for %s. Errors: %s", id, sourceIdentifier,
+                    input.getErrors()));
+
+        }
+
+    }
+
+    private static class NetconfYangTextSchemaSource extends YangTextSchemaSource {
+        private final RemoteDeviceId id;
+        private final Optional<String> schemaString;
+
+        public NetconfYangTextSchemaSource(final RemoteDeviceId id, final SourceIdentifier sId, final Optional<String> schemaString) {
+            super(sId);
+            this.id = id;
+            this.schemaString = schemaString;
+        }
+
+        @Override
+        protected Objects.ToStringHelper addToStringAttributes(final Objects.ToStringHelper toStringHelper) {
+            return toStringHelper.add("device", id);
+        }
+
+        @Override
+        public InputStream openStream() throws IOException {
+            return IOUtils.toInputStream(schemaString.get());
+        }
+    }
+}
index 1e3cf4b6fce9f30917ef4c3bc3c19a61bf9b7d64..893a45aaa2df41eadc71a1a62215493fda3d4a04 100644 (file)
@@ -49,6 +49,8 @@ import org.w3c.dom.Element;
 
 public class NetconfMessageTransformUtil {
 
+    public static final String MESSAGE_ID_ATTR = "message-id";
+
     private NetconfMessageTransformUtil() {}
 
     public static final QName IETF_NETCONF_MONITORING = QName.create(NetconfState.QNAME, "ietf-netconf-monitoring");
@@ -125,8 +127,8 @@ public class NetconfMessageTransformUtil {
 
     public static void checkValidReply(final NetconfMessage input, final NetconfMessage output)
             throws NetconfDocumentedException {
-        final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
-        final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
+        final String inputMsgId = input.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR);
+        final String outputMsgId = output.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR);
 
         if(inputMsgId.equals(outputMsgId) == false) {
             Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
index fa488dadd3efb4afefc48327b683a44f296727dc..218ec0be8d319b91abdb8a1054c93f09675dbc68 100644 (file)
@@ -8,14 +8,18 @@
 package org.opendaylight.controller.sal.connect.netconf;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyCollectionOf;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.Futures;
 import java.io.InputStream;
@@ -28,12 +32,13 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
-import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory;
 import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
@@ -45,8 +50,15 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+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.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
@@ -70,7 +82,13 @@ public class NetconfDeviceTest {
     public static final String TEST_NAMESPACE = "test:namespace";
     public static final String TEST_MODULE = "test-module";
     public static final String TEST_REVISION = "2013-07-22";
-    private NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() {
+    public static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, Optional.of(TEST_REVISION));
+    public static final String TEST_CAPABILITY = TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION;
+
+    public static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", Optional.of(TEST_REVISION));
+    public static final String TEST_CAPABILITY2 = TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&amp;revision=" + TEST_REVISION;
+
+    private static final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() {
 
         @Override
         public NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionCapabilities remoteSessionCapabilities, final RemoteDeviceId id) {
@@ -79,14 +97,71 @@ public class NetconfDeviceTest {
     };
 
     @Test
-    public void testNetconfDeviceWithoutMonitoring() throws Exception {
+    public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
+        final ArrayList<String> capList = Lists.newArrayList(TEST_CAPABILITY);
+
         final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
         final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
 
-        final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), getMessageTransformer(), getSchemaContextProviderFactory(), getSourceProviderFactory(), stateSchemasResolver);
-        device.onRemoteSessionUp(getSessionCaps(false, Collections.<String>emptyList()), listener);
+        final SchemaContextFactory schemaFactory = getSchemaFactory();
+
+        // Make fallback attempt to fail due to empty resolved sources
+        final SchemaResolutionException schemaResolutionException
+                = new SchemaResolutionException("fail first",
+                Collections.<SourceIdentifier>emptyList(), HashMultimap.<SourceIdentifier, ModuleImport>create());
+        doReturn(Futures.immediateFailedCheckedFuture(
+                schemaResolutionException))
+                .when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
+
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
+                = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer());
+        // Monitoring not supported
+        final NetconfSessionCapabilities sessionCaps = getSessionCaps(false, capList);
+        device.onRemoteSessionUp(sessionCaps, listener);
 
         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected();
+        Mockito.verify(listener, Mockito.timeout(5000)).close();
+        Mockito.verify(schemaFactory, times(1)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
+    }
+
+    @Test
+    public void testNetconfDeviceMissingSource() throws Exception {
+        final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
+        final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+
+        final SchemaContextFactory schemaFactory = getSchemaFactory();
+
+        // Make fallback attempt to fail due to empty resolved sources
+        final MissingSchemaSourceException schemaResolutionException = new MissingSchemaSourceException("fail first", TEST_SID);
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(final InvocationOnMock invocation) throws Throwable {
+                if(((Collection<?>) invocation.getArguments()[0]).size() == 2) {
+                    return Futures.immediateFailedCheckedFuture(schemaResolutionException);
+                } else {
+                    return Futures.immediateCheckedFuture(getSchema());
+                }
+            }
+        }).when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
+
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
+                = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer());
+        // Monitoring supported
+        final NetconfSessionCapabilities sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
+        device.onRemoteSessionUp(sessionCaps, listener);
+
+        Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+        Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
+    }
+
+    private SchemaSourceRegistry getSchemaRegistry() {
+        final SchemaSourceRegistry mock = mock(SchemaSourceRegistry.class);
+        final SchemaSourceRegistration mockReg = mock(SchemaSourceRegistration.class);
+        doNothing().when(mockReg).close();
+        doReturn(mockReg).when(mock).registerSchemaSource(any(org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider.class), any(PotentialSchemaSource.class));
+        return mock;
     }
 
     @Test
@@ -95,7 +170,10 @@ public class NetconfDeviceTest {
         final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
 
         final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
-        final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, getSchemaContextProviderFactory(), getSourceProviderFactory(), stateSchemasResolver);
+
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
+                = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer);
 
         device.onNotification(netconfMessage);
         device.onNotification(netconfMessage);
@@ -103,7 +181,7 @@ public class NetconfDeviceTest {
         verify(facade, times(0)).onNotification(any(CompositeNode.class));
 
         final NetconfSessionCapabilities sessionCaps = getSessionCaps(true,
-                Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
+                Lists.newArrayList(TEST_CAPABILITY));
 
         device.onRemoteSessionUp(sessionCaps, listener);
 
@@ -120,40 +198,34 @@ public class NetconfDeviceTest {
         final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
         final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
 
-        final SchemaContextProviderFactory schemaContextProviderFactory = getSchemaContextProviderFactory();
-        final SchemaSourceProviderFactory<InputStream> sourceProviderFactory = getSourceProviderFactory();
+        final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
         final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
 
-        final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, schemaContextProviderFactory, sourceProviderFactory, stateSchemasResolver);
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
+                = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer);
         final NetconfSessionCapabilities sessionCaps = getSessionCaps(true,
                 Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
         device.onRemoteSessionUp(sessionCaps, listener);
 
-        verify(sourceProviderFactory, timeout(5000)).createSourceProvider(any(RpcImplementation.class));
-        verify(schemaContextProviderFactory, timeout(5000)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class));
+        verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
         verify(messageTransformer, timeout(5000)).onGlobalContextUpdated(any(SchemaContext.class));
-        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
 
         device.onRemoteSessionDown();
         verify(facade, timeout(5000)).onDeviceDisconnected();
 
         device.onRemoteSessionUp(sessionCaps, listener);
 
-        verify(sourceProviderFactory, timeout(5000).times(2)).createSourceProvider(any(RpcImplementation.class));
-        verify(schemaContextProviderFactory, timeout(5000).times(2)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class));
+        verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
         verify(messageTransformer, timeout(5000).times(2)).onGlobalContextUpdated(any(SchemaContext.class));
-        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
     }
 
-    private SchemaContextProviderFactory getSchemaContextProviderFactory() {
-        final SchemaContextProviderFactory schemaContextProviderFactory = mockClass(SchemaContextProviderFactory.class);
-        doReturn(new SchemaContextProvider() {
-            @Override
-            public SchemaContext getSchemaContext() {
-                return getSchema();
-            }
-        }).when(schemaContextProviderFactory).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class));
-        return schemaContextProviderFactory;
+    private SchemaContextFactory getSchemaFactory() {
+        final SchemaContextFactory schemaFactory = mockClass(SchemaContextFactory.class);
+        doReturn(Futures.immediateCheckedFuture(getSchema())).when(schemaFactory).createSchemaContext(any(Collection.class));
+        return schemaFactory;
     }
 
     public static SchemaContext getSchema() {
@@ -167,7 +239,7 @@ public class NetconfDeviceTest {
 
     private RemoteDeviceHandler<NetconfSessionCapabilities> getFacade() throws Exception {
         final RemoteDeviceHandler<NetconfSessionCapabilities> remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
-        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
         doNothing().when(remoteDeviceHandler).onNotification(any(CompositeNode.class));
         return remoteDeviceHandler;
@@ -190,7 +262,7 @@ public class NetconfDeviceTest {
     }
 
     private static <T> T mockClass(final Class<T> remoteDeviceHandlerClass) {
-        final T mock = Mockito.mock(remoteDeviceHandlerClass);
+        final T mock = mock(remoteDeviceHandlerClass);
         Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString();
         return mock;
     }
index bd092bc5bd6fb933edf165ad7d9757f056a2c69f..d5c9dc6fc79f627c4dccac89858ca694313b2fcc 100644 (file)
@@ -17,7 +17,7 @@ import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Implementation of RemoteDeviceHandler. Integrates cli with
@@ -41,7 +41,7 @@ public class NetconfDeviceConnectionHandler implements RemoteDeviceHandler<Netco
     }
 
     @Override
-    public synchronized void onDeviceConnected(final SchemaContextProvider contextProvider,
+    public synchronized void onDeviceConnected(final SchemaContext context,
             final NetconfSessionCapabilities capabilities, final RpcImplementation rpcImplementation) {
         console.enterRootContext(new ConsoleContext() {
 
@@ -60,8 +60,8 @@ public class NetconfDeviceConnectionHandler implements RemoteDeviceHandler<Netco
         // possible
         // TODO detect netconf base version
         // TODO detect inet types version
-        commandDispatcher.addRemoteCommands(rpcImplementation, contextProvider.getSchemaContext());
-        schemaContextRegistry.setRemoteSchemaContext(contextProvider.getSchemaContext());
+        commandDispatcher.addRemoteCommands(rpcImplementation, context);
+        schemaContextRegistry.setRemoteSchemaContext(context);
         up = true;
         this.notify();
     }
index c72705d50ebd8f505feaac092331fcbafc1ee41b..e55ec697ba560b528353fc8581c406ff58ea4ffd 100644 (file)
@@ -19,7 +19,8 @@
 
   <modules>
     <module>netconf-api</module>
-    <module>netconf-cli</module>
+      <!--FIXME make compilable-->
+    <!--<module>netconf-cli</module>-->
     <module>netconf-config</module>
     <module>netconf-impl</module>
     <module>config-netconf-connector</module>