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 7742b59f3..32a790f3e 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 91d06790f..55a05a13b 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 000000000..351339218
--- /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 000000000..ee29641c3
--- /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 fdc554d0c..b041e4444 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 fc78ce3c2..6bf0d4452 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 358224105..de48bbe6b 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 0e011e61f..0da098837 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());