diff --git a/nab-starter/pom.xml b/nab-starter/pom.xml index 0276697ac..769980915 100644 --- a/nab-starter/pom.xml +++ b/nab-starter/pom.xml @@ -14,7 +14,6 @@ nuts'n'bolts application starter - 2.27 9.4.6.v20170531 2.6.6 @@ -33,6 +32,11 @@ + + javax.servlet + javax.servlet-api + 3.1.0 + org.glassfish.jersey.ext jersey-spring4 @@ -94,6 +98,11 @@ jetty-servlet ${jetty.version} + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + org.eclipse.jetty jetty-jmx diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/NabApplication.java b/nab-starter/src/main/java/ru/hh/nab/starter/NabApplication.java index 13ca69459..7e7f6c631 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/NabApplication.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/NabApplication.java @@ -1,23 +1,14 @@ package ru.hh.nab.starter; import static java.text.MessageFormat.format; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.util.thread.ThreadPool; -import org.glassfish.jersey.servlet.ServletContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.context.ApplicationContext; -import ru.hh.nab.common.properties.FileSettings; -import ru.hh.nab.starter.jetty.JettyFactory; import ru.hh.nab.starter.servlet.DefaultServletConfig; import ru.hh.nab.starter.servlet.ServletConfig; -import java.lang.management.ManagementFactory; import java.time.LocalDateTime; -import java.util.Arrays; public class NabApplication { private static final Logger LOGGER = LoggerFactory.getLogger(NabApplication.class); @@ -27,66 +18,25 @@ public static ApplicationContext run(Class... primarySources) { } public static ApplicationContext run(ServletConfig servletConfig, Class... primarySources) { - registerSlf4JHandler(); + configureLogger(); NabApplicationContext context = null; try { - context = createApplicationContext(primarySources); - int port = startJettyServer(context, servletConfig); - printApplicationStatus(context, port); + context = new NabApplicationContext(servletConfig, primarySources); + context.refresh(); } catch (Exception e) { logErrorAndExit(e); } return context; } - private static NabApplicationContext createApplicationContext(Class... primarySources) { - final NabApplicationContext context = new NabApplicationContext(); - context.register(primarySources); - context.refresh(); - context.registerShutdownHook(); - return context; - } - - public static int startJettyServer(ApplicationContext context, ServletConfig servletConfig) throws Exception { - final FileSettings settings = context.getBean(FileSettings.class); - final ServletContainer mainServlet = new ServletContainer(servletConfig.createResourceConfig(context)); - final Server jettyServer = JettyFactory.create(settings, context.getBean(ThreadPool.class), mainServlet, servletConfig.getServletMapping()); - servletConfig.configureServletContext((ServletContextHandler) jettyServer.getHandler(), context); - int port = startJettyServer(jettyServer); - - // todo: integrate start of jetty server into spring life cycle - if (context instanceof NabApplicationContext) { - NabApplicationContext nabApplicationContext = (NabApplicationContext) context; - nabApplicationContext.setServer(jettyServer); - } - - return port; - } - - static void registerSlf4JHandler() { + public static void configureLogger() { SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); } - private static int startJettyServer(Server jettyServer) throws Exception { - jettyServer.start(); - return ((ServerConnector) Arrays.stream(jettyServer.getConnectors()) - .filter(a -> a instanceof ServerConnector).findFirst().get()) - .getLocalPort(); - } - - private static void printApplicationStatus(ApplicationContext context, int port) { - AppMetadata appMetadata = context.getBean(AppMetadata.class); - System.out.println(appMetadata.getStatus() + ", pid " + getCurrentPid() + ", listening to port " + port); - } - private static void logErrorAndExit(Exception e) { LOGGER.error("Failed to start, shutting down", e); System.err.println(format("[{0}] Failed to start, shutting down: {1}", LocalDateTime.now(), e.getMessage())); System.exit(1); } - - private static String getCurrentPid() { - return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; - } } diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/NabApplicationContext.java b/nab-starter/src/main/java/ru/hh/nab/starter/NabApplicationContext.java index 5c9a74cc5..f892b9f1a 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/NabApplicationContext.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/NabApplicationContext.java @@ -1,17 +1,73 @@ package ru.hh.nab.starter; -import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.glassfish.jersey.server.ResourceConfig; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextException; +import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import ru.hh.nab.common.properties.FileSettings; +import ru.hh.nab.starter.server.jetty.JettyServer; +import ru.hh.nab.starter.server.jetty.JettyServerFactory; +import ru.hh.nab.starter.servlet.ServletConfig; -class NabApplicationContext extends AnnotationConfigWebApplicationContext { +import java.lang.management.ManagementFactory; - private volatile Server server; +public class NabApplicationContext extends AnnotationConfigWebApplicationContext { + + private volatile JettyServer jettyServer; + + private final ServletConfig servletConfig; + + public NabApplicationContext(ServletConfig servletConfig, Class... primarySources) { + this.servletConfig = servletConfig; + register(primarySources); + registerShutdownHook(); + } + + @Override + protected void finishRefresh() { + super.finishRefresh(); + startJettyServer(); + printStartupInfo(); + } + + private void startJettyServer() { + JettyServer jettyServer = this.jettyServer; + try { + if (jettyServer == null) { + FileSettings jettySettings = getBean(FileSettings.class); + ThreadPool threadPool = getBean(ThreadPool.class); + ResourceConfig resourceConfig = getBean(ResourceConfig.class); + + this.jettyServer = JettyServerFactory.create(jettySettings, threadPool, resourceConfig, servletConfig, (contextHandler) -> { + configureServletContext(contextHandler, this, servletConfig); + setServletContext(contextHandler.getServletContext()); + }); + + this.jettyServer.start(); + } + } catch (Throwable t) { + throw new ApplicationContextException("Unable to start application server", t); + } + } + + public static void configureServletContext(ServletContextHandler handler, ApplicationContext applicationContext, ServletConfig servletConfig) { + handler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext); + servletConfig.configureServletContext(handler, applicationContext); + } boolean isServerRunning() { - return server.isRunning(); + return jettyServer.isRunning(); + } + + private void printStartupInfo() { + AppMetadata appMetadata = getBean(AppMetadata.class); + System.out.println(appMetadata.getStatus() + ", pid " + getCurrentPid()); } - void setServer(Server server) { - this.server = server; + private static String getCurrentPid() { + return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; } } diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/NabCommonConfig.java b/nab-starter/src/main/java/ru/hh/nab/starter/NabCommonConfig.java index a4807b0a9..774c180f2 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/NabCommonConfig.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/NabCommonConfig.java @@ -6,7 +6,7 @@ import org.springframework.context.annotation.Configuration; import ru.hh.nab.common.executor.ScheduledExecutor; import ru.hh.nab.common.properties.FileSettings; -import static ru.hh.nab.starter.jetty.JettyFactory.createJettyThreadPool; +import static ru.hh.nab.starter.server.jetty.JettyServerFactory.createJettyThreadPool; import java.util.concurrent.ScheduledExecutorService; diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/NabProdConfig.java b/nab-starter/src/main/java/ru/hh/nab/starter/NabProdConfig.java index dc2e3aaa1..b65d9096e 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/NabProdConfig.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/NabProdConfig.java @@ -3,12 +3,17 @@ import com.timgroup.statsd.NonBlockingStatsDClient; import com.timgroup.statsd.StatsDClient; import org.eclipse.jetty.servlet.FilterHolder; +import org.glassfish.jersey.server.ResourceConfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.jmx.export.MBeanExporter; import org.springframework.jmx.support.MBeanServerFactoryBean; import ru.hh.metrics.StatsDSender; +import ru.hh.nab.starter.filters.ResourceNameLoggingFilter; +import ru.hh.nab.starter.jersey.FilteredXmlElementProvider; +import ru.hh.nab.starter.jersey.FilteredXmlListElementProvider; +import ru.hh.nab.starter.jersey.FilteredXmlRootElementProvider; import ru.hh.nab.starter.jmx.MBeanExporterFactory; import ru.hh.nab.common.properties.FileSettings; @@ -16,7 +21,9 @@ import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; -import static ru.hh.nab.starter.jetty.HttpCacheFilterFactory.createCacheFilterHolder; +import ru.hh.nab.starter.resource.StatsResource; +import ru.hh.nab.starter.resource.StatusResource; +import static ru.hh.nab.starter.server.cache.HttpCacheFilterFactory.createCacheFilterHolder; import static ru.hh.nab.common.properties.PropertiesUtils.fromFilesInSettingsDir; @Configuration @@ -29,6 +36,24 @@ FileSettings fileSettings() throws Exception { return new FileSettings(properties); } + @Bean + ResourceConfig resourceConfig() { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(FilteredXmlRootElementProvider.App.class); + resourceConfig.register(FilteredXmlRootElementProvider.General.class); + resourceConfig.register(FilteredXmlRootElementProvider.Text.class); + resourceConfig.register(FilteredXmlElementProvider.App.class); + resourceConfig.register(FilteredXmlElementProvider.General.class); + resourceConfig.register(FilteredXmlElementProvider.Text.class); + resourceConfig.register(FilteredXmlListElementProvider.App.class); + resourceConfig.register(FilteredXmlListElementProvider.General.class); + resourceConfig.register(FilteredXmlListElementProvider.Text.class); + resourceConfig.register(new ResourceNameLoggingFilter()); + resourceConfig.register(StatusResource.class); + resourceConfig.register(StatsResource.class); + return resourceConfig; + } + @Bean StatsDClient statsDClient() { return new NonBlockingStatsDClient(null, "localhost", 8125, 10000); @@ -58,14 +83,4 @@ MBeanServerFactoryBean mBeanServerFactoryBean() { MBeanExporter mBeanExporter(FileSettings settings, MBeanServer mbeanServer) { return MBeanExporterFactory.create(settings, mbeanServer); } - - @Bean - StatusResource statusResource(AppMetadata appMetadata) { - return new StatusResource(appMetadata); - } - - @Bean - StatsResource statsResource() { - return new StatsResource(); - } } diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/StatsResource.java b/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatsResource.java similarity index 73% rename from nab-starter/src/main/java/ru/hh/nab/starter/StatsResource.java rename to nab-starter/src/main/java/ru/hh/nab/starter/resource/StatsResource.java index b2406a981..5c95faa24 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/StatsResource.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatsResource.java @@ -1,10 +1,12 @@ -package ru.hh.nab.starter; +package ru.hh.nab.starter.resource; +import javax.inject.Singleton; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path("/stats") +@Singleton public class StatsResource { @GET @Produces("text/csv") diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/StatusResource.java b/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java similarity index 80% rename from nab-starter/src/main/java/ru/hh/nab/starter/StatusResource.java rename to nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java index 4b669989c..711dc0095 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/StatusResource.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java @@ -1,15 +1,21 @@ -package ru.hh.nab.starter; +package ru.hh.nab.starter.resource; +import ru.hh.nab.starter.AppMetadata; + +import javax.inject.Inject; +import javax.inject.Singleton; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/status") +@Singleton public class StatusResource { private final AppMetadata appMetaData; + @Inject public StatusResource(AppMetadata appMetaData) { this.appMetaData = appMetaData; } diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/servlet/DefaultServletConfig.java b/nab-starter/src/main/java/ru/hh/nab/starter/servlet/DefaultServletConfig.java index c8d59efbf..6215da7bc 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/servlet/DefaultServletConfig.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/servlet/DefaultServletConfig.java @@ -1,17 +1,14 @@ package ru.hh.nab.starter.servlet; -import java.util.EnumSet; -import javax.servlet.DispatcherType; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.glassfish.jersey.server.ResourceConfig; import org.springframework.context.ApplicationContext; import org.springframework.web.context.request.RequestContextListener; import ru.hh.nab.starter.filters.RequestIdLoggingFilter; -import ru.hh.nab.starter.filters.ResourceNameLoggingFilter; -import ru.hh.nab.starter.jersey.FilteredXmlElementProvider; -import ru.hh.nab.starter.jersey.FilteredXmlListElementProvider; -import ru.hh.nab.starter.jersey.FilteredXmlRootElementProvider; + +import javax.servlet.DispatcherType; +import java.util.EnumSet; public class DefaultServletConfig implements ServletConfig { @@ -29,22 +26,6 @@ public void configureServletContext(ServletContextHandler servletContextHandler, } @Override - public ResourceConfig createResourceConfig(ApplicationContext context) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.property("contextConfig", context); - context.getBeansWithAnnotation(javax.ws.rs.Path.class) - .forEach((name, resource) -> resourceConfig.register(resource)); - - resourceConfig.register(FilteredXmlRootElementProvider.App.class); - resourceConfig.register(FilteredXmlRootElementProvider.General.class); - resourceConfig.register(FilteredXmlRootElementProvider.Text.class); - resourceConfig.register(FilteredXmlElementProvider.App.class); - resourceConfig.register(FilteredXmlElementProvider.General.class); - resourceConfig.register(FilteredXmlElementProvider.Text.class); - resourceConfig.register(FilteredXmlListElementProvider.App.class); - resourceConfig.register(FilteredXmlListElementProvider.General.class); - resourceConfig.register(FilteredXmlListElementProvider.Text.class); - resourceConfig.register(new ResourceNameLoggingFilter()); - return resourceConfig; + public void registerResources(ResourceConfig resourceConfig) { } } diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/servlet/JerseyServletContextInitializer.java b/nab-starter/src/main/java/ru/hh/nab/starter/servlet/JerseyServletContextInitializer.java new file mode 100644 index 000000000..d122eed81 --- /dev/null +++ b/nab-starter/src/main/java/ru/hh/nab/starter/servlet/JerseyServletContextInitializer.java @@ -0,0 +1,9 @@ +package ru.hh.nab.starter.servlet; + +import org.eclipse.jetty.servlet.ServletContextHandler; + +@FunctionalInterface +public interface JerseyServletContextInitializer { + + void onStartup(ServletContextHandler contextHandler); +} diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/servlet/ServletConfig.java b/nab-starter/src/main/java/ru/hh/nab/starter/servlet/ServletConfig.java index 9d478b97d..d618467f3 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/servlet/ServletConfig.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/servlet/ServletConfig.java @@ -12,5 +12,5 @@ default String getServletMapping() { void configureServletContext(ServletContextHandler servletContextHandler, ApplicationContext applicationContext); - ResourceConfig createResourceConfig(ApplicationContext context); + void registerResources(ResourceConfig resourceConfig); } diff --git a/pom.xml b/pom.xml index 19c4a02ca..9f716e4e4 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ 5.0.3.RELEASE + 2.27 5.2.10.Final 42.2.2 0.16