diff --git a/conf/application.conf b/conf/application.conf index 6d38ed1..bfffa02 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -6,10 +6,8 @@ application { version = "1.0" name="Wurrly API" - # tmpdir - tmpdir = ${java.io.tmpdir}/${application.name} - - # path (a.k.a. as contextPath) + + path = "/v1" # localhost @@ -18,30 +16,6 @@ application { # HTTP ports port = 8090 - # uncomment to enabled HTTPS - # securePort = 8443 - - # we do UTF-8 - charset = UTF-8 - - # date format - dateFormat = dd-MMM-yyyy - - fallbackHandler = "io.sinistral.proteus.server.handlers.ServerFallbackHandler" - - defaultResponseListener = "io.sinistral.server.handlers.ServerDefaultResponseListener" - # number format, system default. set it at runtime - # numberFormat = DecimalFormat.getInstance(${application.lang})).toPattern() - - # comma separated list of locale using the language tag format. Default to: Locale.getDefault() - lang = Locale.getDefault() - - # timezone, system default. set it at runtime - # tz = ZoneId.systemDefault().getId() - - # redirect to/force https - # example: https://my.domain.com/{0} - redirect_https = "" } diff --git a/src/main/java/io/sinistral/proteus/Application.java b/src/main/java/io/sinistral/proteus/Application.java index c7272fd..029d216 100644 --- a/src/main/java/io/sinistral/proteus/Application.java +++ b/src/main/java/io/sinistral/proteus/Application.java @@ -2,7 +2,9 @@ * */ package io.sinistral.proteus; -import java.util.HashSet; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -20,6 +22,7 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; +import com.google.inject.Module; import com.google.inject.name.Named; import com.jsoniter.DecodingMode; import com.jsoniter.JsonIterator; @@ -56,28 +59,56 @@ public class Application @Named("registeredEndpoints") protected Set registeredEndpoints; + @Inject + @Named("registeredServices") + protected Set> registeredServices; + + @Inject + protected RoutingHandler router; + + @Inject + protected Config config; + + protected List registeredModules = new ArrayList<>(); + protected Injector injector = null; protected ServiceManager serviceManager = null; - protected Undertow undertow = null; - - protected Set> registeredServices = new HashSet<>(); - + protected Undertow undertow = null; protected Class rootHandlerClass; - protected HttpHandler rootHandler; + public Application() { - injector = Guice.createInjector(new ConfigModule()); - injector.injectMembers(this); + injector = Guice.createInjector(new ConfigModule("application.conf")); + injector.injectMembers(this); + + } + + public Application(String configFile) + { + + injector = Guice.createInjector(new ConfigModule(configFile)); + injector.injectMembers(this); + + } + + public Application(URL configURL) + { + + injector = Guice.createInjector(new ConfigModule(configURL)); + injector.injectMembers(this); } public void start() { - if( this.rootHandlerClass == null && this.rootHandler == null ) + + injector = injector.createChildInjector(registeredModules); + + if( rootHandlerClass == null && rootHandler == null ) { log.error("Cannot start the server without specifying the root handler class or a root HttpHandler!"); System.exit(1); @@ -86,52 +117,61 @@ public void start() log.info("Starting services..."); Set services = registeredServices.stream() - .map( sc -> injector.getInstance(sc)) + .map( sc -> injector.getInstance(sc) ) .collect(Collectors.toSet()); - this.serviceManager = new ServiceManager(services); - - this.serviceManager.addListener(new Listener() { - public void stopped() { - - } - public void healthy() { - log.info("Services are healthy..."); - - buildServer().start(); - } - public void failure(Service service) - { - log.error("Error on service: " + service); - System.exit(1); - } - }, - MoreExecutors.directExecutor()); + serviceManager = new ServiceManager(services); + + serviceManager.addListener(new Listener() + { + public void stopped() + { + + } + + public void healthy() + { + log.info("Services are healthy..."); + + buildServer().start(); + + printStatus(); + } + + public void failure(Service service) + { + log.error("Error on service: " + service); + System.exit(1); + } + }, MoreExecutors.directExecutor()); + + Runtime.getRuntime().addShutdownHook(new Thread() + { + @Override + public void run() + { + try + { + log.info("Shutting down..."); + + serviceManager.stopAsync().awaitStopped(5, TimeUnit.SECONDS); + undertow.stop(); + + log.info("Shutdown complete."); + } catch (TimeoutException timeout) + { + timeout.printStackTrace(); + } + } + }); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - try { - log.info("Shutting down..."); - - serviceManager.stopAsync().awaitStopped(5, TimeUnit.SECONDS); - undertow.stop(); - - log.info("Shutdown complete."); - } catch (TimeoutException timeout) { - timeout.printStackTrace(); - }}}); - serviceManager.startAsync(); - } + + } public Undertow buildServer() { - - final Config rootConfig = injector.getInstance(Config.class); - - final RoutingHandler router = injector.getInstance(RoutingHandler.class); - + for(Class controllerClass : registeredControllers) { HandlerGenerator generator = new HandlerGenerator("io.sinistral.proteus.controllers.handlers",controllerClass); @@ -142,46 +182,31 @@ public Undertow buildServer() router.addAll(generatedRouteSupplier.get()); } - - Config globalHeaders = rootConfig.getConfig("globalHeaders"); - - Map globalHeadersParameters = globalHeaders.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().render())); - - - StringBuilder sb = new StringBuilder(); - - sb.append("\n\nUsing the following global headers: \n\n"); - sb.append(globalHeadersParameters.entrySet().stream().map( e -> "\t" + e.getKey() + " = " + e.getValue() ).collect(Collectors.joining("\n"))); - sb.append("\n\nRegistered the following endpoints: \n\n"); - sb.append(this.registeredEndpoints.stream().sorted().map(EndpointInfo::toString).collect(Collectors.joining("\n"))); - sb.append("\n"); - - log.info(sb.toString()); - final HttpHandler handler; - if( this.rootHandlerClass != null ) + if( rootHandlerClass != null ) { - handler = this.injector.getInstance(this.rootHandlerClass); + handler = injector.getInstance(rootHandlerClass); } else { - handler = this.rootHandler; + handler = rootHandler; } - + undertow = Undertow.builder() - .addHttpListener(rootConfig.getInt("application.port"),rootConfig.getString("application.host")) - .setBufferSize(1024 * 16) - .setIoThreads(Runtime.getRuntime().availableProcessors()*2) - .setServerOption(UndertowOptions.ENABLE_HTTP2, true) - .setServerOption(UndertowOptions.ALWAYS_SET_DATE, true) - // .setServerOption(UndertowOptions.BUFFER_PIPELINED_DATA, true) - .setSocketOption(org.xnio.Options.BACKLOG, 10000) - .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false) - .setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false) - .setServerOption(UndertowOptions.MAX_ENTITY_SIZE, 1000000L * 200 ) - .setWorkerThreads(Runtime.getRuntime().availableProcessors()*8) + .addHttpListener(config.getInt("application.port"),config.getString("application.host")) + .setServerOption(UndertowOptions.ENABLE_HTTP2, config.getBoolean("undertow.server.enableHttp2")) + .setServerOption(UndertowOptions.ALWAYS_SET_DATE, config.getBoolean("undertow.server.alwaysSetDate")) + .setServerOption(UndertowOptions.BUFFER_PIPELINED_DATA, config.getBoolean("undertow.server.bufferPipelinedData")) + .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, config.getBoolean("undertow.server.alwaysSetKeepAlive")) + .setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, config.getBoolean("undertow.server.recordRequestStartTime")) + .setServerOption(UndertowOptions.MAX_ENTITY_SIZE, config.getBytes("undertow.server.maxEntitySize") ) + .setSocketOption(org.xnio.Options.BACKLOG, config.getInt("undertow.socket.backlog")) + .setWorkerThreads(config.getInt("undertow.workerThreads")) + .setBufferSize(config.getBytes("undertow.bufferSize").intValue()) + .setIoThreads(config.getInt("undertow.ioThreads")) + .setDirectBuffers( config.getBoolean("undertow.directBuffers")) .setHandler( handler ) .build(); @@ -192,13 +217,19 @@ public Undertow buildServer() public Application addService(Class serviceClass) { - this.registeredServices.add(serviceClass); + registeredServices.add(serviceClass); return this; } public Application addController(Class controllerClass) { - this.registeredControllers.add(controllerClass); + registeredControllers.add(controllerClass); + return this; + } + + public Application addModule(Module module) + { + registeredModules.add(module); return this; } @@ -212,16 +243,47 @@ public void setRootHandler( HttpHandler rootHandler ) this.rootHandler = rootHandler; } - - /** - * @return the undertow - */ + public Undertow getUndertow() { return undertow; } + /** + * @return the serviceManager + */ + public ServiceManager getServiceManager() + { + return serviceManager; + } + + /** + * @return the config + */ + public Config getConfig() + { + return config; + } + + public void printStatus() + { + Config globalHeaders = config.getConfig("globalHeaders"); + + Map globalHeadersParameters = globalHeaders.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().render())); + + + StringBuilder sb = new StringBuilder(); + + sb.append("\n\nUsing the following global headers: \n\n"); + sb.append(globalHeadersParameters.entrySet().stream().map( e -> "\t" + e.getKey() + " = " + e.getValue() ).collect(Collectors.joining("\n"))); + sb.append("\n\nRegistered the following endpoints: \n\n"); + sb.append(this.registeredEndpoints.stream().sorted().map(EndpointInfo::toString).collect(Collectors.joining("\n"))); + sb.append("\n"); + + log.info(sb.toString()); + } + public static void main(String[] args) { diff --git a/src/main/java/io/sinistral/proteus/modules/ConfigModule.java b/src/main/java/io/sinistral/proteus/modules/ConfigModule.java index c5736f1..b07e0b7 100644 --- a/src/main/java/io/sinistral/proteus/modules/ConfigModule.java +++ b/src/main/java/io/sinistral/proteus/modules/ConfigModule.java @@ -5,6 +5,7 @@ import java.io.File; import java.lang.reflect.Type; +import java.net.URL; import java.util.List; import java.util.Map.Entry; @@ -18,11 +19,6 @@ import com.google.inject.name.Named; import com.google.inject.name.Names; import com.google.inject.util.Types; -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.annotation.JsoniterAnnotationSupport; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigObject; @@ -41,7 +37,8 @@ public class ConfigModule extends AbstractModule * @param configFileName */ - protected String configFile = "application.conf"; + protected String configFile = null; + protected URL configURL = null; protected Config config = null; public ConfigModule() @@ -53,21 +50,32 @@ public ConfigModule(String configFile) { this.configFile = configFile; } + + public ConfigModule(URL configURL) + { + this.configURL = configURL; + } @Override protected void configure() { - this.bindConfig(fileConfig(configFile)); - - install(new RoutingModule(this.config)); + if(configFile == null && configURL == null) + { + this.bindConfig(ConfigFactory.defaultApplication()); + } + else if(configURL != null) + { + this.bindConfig( ConfigFactory.load(ConfigFactory.parseURL(configURL))); + } + else if(configFile != null) + { + this.bindConfig(fileConfig(configFile)); + } + + install(new ServerModule(this.config)); } - - public void bindFileConfig(String fileName) - { - this.bindConfig(fileConfig(configFile)); - } @SuppressWarnings("unchecked") private void bindConfig(final Config config) @@ -96,7 +104,11 @@ private void bindConfig(final Config config) } // bind config - this.config = ConfigFactory.load(config); + Config referenceConfig = ConfigFactory.load(ConfigFactory.defaultReference()); + + this.config = ConfigFactory.load(config).withFallback(referenceConfig); + + System.out.println(this.config); this.binder().bind(Config.class).toInstance( config ); diff --git a/src/main/java/io/sinistral/proteus/modules/RoutingModule.java b/src/main/java/io/sinistral/proteus/modules/ServerModule.java similarity index 81% rename from src/main/java/io/sinistral/proteus/modules/RoutingModule.java rename to src/main/java/io/sinistral/proteus/modules/ServerModule.java index 2305731..de465bb 100644 --- a/src/main/java/io/sinistral/proteus/modules/RoutingModule.java +++ b/src/main/java/io/sinistral/proteus/modules/ServerModule.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.google.common.util.concurrent.Service; import com.google.inject.AbstractModule; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; @@ -27,16 +28,17 @@ * */ @Singleton -public class RoutingModule extends AbstractModule +public class ServerModule extends AbstractModule { - private static Logger log = LoggerFactory.getLogger(RoutingModule.class.getCanonicalName()); + private static Logger log = LoggerFactory.getLogger(ServerModule.class.getCanonicalName()); protected Set registeredEndpoints = new TreeSet<>(); protected Set> registeredControllers = new HashSet<>(); - + protected Set> registeredServices = new HashSet<>(); + protected Config config; - public RoutingModule(Config config) + public ServerModule(Config config) { this.config = config; } @@ -66,7 +68,7 @@ protected void configure() this.bind(RoutingHandler.class).toInstance(router); - this.bind(RoutingModule.class).toInstance(this); + this.bind(ServerModule.class).toInstance(this); try @@ -84,7 +86,8 @@ protected void configure() this.bind(new TypeLiteral>>() {}).annotatedWith(Names.named("registeredControllers")).toInstance(registeredControllers); this.bind(new TypeLiteral>() {}).annotatedWith(Names.named("registeredEndpoints")).toInstance(registeredEndpoints); - + this.bind(new TypeLiteral>>() {}).annotatedWith(Names.named("registeredServices")).toInstance(registeredServices); + this.bind(XmlMapper.class).toInstance(new XmlMapper()); diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf new file mode 100644 index 0000000..3d59de6 --- /dev/null +++ b/src/main/resources/reference.conf @@ -0,0 +1,94 @@ + +application { + + env = dev + + version = "1.0" + + name="proteus" + # tmpdir + tmpdir = ${java.io.tmpdir}/${application.name} + + # path (a.k.a. as contextPath) + path = "/v1" + + # localhost + host = "localhost" + + # HTTP ports + port = 8090 + + # uncomment to enabled HTTPS + # securePort = 8443 + + # we do UTF-8 + charset = UTF-8 + + # date format + dateFormat = dd-MMM-yyyy + + fallbackHandler = "io.sinistral.proteus.server.handlers.ServerFallbackHandler" + + defaultResponseListener = "io.sinistral.proteus.server.handlers.ServerDefaultResponseListener" + + redirect_https = "" + +} + +api.version="v1" + +globalHeaders +{ +# Access-Control-Allow-Origin: "*" +# Access-Control-Allow-Methods: "*" +# Access-Control-Allow-Headers: "*" + Server = ${application.name} +} + + +assets { + path = "/public" + dir = "./assets" + cache { + time = 500 + } +} + + + +swagger { + swagger: "2.0" + info { + title = ${application.name} + version = ${application.version} + } + theme="default" + basePath= ${application.path}"/swagger" + specFilename="swagger.json" + consumes = ["application/json"] + produces = ["application/json"] + schemes = ["http"] +} + +undertow +{ + server { + enableHttp2 = false + alwaysSetDate = true + alwaysSetKeepAlive = false + recordRequestStartTime = false + maxEntitySize = 200M + bufferPipelinedData = false + } + + socket { + backlog = 10000 + } + # x AvailableProcessors + ioThreads = 8 + workerThreads = 200 + bufferSize = 16K + directBuffers = false +} + + \ No newline at end of file