From d44b7981744de9619a42a565ee443c03b3f3f48c Mon Sep 17 00:00:00 2001 From: wu-sheng Date: Tue, 7 Nov 2017 15:45:16 +0800 Subject: [PATCH] Support cycle module dependencies check. Trigger exception when happen. @peng-yongsheng org.skywalking.apm.collector.core.module.CycleDependencyException: Exist cycle module dependencies in Test[provider=org.skywalking.apm.collector.core.module.TestModuleProvider] BaseA[provider=org.skywalking.apm.collector.core.module.ModuleAProvider] --- .../apm/collector/core/graph/Graph.java | 2 +- ...ava => PotentialCyclicGraphException.java} | 4 +- .../collector/core/module/BootstrapFlow.java | 125 ++++++++++++++++++ .../core/module/CycleDependencyException.java | 28 ++++ .../apm/collector/core/module/Module.java | 34 +---- .../collector/core/module/ModuleManager.java | 11 +- .../collector/core/module/ModuleProvider.java | 8 ++ .../core/graph/GraphManagerTest.java | 2 +- 8 files changed, 171 insertions(+), 43 deletions(-) rename apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/{PotentialAcyclicGraphException.java => PotentialCyclicGraphException.java} (86%) create mode 100644 apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/BootstrapFlow.java create mode 100644 apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/CycleDependencyException.java diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/Graph.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/Graph.java index 7742b59f31..32a790f3eb 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/Graph.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/Graph.java @@ -46,7 +46,7 @@ public final class Graph { void checkForNewNode(Node node) { int nodeId = node.getHandler().id(); if (nodeIndex.containsKey(nodeId)) { - throw new PotentialAcyclicGraphException("handler=" + throw new PotentialCyclicGraphException("handler=" + node.getHandler().getClass().getName() + " already exists in graph[" + id + "】"); } diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/PotentialAcyclicGraphException.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/PotentialCyclicGraphException.java similarity index 86% rename from apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/PotentialAcyclicGraphException.java rename to apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/PotentialCyclicGraphException.java index 91d06790fa..55a05a13b8 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/PotentialAcyclicGraphException.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/graph/PotentialCyclicGraphException.java @@ -21,8 +21,8 @@ package org.skywalking.apm.collector.core.graph; /** * @author wusheng */ -public class PotentialAcyclicGraphException extends RuntimeException { - public PotentialAcyclicGraphException(String message) { +public class PotentialCyclicGraphException extends RuntimeException { + public PotentialCyclicGraphException(String message) { super(message); } } diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/BootstrapFlow.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/BootstrapFlow.java new file mode 100644 index 0000000000..3513392186 --- /dev/null +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/BootstrapFlow.java @@ -0,0 +1,125 @@ +/* + * Copyright 2017, OpenSkywalking Organization All rights reserved. + * + * 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. + * + * Project repository: https://github.com/OpenSkywalking/skywalking + */ + +package org.skywalking.apm.collector.core.module; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.skywalking.apm.collector.core.util.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author wu-sheng + */ +public class BootstrapFlow { + private final Logger logger = LoggerFactory.getLogger(BootstrapFlow.class); + + private Map loadedModules; + private ApplicationConfiguration applicationConfiguration; + private List startupSequence; + + public BootstrapFlow(Map loadedModules, + ApplicationConfiguration applicationConfiguration) throws CycleDependencyException { + this.loadedModules = loadedModules; + this.applicationConfiguration = applicationConfiguration; + startupSequence = new LinkedList<>(); + + makeSequence(); + } + + void start(ModuleManager moduleManager, + ApplicationConfiguration configuration) throws ProviderNotFoundException, ModuleNotFoundException, ServiceNotProvidedException { + for (ModuleProvider provider : startupSequence) { + String[] requiredModules = provider.requiredModules(); + if (requiredModules != null) { + for (String module : requiredModules) { + if (!moduleManager.has(module)) { + throw new ModuleNotFoundException(module + " is required by " + provider.getModuleName() + + "." + provider.name() + ", but not found."); + } + } + } + logger.info("start the provider {} in {} module.", provider.name(), provider.getModuleName()); + provider.start(configuration.getModuleConfiguration(provider.getModuleName()).getProviderConfiguration(provider.name())); + + provider.requiredCheck(provider.getModule().services()); + } + } + + void notifyAfterCompleted() throws ProviderNotFoundException, ModuleNotFoundException, ServiceNotProvidedException { + for (ModuleProvider provider : startupSequence) { + provider.notifyAfterCompleted(); + } + } + + private void makeSequence() throws CycleDependencyException { + List allProviders = new ArrayList<>(); + loadedModules.forEach((moduleName, module) -> { + module.providers().forEach(provider -> { + allProviders.add(provider); + }); + }); + + while (true) { + int numOfToBeSequenced = allProviders.size(); + for (int i = 0; i < allProviders.size(); i++) { + ModuleProvider provider = allProviders.get(i); + String[] requiredModules = provider.requiredModules(); + if (CollectionUtils.isNotEmpty(requiredModules)) { + boolean isAllRequiredModuleStarted = true; + for (String module : requiredModules) { + // find module in all ready existed startupSequence + boolean exist = false; + for (ModuleProvider moduleProvider : startupSequence) { + if (moduleProvider.getModuleName().equals(module)) { + exist = true; + break; + } + } + if (!exist) { + isAllRequiredModuleStarted = false; + break; + } + } + + if (isAllRequiredModuleStarted) { + startupSequence.add(provider); + allProviders.remove(i); + } + } else { + startupSequence.add(provider); + allProviders.remove(i); + } + } + + if (numOfToBeSequenced == allProviders.size()) { + StringBuilder unsequencedProviders = new StringBuilder(); + allProviders.forEach(provider -> { + unsequencedProviders.append(provider.getModuleName()).append("[provider=").append(provider.getClass().getName()).append("]\n"); + }); + throw new CycleDependencyException("Exist cycle module dependencies in \n" + unsequencedProviders.substring(0, unsequencedProviders.length() - 1)); + } + if (allProviders.size() == 0) { + break; + } + } + } +} diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/CycleDependencyException.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/CycleDependencyException.java new file mode 100644 index 0000000000..ee29641c38 --- /dev/null +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/CycleDependencyException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017, OpenSkywalking Organization All rights reserved. + * + * 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. + * + * Project repository: https://github.com/OpenSkywalking/skywalking + */ + +package org.skywalking.apm.collector.core.module; + +/** + * @author wu-sheng + */ +public class CycleDependencyException extends RuntimeException { + public CycleDependencyException(String message) { + super(message); + } +} diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/Module.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/Module.java index fdc554d0c2..b041e44442 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/Module.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/Module.java @@ -87,45 +87,15 @@ public abstract class Module { } } - void start(ModuleManager moduleManager, - ApplicationConfiguration.ModuleConfiguration configuration) throws ProviderNotFoundException, ModuleNotFoundException, ServiceNotProvidedException { - for (ModuleProvider provider : loadedProviders) { - String[] requiredModules = provider.requiredModules(); - if (requiredModules != null) { - for (String module : requiredModules) { - if (!moduleManager.has(module)) { - throw new ModuleNotFoundException(module + " is required by " + name() + ", but not found."); - } - } - } - logger.info("start the provider {} in {} module.", provider.name(), provider.module().getName()); - provider.start(configuration.getProviderConfiguration(provider.name())); - - provider.requiredCheck(services()); - } - } - - void notifyAfterCompleted() throws ProviderNotFoundException, ModuleNotFoundException, ServiceNotProvidedException { - for (ModuleProvider provider : loadedProviders) { - provider.notifyAfterCompleted(); - } - } - /** * @return providers of this module */ - final List providers() throws ProviderNotFoundException { - if (loadedProviders.size() == 0) { - throw new ProviderNotFoundException(this.name() + " module no provider exists."); - } - + final List providers() { return loadedProviders; } final ModuleProvider provider() throws ProviderNotFoundException, DuplicateProviderException { - if (loadedProviders.size() == 0) { - throw new ProviderNotFoundException(this.name() + " module no provider exists."); - } else if (loadedProviders.size() > 1) { + if (loadedProviders.size() > 1) { throw new DuplicateProviderException(this.name() + " module exist " + loadedProviders.size() + " providers"); } diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java index fc78ce3c2d..6bf0d4452f 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java @@ -39,7 +39,7 @@ public class ModuleManager { * @param applicationConfiguration */ public void init( - ApplicationConfiguration applicationConfiguration) throws ModuleNotFoundException, ProviderNotFoundException, ServiceNotProvidedException { + ApplicationConfiguration applicationConfiguration) throws ModuleNotFoundException, ProviderNotFoundException, ServiceNotProvidedException, CycleDependencyException { String[] moduleNames = applicationConfiguration.moduleList(); ServiceLoader moduleServiceLoader = ServiceLoader.load(Module.class); LinkedList moduleList = new LinkedList(Arrays.asList(moduleNames)); @@ -65,13 +65,10 @@ public class ModuleManager { throw new ModuleNotFoundException(moduleList.toString() + " missing."); } - for (Module module : loadedModules.values()) { - module.start(this, applicationConfiguration.getModuleConfiguration(module.name())); - } + BootstrapFlow bootstrapFlow = new BootstrapFlow(loadedModules, applicationConfiguration); - for (Module module : loadedModules.values()) { - module.notifyAfterCompleted(); - } + bootstrapFlow.start(this, applicationConfiguration); + bootstrapFlow.notifyAfterCompleted(); } public boolean has(String moduleName) { diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java index 3582241054..de48bbe6ba 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java @@ -133,4 +133,12 @@ public abstract class ModuleProvider { throw new ServiceNotProvidedException("Service " + serviceType.getName() + " should not be provided, based on module define."); } + + Module getModule() { + return module; + } + + String getModuleName() { + return module.name(); + } } diff --git a/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/graph/GraphManagerTest.java b/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/graph/GraphManagerTest.java index 0e011e61ff..0da0988372 100644 --- a/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/graph/GraphManagerTest.java +++ b/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/graph/GraphManagerTest.java @@ -75,7 +75,7 @@ public class GraphManagerTest { Assert.assertEquals(expected, output); } - @Test(expected = PotentialAcyclicGraphException.class) + @Test(expected = PotentialCyclicGraphException.class) public void testPotentialAcyclicGraph() { Graph testGraph = GraphManager.INSTANCE.createIfAbsent(3, String.class); Node node = testGraph.addNode(new Node1Processor()); -- GitLab