提交 e05b99a9 编写于 作者: wu-sheng's avatar wu-sheng

Restore module management.

上级 de3bc220
......@@ -58,4 +58,9 @@ public class ClusterModuleStandaloneProvider extends ModuleProvider {
@Override public void notifyAfterCompleted() {
}
@Override
public String[] requiredModules() {
return new String[0];
}
}
......@@ -83,4 +83,9 @@ public class ClusterModuleZookeeperProvider extends ModuleProvider {
@Override public void notifyAfterCompleted() {
}
@Override
public String[] requiredModules() {
return new String[0];
}
}
......@@ -89,4 +89,9 @@ public class CoreModuleProvider extends ModuleProvider {
restServerInstance.setContextPath(moduleConfig.getRestContextPath());
this.getManager().find(ClusterModule.NAME).getService(ModuleRegister.class).register(CoreModule.NAME, "rest", restServerInstance);
}
@Override
public String[] requiredModules() {
return new String[0];
}
}
......@@ -18,9 +18,10 @@
package org.apache.skywalking.oap.server.core.cluster;
import java.util.List;
import org.apache.skywalking.oap.server.library.module.Service;
import java.util.List;
/**
* @author peng-yongsheng
*/
......
......@@ -29,4 +29,12 @@
<artifactId>library-module</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>library-util</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -16,18 +16,19 @@
*
*/
package org.apache.skywalking.oap.server.library.module;
import java.util.*;
import java.util.HashMap;
import java.util.Properties;
/**
* Modularization configurations. The {@link ModuleManager} is going to start, lookup, start modules based on this.
* Modulization configurations. The {@link ModuleManager} is going to start, lookup, start modules based on this.
*
* @author wu-sheng, peng-yongsheng
*/
public class ApplicationConfiguration {
private final Map<String, ModuleConfiguration> modules = new HashMap<>();
private HashMap<String, ModuleConfiguration> modules = new HashMap<>();
public String[] moduleList() {
return modules.keySet().toArray(new String[0]);
......@@ -51,7 +52,7 @@ public class ApplicationConfiguration {
* The configurations about a certain module.
*/
public class ModuleConfiguration {
private final Map<String, ProviderConfiguration> providers = new HashMap<>();
private HashMap<String, ProviderConfiguration> providers = new HashMap<>();
private ModuleConfiguration() {
}
......@@ -64,10 +65,6 @@ public class ApplicationConfiguration {
return providers.containsKey(name);
}
public String[] providerList() {
return providers.keySet().toArray(new String[0]);
}
public ModuleConfiguration addProviderConfiguration(String name, Properties properties) {
ProviderConfiguration newProvider = new ProviderConfiguration(properties);
providers.put(name, newProvider);
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.skywalking.oap.server.library.module;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author wu-sheng, peng-yongsheng
*/
class BootstrapFlow {
private static final Logger logger = LoggerFactory.getLogger(BootstrapFlow.class);
private Map<String, ModuleDefine> loadedModules;
private List<ModuleProvider> startupSequence;
BootstrapFlow(Map<String, ModuleDefine> loadedModules) throws CycleDependencyException {
this.loadedModules = loadedModules;
startupSequence = new LinkedList<>();
makeSequence();
}
@SuppressWarnings("unchecked")
void start(
ModuleManager moduleManager) throws ModuleNotFoundException, ServiceNotProvidedException, ModuleStartException {
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.requiredCheck(provider.getModule().services());
provider.start();
}
}
void notifyAfterCompleted() throws ServiceNotProvidedException {
for (ModuleProvider provider : startupSequence) {
provider.notifyAfterCompleted();
}
}
private void makeSequence() throws CycleDependencyException {
List<ModuleProvider> allProviders = new ArrayList<>();
loadedModules.forEach((moduleName, module) -> allProviders.addAll(module.providers()));
do {
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);
i--;
}
} else {
startupSequence.add(provider);
allProviders.remove(i);
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));
}
}
while (allProviders.size() != 0);
}
}
......@@ -16,22 +16,14 @@
*
*/
package org.apache.skywalking.oap.server.core.cluster;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
package org.apache.skywalking.oap.server.library.module;
/**
* @author peng-yongsheng
* @author wu-sheng
*/
public class ClusterModule extends ModuleDefine {
public static final String NAME = "cluster";
@Override public String name() {
return NAME;
}
@Override public Class[] services() {
return new Class[] {ModuleRegister.class, ModuleQuery.class};
public class CycleDependencyException extends RuntimeException {
public CycleDependencyException(String message) {
super(message);
}
}
......@@ -16,6 +16,7 @@
*
*/
package org.apache.skywalking.oap.server.library.module;
public class DuplicateProviderException extends Exception {
......
......@@ -23,10 +23,6 @@ package org.apache.skywalking.oap.server.library.module;
*/
public class ModuleConfigException extends Exception {
public ModuleConfigException(String message) {
super(message);
}
public ModuleConfigException(String message, Throwable cause) {
super(message, cause);
}
......
......@@ -18,9 +18,11 @@
package org.apache.skywalking.oap.server.library.module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.*;
import org.slf4j.*;
/**
* A module definition.
......@@ -29,9 +31,9 @@ import org.slf4j.*;
*/
public abstract class ModuleDefine {
private static final Logger logger = LoggerFactory.getLogger(ModuleDefine.class);
private final Logger logger = LoggerFactory.getLogger(ModuleDefine.class);
private ModuleProvider moduleProvider;
private LinkedList<ModuleProvider> loadedProviders = new LinkedList<>();
/**
* @return the module name
......@@ -50,13 +52,9 @@ public abstract class ModuleDefine {
* @param configuration of this module
* @throws ProviderNotFoundException when even don't find a single one providers.
*/
public void prepare(ModuleManager moduleManager,
void prepare(ModuleManager moduleManager,
ApplicationConfiguration.ModuleConfiguration configuration) throws ProviderNotFoundException, ServiceNotProvidedException, ModuleConfigException {
ServiceLoader<ModuleProvider> moduleProviderLoader = ServiceLoader.load(ModuleProvider.class);
if (configuration.providerList().length != 1) {
throw new ModuleConfigException("Just a single provider configuration allowed to open in one module.");
}
boolean providerExist = false;
for (ModuleProvider provider : moduleProviderLoader) {
if (!configuration.has(provider.name())) {
......@@ -65,13 +63,15 @@ public abstract class ModuleDefine {
providerExist = true;
if (provider.module().equals(getClass())) {
ModuleProvider newProvider;
try {
moduleProvider = provider.getClass().newInstance();
newProvider = provider.getClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new ProviderNotFoundException(e);
}
moduleProvider.setManager(moduleManager);
moduleProvider.setModule(this);
newProvider.setManager(moduleManager);
newProvider.setModule(this);
loadedProviders.add(newProvider);
}
}
......@@ -79,13 +79,15 @@ public abstract class ModuleDefine {
throw new ProviderNotFoundException(this.name() + " module no provider exists.");
}
logger.info("Prepare the {} provider in {} module.", moduleProvider.name(), this.name());
try {
copyProperties(moduleProvider.createConfigBeanIfAbsent(), configuration.getProviderConfiguration(moduleProvider.name()), this.name(), moduleProvider.name());
} catch (IllegalAccessException e) {
throw new ModuleConfigException(this.name() + " module config transport to config bean failure.", e);
for (ModuleProvider moduleProvider : loadedProviders) {
logger.info("Prepare the {} provider in {} module.", moduleProvider.name(), this.name());
try {
copyProperties(moduleProvider.createConfigBeanIfAbsent(), configuration.getProviderConfiguration(moduleProvider.name()), this.name(), moduleProvider.name());
} catch (IllegalAccessException e) {
throw new ModuleConfigException(this.name() + " module config transport to config bean failure.", e);
}
moduleProvider.prepare();
}
moduleProvider.prepare();
}
private void copyProperties(ModuleConfig dest, Properties src, String moduleName,
......@@ -119,14 +121,25 @@ public abstract class ModuleDefine {
throw new NoSuchFieldException();
}
public final ModuleProvider provider() {
return moduleProvider;
/**
* @return providers of this module
*/
final List<ModuleProvider> providers() {
return loadedProviders;
}
final ModuleProvider provider() throws DuplicateProviderException {
if (loadedProviders.size() > 1) {
throw new DuplicateProviderException(this.name() + " module exist " + loadedProviders.size() + " providers");
}
return loadedProviders.getFirst();
}
public final <T extends Service> T getService(Class<T> serviceType) throws ServiceNotProvidedRuntimeException {
try {
return provider().getService(serviceType);
} catch (ServiceNotProvidedException e) {
} catch (DuplicateProviderException | ServiceNotProvidedException e) {
throw new ServiceNotProvidedRuntimeException(e.getMessage());
}
}
......
......@@ -18,15 +18,68 @@
package org.apache.skywalking.oap.server.library.module;
import java.util.*;
/**
* @author peng-yongsheng
* The <code>ModuleManager</code> takes charge of all {@link ModuleDefine}s in collector.
*
* @author wu-sheng, peng-yongsheng
*/
public interface ModuleManager {
public class ModuleManager {
private boolean isInPrepareStage = true;
private Map<String, ModuleDefine> loadedModules = new HashMap<>();
/**
* Init the given modules
*/
public void init(
ApplicationConfiguration applicationConfiguration) throws ModuleNotFoundException, ProviderNotFoundException, ServiceNotProvidedException, CycleDependencyException, ModuleConfigException, ModuleStartException {
String[] moduleNames = applicationConfiguration.moduleList();
ServiceLoader<ModuleDefine> moduleServiceLoader = ServiceLoader.load(ModuleDefine.class);
LinkedList<String> moduleList = new LinkedList<>(Arrays.asList(moduleNames));
for (ModuleDefine module : moduleServiceLoader) {
for (String moduleName : moduleNames) {
if (moduleName.equals(module.name())) {
ModuleDefine newInstance;
try {
newInstance = module.getClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new ModuleNotFoundException(e);
}
newInstance.prepare(this, applicationConfiguration.getModuleConfiguration(moduleName));
loadedModules.put(moduleName, newInstance);
moduleList.remove(moduleName);
}
}
}
// Finish prepare stage
isInPrepareStage = false;
if (moduleList.size() > 0) {
throw new ModuleNotFoundException(moduleList.toString() + " missing.");
}
BootstrapFlow bootstrapFlow = new BootstrapFlow(loadedModules);
bootstrapFlow.start(this);
bootstrapFlow.notifyAfterCompleted();
}
void init(
ModuleDefine moduleDefine) throws ServiceNotProvidedException, ModuleConfigException, ProviderNotFoundException;
public boolean has(String moduleName) {
return loadedModules.get(moduleName) != null;
}
void start() throws ServiceNotProvidedException, ModuleConfigException, ProviderNotFoundException, ModuleStartException;
public ModuleDefine find(String moduleName) throws ModuleNotFoundRuntimeException {
assertPreparedStage();
ModuleDefine module = loadedModules.get(moduleName);
if (module != null)
return module;
throw new ModuleNotFoundRuntimeException(moduleName + " missing.");
}
ModuleDefine find(String moduleName) throws ModuleNotFoundRuntimeException;
private void assertPreparedStage() {
if (isInPrepareStage) {
throw new AssertionError("Still in preparing stage.");
}
}
}
......@@ -16,6 +16,7 @@
*
*/
package org.apache.skywalking.oap.server.library.module;
public class ModuleNotFoundException extends Exception {
......
......@@ -16,6 +16,7 @@
*
*/
package org.apache.skywalking.oap.server.library.module;
/**
......@@ -23,6 +24,10 @@ package org.apache.skywalking.oap.server.library.module;
*/
public class ModuleNotFoundRuntimeException extends RuntimeException {
public ModuleNotFoundRuntimeException(Throwable cause) {
super(cause);
}
public ModuleNotFoundRuntimeException(String message) {
super(message);
}
......
......@@ -18,7 +18,8 @@
package org.apache.skywalking.oap.server.library.module;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
/**
* The <code>ModuleProvider</code> is an implementation of a {@link ModuleDefine}.
......@@ -55,7 +56,7 @@ public abstract class ModuleProvider {
/**
* @return the module name
*/
public abstract Class module();
public abstract Class<? extends ModuleDefine> module();
/**
* @return ModuleConfig
......@@ -77,6 +78,11 @@ public abstract class ModuleProvider {
*/
public abstract void notifyAfterCompleted() throws ServiceNotProvidedException;
/**
* @return module names which does this module require?
*/
public abstract String[] requiredModules();
/**
* Register a implementation for the service of this module provider.
*/
......@@ -110,8 +116,7 @@ public abstract class ModuleProvider {
}
}
@SuppressWarnings("unchecked")
public <T extends Service> T getService(
@SuppressWarnings("unchecked") <T extends Service> T getService(
Class<T> serviceType) throws ServiceNotProvidedException {
Service serviceImpl = services.get(serviceType);
if (serviceImpl != null) {
......
......@@ -16,6 +16,7 @@
*
*/
package org.apache.skywalking.oap.server.library.module;
/**
......@@ -26,4 +27,4 @@ package org.apache.skywalking.oap.server.library.module;
* @author wu-sheng
*/
public interface Service {
}
\ No newline at end of file
}
......@@ -16,10 +16,11 @@
*
*/
package org.apache.skywalking.oap.server.library.module;
public class ServiceNotProvidedRuntimeException extends RuntimeException {
public ServiceNotProvidedRuntimeException(String message) {
super(message);
}
}
\ No newline at end of file
}
......@@ -33,11 +33,11 @@ public class BaseModuleA extends ModuleDefine {
return new Class[] {ServiceABusiness1.class, ServiceABusiness2.class};
}
public interface ServiceABusiness1 extends Service {
public interface ServiceABusiness1 {
void print();
}
public interface ServiceABusiness2 extends Service {
public interface ServiceABusiness2 {
}
}
......@@ -33,11 +33,11 @@ public class BaseModuleB extends ModuleDefine {
return new Class[] {BaseModuleB.ServiceBBusiness1.class, BaseModuleB.ServiceBBusiness2.class};
}
public interface ServiceBBusiness1 extends Service {
public interface ServiceBBusiness1 {
}
public interface ServiceBBusiness2 extends Service {
public interface ServiceBBusiness2 {
}
}
......@@ -53,7 +53,7 @@ public class ModuleAProvider extends ModuleProvider {
@Override public void notifyAfterCompleted() {
}
public class Config extends ModuleConfig {
public class Config {
private String host;
public String getHost() {
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.skywalking.oap.server.starter;
import java.util.*;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.cluster.ClusterModule;
import org.apache.skywalking.oap.server.core.storage.StorageModule;
import org.apache.skywalking.oap.server.library.module.*;
/**
* @author peng-yongsheng
*/
public class ModuleManagerImpl implements ModuleManager {
private final ApplicationConfiguration applicationConfiguration;
private final Map<String, ModuleDefine> modules;
public ModuleManagerImpl(ApplicationConfiguration applicationConfiguration) {
this.applicationConfiguration = applicationConfiguration;
this.modules = new HashMap<>();
}
@Override
public void start() throws ServiceNotProvidedException, ModuleConfigException, ProviderNotFoundException, ModuleStartException {
CoreModule coreModule = new CoreModule();
ClusterModule clusterModule = new ClusterModule();
StorageModule storageModule = new StorageModule();
init(coreModule);
init(clusterModule);
init(storageModule);
coreModule.provider().start();
storageModule.provider().start();
clusterModule.provider().start();
coreModule.provider().notifyAfterCompleted();
storageModule.provider().notifyAfterCompleted();
clusterModule.provider().notifyAfterCompleted();
}
@Override public void init(
ModuleDefine moduleDefine) throws ServiceNotProvidedException, ModuleConfigException, ProviderNotFoundException {
if (!applicationConfiguration.has(moduleDefine.name())) {
throw new ModuleConfigException("Can't found core module configuration, please check the application.yml file.");
}
moduleDefine.prepare(this, applicationConfiguration.getModuleConfiguration(moduleDefine.name()));
modules.put(moduleDefine.name(), moduleDefine);
}
@Override public ModuleDefine find(String moduleName) throws ModuleNotFoundRuntimeException {
ModuleDefine module = modules.get(moduleName);
if (module != null)
return module;
throw new ModuleNotFoundRuntimeException(moduleName + " missing.");
}
}
......@@ -18,9 +18,9 @@
package org.apache.skywalking.oap.server.starter;
import java.util.concurrent.TimeUnit;
import org.apache.skywalking.oap.server.library.module.ApplicationConfiguration;
import org.apache.skywalking.oap.server.library.module.*;
import org.apache.skywalking.oap.server.starter.config.ApplicationConfigLoader;
import org.apache.skywalking.oap.server.starter.config.ConfigFileNotFoundException;
import org.slf4j.*;
/**
......@@ -32,21 +32,13 @@ public class OAPServerStartUp {
public static void main(String[] args) {
ApplicationConfigLoader configLoader = new ApplicationConfigLoader();
ModuleManager manager = new ModuleManager();
try {
ApplicationConfiguration applicationConfiguration = configLoader.load();
ModuleManagerImpl moduleManager = new ModuleManagerImpl(applicationConfiguration);
moduleManager.start();
} catch (Throwable e) {
manager.init(applicationConfiguration);
} catch (ConfigFileNotFoundException | ModuleNotFoundException | ProviderNotFoundException | ServiceNotProvidedException | ModuleConfigException | ModuleStartException e) {
logger.error(e.getMessage(), e);
System.exit(1);
}
logger.info("OAP server start up successful.");
try {
TimeUnit.MINUTES.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
......@@ -18,13 +18,18 @@
package org.apache.skywalking.oap.server.starter.config;
import java.io.*;
import java.util.*;
import org.apache.skywalking.oap.server.library.module.ApplicationConfiguration;
import org.apache.skywalking.oap.server.library.util.*;
import org.slf4j.*;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.apache.skywalking.oap.server.library.util.ResourceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import java.io.FileNotFoundException;
import java.io.Reader;
import java.util.Map;
import java.util.Properties;
/**
* Initialize collector settings with following sources.
* Use application.yml as primary setting,
......@@ -43,6 +48,7 @@ public class ApplicationConfigLoader implements ConfigLoader<ApplicationConfigur
@Override public ApplicationConfiguration load() throws ConfigFileNotFoundException {
ApplicationConfiguration configuration = new ApplicationConfiguration();
this.loadConfig(configuration);
this.loadDefaultConfig(configuration);
this.overrideConfigBySystemEnv(configuration);
return configuration;
}
......@@ -78,6 +84,31 @@ public class ApplicationConfigLoader implements ConfigLoader<ApplicationConfigur
}
}
@SuppressWarnings("unchecked")
private void loadDefaultConfig(ApplicationConfiguration configuration) throws ConfigFileNotFoundException {
try {
Reader applicationReader = ResourceUtils.read("application-default.yml");
Map<String, Map<String, Map<String, ?>>> moduleConfig = yaml.loadAs(applicationReader, Map.class);
if (CollectionUtils.isNotEmpty(moduleConfig)) {
moduleConfig.forEach((moduleName, providerConfig) -> {
if (!configuration.has(moduleName)) {
logger.warn("The {} module did't define in application.yml, use default", moduleName);
ApplicationConfiguration.ModuleConfiguration moduleConfiguration = configuration.addModule(moduleName);
providerConfig.forEach((name, propertiesConfig) -> {
Properties properties = new Properties();
if (propertiesConfig != null) {
propertiesConfig.forEach(properties::put);
}
moduleConfiguration.addProviderConfiguration(name, properties);
});
}
});
}
} catch (FileNotFoundException e) {
throw new ConfigFileNotFoundException(e.getMessage(), e);
}
}
private void overrideConfigBySystemEnv(ApplicationConfiguration configuration) {
for (Map.Entry<Object, Object> prop : System.getProperties().entrySet()) {
overrideModuleSettings(configuration, prop.getKey().toString(), prop.getValue().toString());
......
......@@ -16,8 +16,10 @@
*
*/
package org.apache.skywalking.oap.server.starter.config;
/**
* @author peng-yongsheng
*/
......
......@@ -16,6 +16,7 @@
*
*/
package org.apache.skywalking.oap.server.starter.config;
/**
......
......@@ -36,24 +36,35 @@ public class StorageModuleElasticsearchProvider extends ModuleProvider {
this.storageConfig = new StorageModuleElasticsearchConfig();
}
@Override public String name() {
@Override
public String name() {
return "elasticsearch";
}
@Override public Class module() {
@Override
public Class module() {
return StorageModule.class;
}
@Override public ModuleConfig createConfigBeanIfAbsent() {
@Override
public ModuleConfig createConfigBeanIfAbsent() {
return storageConfig;
}
@Override public void prepare() throws ServiceNotProvidedException {
@Override
public void prepare() throws ServiceNotProvidedException {
}
@Override public void start() throws ModuleStartException {
@Override
public void start() throws ModuleStartException {
}
@Override public void notifyAfterCompleted() {
@Override
public void notifyAfterCompleted() {
}
@Override
public String[] requiredModules() {
return new String[0];
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册