Merge "Bug 4577 Allow specification of a distinct schema cache directory per netconf...
[netconf.git] / opendaylight / netconf / netconf-topology / src / main / java / org / opendaylight / netconf / topology / AbstractNetconfTopology.java
index 88ed13ca6de1dc3689db3afc70621d6cf3da84cd..de0dc6921577e3356c57cc7550494d409b93e9ae 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.netconf.topology;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -19,8 +20,7 @@ import java.net.InetSocketAddress;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.Map;
 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
 import org.opendaylight.controller.config.threadpool.ThreadPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -32,6 +32,7 @@ import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
 import org.opendaylight.controller.sal.core.api.Provider;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
@@ -74,32 +75,91 @@ import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class AbstractNetconfTopology implements NetconfTopology, BindingAwareProvider, Provider{
+public abstract class AbstractNetconfTopology implements NetconfTopology, BindingAwareProvider, Provider {
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
 
-    private static final long DEFAULT_REQUEST_TIMEOUT_MILIS = 60000L;
-    private static final int DEFAULT_KEEPALIVE_DELAY = 0;
-    private static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
+    protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
+    protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
+    protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
     private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
     private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
+    private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
     private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
 
-    private static FilesystemSchemaSourceCache<YangTextSchemaSource> CACHE = null;
-    //keep track of already initialized repositories to avoid adding redundant listeners
-    private static final Set<SchemaRepository> INITIALIZED_SCHEMA_REPOSITORIES = new HashSet<>();
+    // constants related to Schema Cache(s)
+    /**
+     * Filesystem based caches are stored relative to the cache directory.
+     */
+    private static final String CACHE_DIRECTORY = "cache";
+
+    /**
+     * The default cache directory relative to <code>CACHE_DIRECTORY</code>
+     */
+    private static final String DEFAULT_CACHE_DIRECTORY = "schema";
+
+    /**
+     * The qualified schema cache directory <code>cache/schema</code>
+     */
+    private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY = CACHE_DIRECTORY + File.separator+ DEFAULT_CACHE_DIRECTORY;
+
+    /**
+     * The name for the default schema repository
+     */
+    private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
+
+    /**
+     * The default schema repository in the case that one is not specified.
+     */
+    private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
+            new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
+
+    /**
+     * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
+     */
+    private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
+            new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
+                    new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
+
+    /**
+     * The default factory for creating <code>SchemaContext</code> instances.
+     */
+    private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
+            DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+
+    /**
+     * Keeps track of initialized Schema resources.  A Map is maintained in which the key represents the name
+     * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>.  The
+     * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
+     * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
+     * Netconf mount.  Access to <code>schemaResourcesDTOs</code> should be surrounded by appropriate
+     * synchronization locks.
+     */
+    private static volatile Map<String, NetconfDevice.SchemaResourcesDTO> schemaResourcesDTOs = new HashMap<>();
+
+    // Initializes default constant instances for the case when the default schema repository
+    // directory cache/schema is used.
+    static {
+        schemaResourcesDTOs.put(DEFAULT_CACHE_DIRECTORY,
+                new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY,
+                        DEFAULT_SCHEMA_CONTEXT_FACTORY,
+                        new NetconfStateSchemas.NetconfStateSchemasResolverImpl()));
+        DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
+        DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
+                TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
+    }
 
     protected final String topologyId;
     private final NetconfClientDispatcher clientDispatcher;
     protected final BindingAwareBroker bindingAwareBroker;
-    private final Broker domBroker;
+    protected final Broker domBroker;
     private final EventExecutor eventExecutor;
-    private final ScheduledThreadPool keepaliveExecutor;
-    private final ThreadPool processingExecutor;
-    private final SharedSchemaRepository sharedSchemaRepository;
+    protected final ScheduledThreadPool keepaliveExecutor;
+    protected final ThreadPool processingExecutor;
+    protected final SharedSchemaRepository sharedSchemaRepository;
 
-    private SchemaSourceRegistry schemaRegistry = null;
-    private SchemaContextFactory schemaContextFactory = null;
+    protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
+    protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
 
     protected DOMMountPointService mountPointService = null;
     protected DataBroker dataBroker = null;
@@ -117,8 +177,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
-
-        initFilesystemSchemaSourceCache(sharedSchemaRepository);
     }
 
     protected void registerToSal(BindingAwareProvider baProvider, Provider provider) {
@@ -126,20 +184,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         bindingAwareBroker.registerProvider(baProvider);
     }
 
-    private void initFilesystemSchemaSourceCache(SharedSchemaRepository repository) {
-        LOG.warn("Schema repository used: {}", repository.getIdentifier());
-        if (CACHE == null) {
-            CACHE = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema"));
-        }
-        if (!INITIALIZED_SCHEMA_REPOSITORIES.contains(repository)) {
-            repository.registerSchemaSourceListener(CACHE);
-            repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
-            INITIALIZED_SCHEMA_REPOSITORIES.add(repository);
-        }
-        setSchemaRegistry(repository);
-        setSchemaContextFactory(repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT));
-    }
-
     public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
         this.schemaRegistry = schemaRegistry;
     }
@@ -181,7 +225,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         return Futures.immediateFuture(null);
     }
 
-    private ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
+    protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
                                                                         final Node configNode) {
         final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
 
@@ -191,8 +235,10 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
 
         final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
         final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
-        final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(deviceCommunicator, netconfNode);
+        final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
+        final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(netconfClientSessionListener, netconfNode);
         final ListenableFuture<NetconfDeviceCapabilities> future = deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
+
         activeConnectors.put(nodeId, deviceCommunicatorDTO);
 
         Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
@@ -211,11 +257,11 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         return future;
     }
 
-    private NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
+    protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
                                                          final NetconfNode node) {
         //setup default values since default value is not supported yet in mdsal
         // TODO remove this when mdsal starts supporting default values
-        final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILIS : node.getDefaultRequestTimeoutMillis();
+        final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
         final Long keepaliveDelay = node.getKeepaliveDelay() == null ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
         final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
 
@@ -233,20 +279,93 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay);
         }
 
-        NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
-                new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
 
-        NetconfDevice device = new NetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
                 processingExecutor.getExecutor(), reconnectOnChangedSchema);
 
         return new NetconfConnectorDTO(new NetconfDeviceCommunicator(remoteDeviceId, device), salFacade);
     }
 
-    public NetconfReconnectingClientConfiguration getClientConfig(final NetconfDeviceCommunicator listener, NetconfNode node) {
+    protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
+        // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
+        NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
+        final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
+        // Only checks to ensure the String is not empty or null;  further checks related to directory accessibility and file permissions
+        // are handled during the FilesystemScehamSourceCache initialization.
+        if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
+            // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry and
+            // SchemaContextFactory remain the default values.
+            if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
+                // Multiple modules may be created at once;  synchronize to avoid issues with data consistency among threads.
+                synchronized(schemaResourcesDTOs) {
+                    // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if they already exist
+                    final NetconfDevice.SchemaResourcesDTO dto =
+                            schemaResourcesDTOs.get(moduleSchemaCacheDirectory);
+                    if (dto == null) {
+                        schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory, nodeId.getValue());
+                        schemaRegistry.registerSchemaSourceListener(
+                                TextToASTTransformer.create((SchemaRepository) schemaRegistry, schemaRegistry));
+                        schemaResourcesDTOs.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
+                    } else {
+                        setSchemaContextFactory(dto.getSchemaContextFactory());
+                        setSchemaRegistry(dto.getSchemaRegistry());
+                        schemaResourcesDTO = dto;
+                    }
+                }
+                LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
+                        nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
+            }
+        } else {
+            LOG.warn("schema-cache-directory for {} is null or empty;  using the default {}",
+                    nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
+        }
+
+        if (schemaResourcesDTO == null) {
+            schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory,
+                    new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        }
+
+        return schemaResourcesDTO;
+    }
+
+    /**
+     * Creates the backing Schema classes for a particular directory.
+     *
+     * @param moduleSchemaCacheDirectory The string directory relative to "cache"
+     * @return A DTO containing the Schema classes for the Netconf mount.
+     */
+    private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory,
+            final String instanceName) {
+
+        final SharedSchemaRepository repository = new SharedSchemaRepository(instanceName);
+        final SchemaContextFactory schemaContextFactory
+                = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+        setSchemaRegistry(repository);
+        setSchemaContextFactory(schemaContextFactory);
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
+                createDeviceFilesystemCache(moduleSchemaCacheDirectory);
+        repository.registerSchemaSourceListener(deviceCache);
+        return new NetconfDevice.SchemaResourcesDTO(repository, schemaContextFactory,
+                new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+    }
+
+    /**
+     * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
+     *
+     * @param schemaCacheDirectory The custom cache directory relative to "cache"
+     * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
+     */
+    private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(final String schemaCacheDirectory) {
+        final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
+        return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, new File(relativeSchemaCacheDirectory));
+    }
+
+    public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener, NetconfNode node) {
 
         //setup default values since default value is not supported yet in mdsal
         // TODO remove this when mdsal starts supporting default values
-        final long clientConnectionTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILIS : node.getDefaultRequestTimeoutMillis();
+        final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
         final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
         final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
         final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
@@ -357,12 +476,12 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         }
     }
 
-    protected static final class NetconfConnectorDTO {
+    protected static class NetconfConnectorDTO {
 
         private final NetconfDeviceCommunicator communicator;
         private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
 
-        private NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
+        public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
             this.communicator = communicator;
             this.facade = facade;
         }
@@ -374,6 +493,10 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
             return facade;
         }
+
+        public NetconfClientSessionListener getSessionListener() {
+            return communicator;
+        }
     }
 
 }