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]
This commit is contained in:
parent
e3e06ec3cf
commit
d44b798174
|
|
@ -46,7 +46,7 @@ public final class Graph<INPUT> {
|
|||
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 + "】");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, Module> loadedModules;
|
||||
private ApplicationConfiguration applicationConfiguration;
|
||||
private List<ModuleProvider> startupSequence;
|
||||
|
||||
public BootstrapFlow(Map<String, Module> 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<ModuleProvider> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ModuleProvider> providers() throws ProviderNotFoundException {
|
||||
if (loadedProviders.size() == 0) {
|
||||
throw new ProviderNotFoundException(this.name() + " module no provider exists.");
|
||||
}
|
||||
|
||||
final List<ModuleProvider> 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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Module> moduleServiceLoader = ServiceLoader.load(Module.class);
|
||||
LinkedList<String> 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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public class GraphManagerTest {
|
|||
Assert.assertEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test(expected = PotentialAcyclicGraphException.class)
|
||||
@Test(expected = PotentialCyclicGraphException.class)
|
||||
public void testPotentialAcyclicGraph() {
|
||||
Graph<String> testGraph = GraphManager.INSTANCE.createIfAbsent(3, String.class);
|
||||
Node<String, String> node = testGraph.addNode(new Node1Processor());
|
||||
|
|
|
|||
Loading…
Reference in New Issue