From 6c3f614edb18bdb8cc6e7b87627f240d97a258c3 Mon Sep 17 00:00:00 2001 From: Jan Srnicek Date: Thu, 13 Oct 2016 13:56:47 +0200 Subject: HONEYCOMB-207 : Configurable modules list for distributions Export list of modules for built distribution on compile time according to distribution.modules property to ***module-config.txt Load aggregated set of modules on start from all descriptors in /modules folder Change-Id: Icdeb23536aee3a243a221d3f2ec5f340d387764e Signed-off-by: Jan Srnicek --- .../infra/distro/ActiveModuleProvider.java | 154 +++++++++++++++++++++ .../java/io/fd/honeycomb/infra/distro/Main.java | 36 ++--- 2 files changed, 165 insertions(+), 25 deletions(-) create mode 100644 infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/ActiveModuleProvider.java (limited to 'infra/minimal-distribution/src/main/java/io/fd') diff --git a/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/ActiveModuleProvider.java b/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/ActiveModuleProvider.java new file mode 100644 index 000000000..03c56969d --- /dev/null +++ b/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/ActiveModuleProvider.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.infra.distro; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Module; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides list of active modules for distribution + */ +public class ActiveModuleProvider { + + public static final String STANDARD_MODULES_RELATIVE_PATH = "../modules/"; + private static final Logger LOG = LoggerFactory.getLogger(ActiveModuleProvider.class); + + /** + * Provide unique set of active modules filtered from provided resources + */ + public static Set loadActiveModules(@Nonnull final List moduleNames) { + final ClassLoader classLoader = ActiveModuleProvider.class.getClassLoader(); + LOG.info("Reading active modules configuration for distribution"); + + // process resources to resource modules + return moduleNames.stream() + .map(String::trim) + .filter(trimmedLine -> trimmedLine.length() != 0) + // filter out commented lines + .filter(nonEmptyLine -> !nonEmptyLine.startsWith("//")) + // filter duplicates + .distinct() + .map(validLine -> nameToClass(validLine, classLoader)) + // filters out classes that are not modules + .filter(ActiveModuleProvider::filterNonModules) + .map(ActiveModuleProvider::classToInstance) + .collect(Collectors.toSet()); + } + + /** + * Aggregate all resources from provided relative path into a {@code List} + */ + public static List aggregateResources(final String relativePath, final ClassLoader classLoader) { + try { + return Collections.list(classLoader.getResources(relativePath)).stream() + .map(ActiveModuleProvider::toURI) + .flatMap(ActiveModuleProvider::folderToFile) + .map(File::toURI) + .map(Paths::get) + // readAll lines and add them to iteration + .flatMap(ActiveModuleProvider::readLines) + .collect(Collectors.toList()); + } catch (IOException e) { + LOG.error("Unable to load resources from relative path {}", relativePath, e); + throw new IllegalStateException("Unable to load resources from relative path " + relativePath, e); + } + } + + private static Stream folderToFile(final URI uri) { + final File[] files = new File(uri).listFiles(File::isFile); + + return files != null + ? ImmutableList.copyOf(files).stream() + : Collections.emptyList().stream(); + } + + private static boolean filterNonModules(final Class clazz) { + final boolean isModule = Module.class.isAssignableFrom(clazz); + if (!isModule) { + LOG.warn("Class {} is provided in modules configuration, but is not a Module and will be ignored", clazz); + } + return isModule; + } + + /** + * Read lines from {@code Path} + */ + private static Stream readLines(final Path path) { + try { + return Files.readAllLines(path).stream(); + } catch (IOException e) { + LOG.error("Unable to read content of {}", path, e); + throw new IllegalStateException("Unable to read content of " + path, e); + } + } + + /** + * Converts {@code URL} to {@code URI} + */ + private static URI toURI(final URL url) { + try { + return url.toURI(); + } catch (URISyntaxException e) { + LOG.error("Unable to convert {} to uri", url); + throw new IllegalStateException("Unable to convert " + url + " to uri", e); + } + } + + /** + * Loads class by provided name + */ + private static Class nameToClass(final String name, + final ClassLoader classLoader) { + try { + LOG.info("Loading module class {}", name); + return classLoader.loadClass(name); + } catch (ClassNotFoundException e) { + LOG.error("Unable to convert {} to class, make sure you've provided sources to classpath", name); + throw new IllegalStateException( + "Unable to convert " + name + " to class, make sure you've provided sources to classpath", e); + } + } + + /** + * Creates instance of module class + */ + private static Module classToInstance(final Class moduleClass) { + try { + LOG.info("Creating instance for module {}", moduleClass); + return Module.class.cast(moduleClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + LOG.error("Unable to create instance for class {}", moduleClass, e); + throw new IllegalStateException("Unable to create instance for class" + moduleClass, e); + } + } +} diff --git a/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/Main.java b/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/Main.java index d3a562d3a..3c62382c2 100644 --- a/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/Main.java +++ b/infra/minimal-distribution/src/main/java/io/fd/honeycomb/infra/distro/Main.java @@ -16,8 +16,11 @@ package io.fd.honeycomb.infra.distro; +import static io.fd.honeycomb.infra.distro.ActiveModuleProvider.STANDARD_MODULES_RELATIVE_PATH; +import static io.fd.honeycomb.infra.distro.ActiveModuleProvider.aggregateResources; +import static io.fd.honeycomb.infra.distro.ActiveModuleProvider.loadActiveModules; + import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; import com.google.inject.ConfigurationException; import com.google.inject.CreationException; import com.google.inject.Guice; @@ -28,20 +31,14 @@ import com.google.inject.ProvisionException; import com.google.inject.name.Names; import io.fd.honeycomb.data.init.DataTreeInitializer; import io.fd.honeycomb.data.init.InitializerRegistry; -import io.fd.honeycomb.infra.distro.cfgattrs.CfgAttrsModule; import io.fd.honeycomb.infra.distro.cfgattrs.HoneycombConfiguration; -import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule; -import io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule; import io.fd.honeycomb.infra.distro.initializer.InitializerPipelineModule; import io.fd.honeycomb.infra.distro.netconf.HoneycombNotification2NetconfProvider; import io.fd.honeycomb.infra.distro.netconf.NetconfModule; -import io.fd.honeycomb.infra.distro.netconf.NetconfReadersModule; import io.fd.honeycomb.infra.distro.netconf.NetconfSshServerProvider; import io.fd.honeycomb.infra.distro.netconf.NetconfTcpServerProvider; import io.fd.honeycomb.infra.distro.restconf.RestconfModule; -import io.fd.honeycomb.infra.distro.schema.SchemaModule; -import io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule; -import java.util.List; +import java.util.Set; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory; @@ -53,29 +50,18 @@ public final class Main { private static final Logger LOG = LoggerFactory.getLogger(Main.class); - public static final List BASE_MODULES = ImmutableList.of( - // Infra - new YangBindingProviderModule(), - new SchemaModule(), - new ConfigAndOperationalPipelineModule(), - new ContextPipelineModule(), - new InitializerPipelineModule(), - new NetconfModule(), - new NetconfReadersModule(), - new RestconfModule(), - // Json config attributes - new CfgAttrsModule()); - - private Main() {} + private Main() { + } public static void main(String[] args) { - init(BASE_MODULES); + final ClassLoader classLoader = Main.class.getClassLoader(); + init(loadActiveModules(aggregateResources(STANDARD_MODULES_RELATIVE_PATH, classLoader))); } /** - * Initialize the Honeycomb infrastructure + all wired plugins. + * Initialize the Honeycomb with provided modules */ - public static Injector init(final List modules) { + public static Injector init(final Set modules) { try { LOG.info("Starting honeycomb"); Injector injector = Guice.createInjector(modules); -- cgit 1.2.3-korg