package com.aol.micro.server;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import com.aol.cyclops2.util.ExceptionSoftener;
import cyclops.Streams;
import cyclops.collections.ListX;
import cyclops.stream.ReactiveSeq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import com.aol.micro.server.config.Config;
import com.aol.micro.server.config.MicroserverConfigurer;
import com.aol.micro.server.module.Module;
import com.aol.micro.server.servers.ApplicationRegister;
import com.aol.micro.server.servers.ServerApplication;
import com.aol.micro.server.servers.ServerApplicationFactory;
import com.aol.micro.server.servers.ServerRunner;
import com.aol.micro.server.spring.SpringContextFactory;
import lombok.Getter;
/**
*
* Startup class for Microserver instance
*
* @author johnmcclean
*
*/
public class MicroserverApp {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final ListX<Module> modules;
private final CompletableFuture end = new CompletableFuture();
@Getter
private final ApplicationContext springContext;
/**
* This will construct a Spring context for this Microserver instance.
* The calling class will be used to determine the base package to auto-scan from for Spring Beans
* It will attempt to pick up an @Microservice annotation first, if not present the package of the calling class
* will be used.
*
* @param modules Multiple Microservice end points that can be deployed within a single Spring context
*/
public MicroserverApp(Module... modules) {
this.modules = ListX.of(modules);
GlobalState.state.setModules(this.modules);
initSpringProperties(modules[0]);
Class c = extractClass();
springContext = new SpringContextFactory(
new MicroserverConfigurer().buildConfig(c), extractClass(),
modules[0].getSpringConfigurationClasses()).createSpringContext();
}
/**
* This will construct a Spring context for this Microserver instance.
* The provided class will be used to determine the base package to auto-scan from for Spring Beans
* It will attempt to pick up an @Microservice annotation first, if not present the package of the provided class
* will be used.
*
* @param c Class used to configure Spring
* @param modules Multiple Microservice end points that can be deployed within a single Spring context
*/
public MicroserverApp(Class c, Module... modules) {
this.modules = ListX.of(modules);
GlobalState.state.setModules(this.modules);
initSpringProperties(modules[0]);
springContext = new SpringContextFactory(
new MicroserverConfigurer().buildConfig(c), c,
modules[0].getSpringConfigurationClasses()).createSpringContext();
}
private void initSpringProperties(Module m) {
System.setProperty("server.contextPath", "/" + m.getContext());
}
private Class extractClass() {
try {
return Class.forName(new Exception().getStackTrace()[2].getClassName());
} catch (ClassNotFoundException e) {
throw ExceptionSoftener.throwSoftenedException(e);
}
}
public void stop() {
end.complete(true);
Config.reset();
}
public void run() {
start().forEach(thread -> join(thread));
}
public List<Thread> start() {
List<ServerApplication> apps = modules.map(module -> createServer(module));
ServerRunner runner;
try {
runner = new ServerRunner(
springContext.getBean(ApplicationRegister.class), apps, end);
} catch (BeansException e) {
runner = new ServerRunner(
apps, end);
}
return runner.run();
}
private ServerApplication createServer(Module module) {
List<ServerApplicationFactory> applications = ReactiveSeq.fromStream(PluginLoader.INSTANCE.plugins.get()
.stream())
.filter(m -> m.serverApplicationFactory() != null)
.map(Plugin::serverApplicationFactory)
.flatMap(Streams::optionalToStream)
.toList();
if (applications.size() > 1) {
logger.error("ERROR! Multiple server application factories found : The solution is remove one these plugins from your classpath ",
applications);
System.err.println("ERROR! Multiple server application factories found : The solution is remove one these plugins from your classpath "
+ applications);
throw new IncorrectNumberOfServersConfiguredException(
"Multiple server application factories found : The solution is remove one these plugins from your classpath "
+ applications);
} else if (applications.size() == 0) {
logger.error("ERROR! No server application factories found. If you using micro-spring-boot don't call MicroserverApp.start() method. A possible solution is add one of micro-grizzly or micro-tomcat to the classpath.");
System.err.println("ERROR! No server application factories found. If you using micro-spring-boot don't call MicroserverApp.start() method. A possible solution is add one of micro-grizzly or micro-tomcat to the classpath.");
throw new IncorrectNumberOfServersConfiguredException(
"No server application factories found. If you using micro-spring-boot don't call MicroserverApp.start() method. A possible solution is add one of micro-grizzly or micro-tomcat to the classpath. ");
}
ServerApplication app = applications.get(0)
.createApp(module, springContext);
return app;
}
private void join(Thread thread) {
try {
thread.join();
} catch (InterruptedException e) {
Thread.currentThread()
.interrupt();
ExceptionSoftener.throwSoftenedException(e);
}
}
}