From 7a311d484a2bb19c83fbbd59c6d691a0fba4f90b Mon Sep 17 00:00:00 2001 From: Artur Khalikov Date: Fri, 3 Aug 2018 16:34:42 +0300 Subject: [PATCH] HH-80325 add NabJerseyTestBase --- .../java/ru/hh/nab/example/ExampleConfig.java | 10 ++ .../java/ru/hh/nab/example/ExampleMain.java | 7 +- nab-hibernate/pom.xml | 6 + .../java/ru/hh/nab/starter/AppMetadata.java | 20 +-- .../ru/hh/nab/starter/NabApplication.java | 21 +++- .../hh/nab/starter/NabApplicationContext.java | 19 +-- .../java/ru/hh/nab/starter/NabProdConfig.java | 32 +---- .../filters/RequestIdLoggingFilter.java | 1 + .../starter/jersey/DefaultResourceConfig.java | 24 ++++ .../nab/starter/resource/StatusResource.java | 2 +- .../nab/starter/server/jetty/JettyServer.java | 8 +- .../server/jetty/JettyServerException.java | 2 +- .../server/jetty/JettyServerFactory.java | 2 +- .../server/jetty/JettyWebAppContext.java | 8 +- .../starter/servlet/DefaultServletConfig.java | 5 - .../hh/nab/starter/servlet/ServletConfig.java | 3 +- .../src/main/resources/nab-shared-logback.xml | 3 + nab-testbase/pom.xml | 15 ++- .../hh/nab/testbase/JettyStarterTestBase.java | 113 ----------------- .../testbase/JettyTestContainerFactory.java | 115 ++++++++++++++++++ .../ru/hh/nab/testbase/NabJerseyTestBase.java | 102 ++++++++++++++++ .../ru/hh/nab/testbase/NabTestConfig.java | 1 + nab-tests/pom.xml | 7 -- .../ru/hh/nab/starter/NabApplicationTest.java | 2 +- .../filters/RequestIdLoggingFilterTest.java | 33 +++-- .../ResourceNameLoggingFilterTest.java | 50 ++++++++ .../starter/filters/SkippableFilterTest.java | 29 ++--- 27 files changed, 391 insertions(+), 249 deletions(-) create mode 100644 nab-example/src/main/java/ru/hh/nab/example/ExampleConfig.java create mode 100644 nab-starter/src/main/java/ru/hh/nab/starter/jersey/DefaultResourceConfig.java delete mode 100644 nab-testbase/src/main/java/ru/hh/nab/testbase/JettyStarterTestBase.java create mode 100644 nab-testbase/src/main/java/ru/hh/nab/testbase/JettyTestContainerFactory.java create mode 100644 nab-testbase/src/main/java/ru/hh/nab/testbase/NabJerseyTestBase.java create mode 100644 nab-tests/src/test/java/ru/hh/nab/starter/filters/ResourceNameLoggingFilterTest.java diff --git a/nab-example/src/main/java/ru/hh/nab/example/ExampleConfig.java b/nab-example/src/main/java/ru/hh/nab/example/ExampleConfig.java new file mode 100644 index 000000000..b18ac7e5e --- /dev/null +++ b/nab-example/src/main/java/ru/hh/nab/example/ExampleConfig.java @@ -0,0 +1,10 @@ +package ru.hh.nab.example; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import ru.hh.nab.starter.NabProdConfig; + +@Configuration +@Import(NabProdConfig.class) +public class ExampleConfig { +} diff --git a/nab-example/src/main/java/ru/hh/nab/example/ExampleMain.java b/nab-example/src/main/java/ru/hh/nab/example/ExampleMain.java index c0bff65a0..3710d3615 100644 --- a/nab-example/src/main/java/ru/hh/nab/example/ExampleMain.java +++ b/nab-example/src/main/java/ru/hh/nab/example/ExampleMain.java @@ -1,14 +1,9 @@ package ru.hh.nab.example; import org.glassfish.jersey.server.ResourceConfig; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import ru.hh.nab.starter.NabApplication; -import ru.hh.nab.starter.NabProdConfig; import ru.hh.nab.starter.servlet.DefaultServletConfig; -@Configuration -@Import({NabProdConfig.class}) public class ExampleMain { public static void main(String[] args) { @@ -17,6 +12,6 @@ public static void main(String[] args) { public void registerResources(ResourceConfig resourceConfig) { resourceConfig.register(ExampleResource.class); } - }, ExampleMain.class); + }, ExampleConfig.class); } } diff --git a/nab-hibernate/pom.xml b/nab-hibernate/pom.xml index 79c620c7b..911139b1b 100644 --- a/nab-hibernate/pom.xml +++ b/nab-hibernate/pom.xml @@ -32,6 +32,12 @@ ${hibernate.version} + + javax.el + javax.el-api + 3.0.1-b06 + + org.springframework diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/AppMetadata.java b/nab-starter/src/main/java/ru/hh/nab/starter/AppMetadata.java index 663af4175..b4ad46693 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/AppMetadata.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/AppMetadata.java @@ -4,10 +4,6 @@ import org.slf4j.LoggerFactory; import java.io.InputStream; -import java.text.MessageFormat; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Properties; import java.util.concurrent.TimeUnit; @@ -17,11 +13,11 @@ public class AppMetadata { private static final String PROJECT_PROPERTIES = "/project.properties"; - private final String name; + private final String serviceName; private final String version; private final long started; - AppMetadata(String name) { + AppMetadata(String serviceName) { Properties projectProps = new Properties(); try (InputStream s = AppMetadata.class.getResourceAsStream(PROJECT_PROPERTIES)) { @@ -31,18 +27,14 @@ public class AppMetadata { LOGGER.warn("Failed to load {}, project version will be unknown, ignoring", PROJECT_PROPERTIES, e); } - this.name = name; + this.serviceName = serviceName; + version = projectProps.getProperty("project.version", "unknown"); started = System.currentTimeMillis(); } - public String getStatus() { - LocalDateTime start = Instant.ofEpochMilli(started).atZone(ZoneId.systemDefault()).toLocalDateTime(); - return MessageFormat.format("[{0}] {1} (ver. {2}) started at {3}", LocalDateTime.now(), name, version, start); - } - - public String getName() { - return name; + public String getServiceName() { + return serviceName; } public String getVersion() { 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 7e7f6c631..219ad8cec 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 @@ -8,21 +8,23 @@ import ru.hh.nab.starter.servlet.DefaultServletConfig; import ru.hh.nab.starter.servlet.ServletConfig; +import java.lang.management.ManagementFactory; import java.time.LocalDateTime; -public class NabApplication { +public final class NabApplication { private static final Logger LOGGER = LoggerFactory.getLogger(NabApplication.class); - public static ApplicationContext run(Class... primarySources) { - return run(new DefaultServletConfig(), primarySources); + public static NabApplicationContext run(Class... configurationClasses) { + return run(new DefaultServletConfig(), configurationClasses); } - public static ApplicationContext run(ServletConfig servletConfig, Class... primarySources) { + public static NabApplicationContext run(ServletConfig servletConfig, Class... configurationClasses) { configureLogger(); NabApplicationContext context = null; try { - context = new NabApplicationContext(servletConfig, primarySources); + context = new NabApplicationContext(servletConfig, configurationClasses); context.refresh(); + logStartupInfo(context); } catch (Exception e) { logErrorAndExit(e); } @@ -34,6 +36,15 @@ public static void configureLogger() { SLF4JBridgeHandler.install(); } + private static void logStartupInfo(ApplicationContext context) { + AppMetadata appMetadata = context.getBean(AppMetadata.class); + LOGGER.info("Started {} PID={} (version={})", appMetadata.getServiceName(), getCurrentPid(), appMetadata.getVersion()); + } + + private static String getCurrentPid() { + return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + } + 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())); 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 f892b9f1a..f70b3c62c 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 @@ -8,19 +8,18 @@ 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.jersey.DefaultResourceConfig; import ru.hh.nab.starter.server.jetty.JettyServer; import ru.hh.nab.starter.server.jetty.JettyServerFactory; import ru.hh.nab.starter.servlet.ServletConfig; -import java.lang.management.ManagementFactory; - -public class NabApplicationContext extends AnnotationConfigWebApplicationContext { +public final class NabApplicationContext extends AnnotationConfigWebApplicationContext { private volatile JettyServer jettyServer; private final ServletConfig servletConfig; - public NabApplicationContext(ServletConfig servletConfig, Class... primarySources) { + NabApplicationContext(ServletConfig servletConfig, Class... primarySources) { this.servletConfig = servletConfig; register(primarySources); registerShutdownHook(); @@ -30,7 +29,6 @@ public NabApplicationContext(ServletConfig servletConfig, Class... primarySou protected void finishRefresh() { super.finishRefresh(); startJettyServer(); - printStartupInfo(); } private void startJettyServer() { @@ -39,7 +37,7 @@ private void startJettyServer() { if (jettyServer == null) { FileSettings jettySettings = getBean(FileSettings.class); ThreadPool threadPool = getBean(ThreadPool.class); - ResourceConfig resourceConfig = getBean(ResourceConfig.class); + ResourceConfig resourceConfig = new DefaultResourceConfig(); this.jettyServer = JettyServerFactory.create(jettySettings, threadPool, resourceConfig, servletConfig, (contextHandler) -> { configureServletContext(contextHandler, this, servletConfig); @@ -61,13 +59,4 @@ public static void configureServletContext(ServletContextHandler handler, Applic boolean isServerRunning() { return jettyServer.isRunning(); } - - private void printStartupInfo() { - AppMetadata appMetadata = getBean(AppMetadata.class); - System.out.println(appMetadata.getStatus() + ", pid " + getCurrentPid()); - } - - private static String getCurrentPid() { - return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; - } } 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 b65d9096e..05c9f031f 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,29 +3,21 @@ 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; +import static ru.hh.nab.common.properties.PropertiesUtils.fromFilesInSettingsDir; +import ru.hh.nab.starter.jmx.MBeanExporterFactory; +import static ru.hh.nab.starter.server.cache.HttpCacheFilterFactory.createCacheFilterHolder; import javax.management.MBeanServer; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; -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 @Import({NabCommonConfig.class}) public class NabProdConfig { @@ -36,24 +28,6 @@ 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); diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/filters/RequestIdLoggingFilter.java b/nab-starter/src/main/java/ru/hh/nab/starter/filters/RequestIdLoggingFilter.java index 9c74c22f9..3ec5ea4bc 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/filters/RequestIdLoggingFilter.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/filters/RequestIdLoggingFilter.java @@ -26,6 +26,7 @@ protected void doFilterInternal(HttpServletRequest request, response.addHeader(RequestHeaders.REQUEST_ID, requestId); } MDC.setKey(REQUEST_ID_MDC_KEY, requestId); + filterChain.doFilter(request, response); } finally { diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/jersey/DefaultResourceConfig.java b/nab-starter/src/main/java/ru/hh/nab/starter/jersey/DefaultResourceConfig.java new file mode 100644 index 000000000..66d351151 --- /dev/null +++ b/nab-starter/src/main/java/ru/hh/nab/starter/jersey/DefaultResourceConfig.java @@ -0,0 +1,24 @@ +package ru.hh.nab.starter.jersey; + +import org.glassfish.jersey.server.ResourceConfig; +import ru.hh.nab.starter.filters.ResourceNameLoggingFilter; +import ru.hh.nab.starter.resource.StatsResource; +import ru.hh.nab.starter.resource.StatusResource; + +public final class DefaultResourceConfig extends ResourceConfig { + + public DefaultResourceConfig() { + register(FilteredXmlRootElementProvider.App.class); + register(FilteredXmlRootElementProvider.General.class); + register(FilteredXmlRootElementProvider.Text.class); + register(FilteredXmlElementProvider.App.class); + register(FilteredXmlElementProvider.General.class); + register(FilteredXmlElementProvider.Text.class); + register(FilteredXmlListElementProvider.App.class); + register(FilteredXmlListElementProvider.General.class); + register(FilteredXmlListElementProvider.Text.class); + register(ResourceNameLoggingFilter.class); + register(StatusResource.class); + register(StatsResource.class); + } +} diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java b/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java index 711dc0095..3fb048e45 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/resource/StatusResource.java @@ -24,7 +24,7 @@ public StatusResource(AppMetadata appMetaData) { @Produces(MediaType.TEXT_XML) public String status() { return "\n" - + "\n" + + "\n" + " " + appMetaData.getVersion() + "\n" + " " + appMetaData.getUpTimeSeconds() + "\n" + "\n"; diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServer.java b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServer.java index 3eb9e7f65..ae203c0d2 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServer.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServer.java @@ -24,11 +24,9 @@ public final class JettyServer { private final FileSettings jettySettings; private final Server server; - private final ServletContextHandler servletContextHandler; JettyServer(ThreadPool threadPool, FileSettings jettySettings, ServletContextHandler servletContextHandler) { this.jettySettings = jettySettings; - this.servletContextHandler = servletContextHandler; server = new Server(threadPool); configureConnector(); @@ -44,7 +42,7 @@ public void start() throws JettyServerException { server.start(); server.setStopAtShutdown(true); - LOGGER.info(" Jetty started on port {}", getPort()); + LOGGER.info("Jetty started on port {}", getPort()); } catch (Exception e) { stopSilently(); throw new JettyServerException("Unable to start Jetty server", e); @@ -134,8 +132,4 @@ private void stopSilently() { public Server getServer() { return server; } - - public ServletContextHandler getServletContextHandler() { - return servletContextHandler; - } } diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerException.java b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerException.java index 411f6a673..2d1a52b57 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerException.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerException.java @@ -1,6 +1,6 @@ package ru.hh.nab.starter.server.jetty; -class JettyServerException extends RuntimeException { +final class JettyServerException extends RuntimeException { JettyServerException(String message, Throwable cause) { super(message, cause); diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerFactory.java b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerFactory.java index 8eb97b303..dcfe7be8a 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerFactory.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyServerFactory.java @@ -15,7 +15,7 @@ import javax.servlet.Servlet; -public class JettyServerFactory { +public final class JettyServerFactory { public static JettyServer create(FileSettings fileSettings, ThreadPool threadPool, diff --git a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyWebAppContext.java b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyWebAppContext.java index 09dfbb989..a4e576507 100644 --- a/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyWebAppContext.java +++ b/nab-starter/src/main/java/ru/hh/nab/starter/server/jetty/JettyWebAppContext.java @@ -9,9 +9,9 @@ import java.util.ArrayList; import java.util.List; -public final class JettyWebAppContext extends WebAppContext { +final class JettyWebAppContext extends WebAppContext { - public JettyWebAppContext(JerseyServletContextInitializer servletContextInitializer, boolean sessionEnabled) { + JettyWebAppContext(JerseyServletContextInitializer servletContextInitializer, boolean sessionEnabled) { super(null, null, null, null, null, null, sessionEnabled ? SESSIONS: 0); @@ -20,7 +20,7 @@ public JettyWebAppContext(JerseyServletContextInitializer servletContextInitiali setConfigurations(configurations.toArray(new Configuration[0])); } - private static class ServletContextInitializerConfiguration extends AbstractConfiguration { + private static final class ServletContextInitializerConfiguration extends AbstractConfiguration { private final JerseyServletContextInitializer servletContextInitializer; ServletContextInitializerConfiguration(JerseyServletContextInitializer servletContextInitializer) { @@ -32,7 +32,7 @@ public void configure(WebAppContext context) { context.addBean(new InitializingLifeCycle(context), true); } - class InitializingLifeCycle extends AbstractLifeCycle { + final class InitializingLifeCycle extends AbstractLifeCycle { private final WebAppContext context; InitializingLifeCycle(WebAppContext context) { 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 6215da7bc..117516424 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 @@ -2,7 +2,6 @@ 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; @@ -24,8 +23,4 @@ public void configureServletContext(ServletContextHandler servletContextHandler, } } } - - @Override - public void registerResources(ResourceConfig resourceConfig) { - } } 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 d618467f3..2311d4652 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,6 @@ default String getServletMapping() { void configureServletContext(ServletContextHandler servletContextHandler, ApplicationContext applicationContext); - void registerResources(ResourceConfig resourceConfig); + default void registerResources(ResourceConfig resourceConfig) { + } } diff --git a/nab-starter/src/main/resources/nab-shared-logback.xml b/nab-starter/src/main/resources/nab-shared-logback.xml index 7f949190e..d2a6bb635 100644 --- a/nab-starter/src/main/resources/nab-shared-logback.xml +++ b/nab-starter/src/main/resources/nab-shared-logback.xml @@ -80,6 +80,9 @@ + + + diff --git a/nab-testbase/pom.xml b/nab-testbase/pom.xml index 56046ad31..8d7137570 100644 --- a/nab-testbase/pom.xml +++ b/nab-testbase/pom.xml @@ -43,20 +43,25 @@ - javax.el - javax.el-api - 3.0.1-b06 + junit + junit - junit - junit + org.mockito + mockito-core org.springframework spring-test + + + org.glassfish.jersey.test-framework + jersey-test-framework-core + ${jersey.version} + diff --git a/nab-testbase/src/main/java/ru/hh/nab/testbase/JettyStarterTestBase.java b/nab-testbase/src/main/java/ru/hh/nab/testbase/JettyStarterTestBase.java deleted file mode 100644 index 349316f66..000000000 --- a/nab-testbase/src/main/java/ru/hh/nab/testbase/JettyStarterTestBase.java +++ /dev/null @@ -1,113 +0,0 @@ -package ru.hh.nab.testbase; - -import java.util.concurrent.ConcurrentHashMap; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.DefaultRoutePlanner; -import org.apache.http.protocol.HttpContext; -import org.junit.Before; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; -import org.springframework.test.context.web.WebAppConfiguration; -import ru.hh.nab.starter.servlet.DefaultServletConfig; -import ru.hh.nab.starter.NabApplication; -import ru.hh.nab.starter.servlet.ServletConfig; - -import java.util.concurrent.ConcurrentMap; - -/** - * Launches Jetty instance with application context provided by {@link AbstractJUnit4SpringContextTests} - * and servlet config provided by {@link #servletConfig()} on a random port before test methods start to execute. - * For some examples see nab-starter-tests module. - */ -@WebAppConfiguration -public abstract class JettyStarterTestBase extends AbstractJUnit4SpringContextTests { - - private static final Logger LOGGER = LoggerFactory.getLogger(JettyStarterTestBase.class); - private static final ConcurrentMap, Instance> INSTANCES = new ConcurrentHashMap<>(); - - @Before - public void setUp() { - Class aClass = findMostGenericBaseClass(getClass()); - - INSTANCES.computeIfAbsent(aClass, key -> { - try { - int port = NabApplication.startJettyServer(applicationContext, servletConfig()); - LOGGER.info("Test server is bound to port {}", port); - return new Instance(port); - } catch (Exception e) { - throw new RuntimeException("Exception during Jetty startup", e); - } - }); - } - - private static Class findMostGenericBaseClass(Class clazz) { - Class current = clazz; - while (true) { - try { - current.getDeclaredMethod("servletConfig"); - return current; - } catch (NoSuchMethodException e) { - current = current.getSuperclass().asSubclass(JettyStarterTestBase.class); - } - } - } - - /** - * Override to provide custom servlet config for Jetty instance - */ - protected ServletConfig servletConfig() { - return new DefaultServletConfig(); - } - - private Instance instance() { - return INSTANCES.get(findMostGenericBaseClass(getClass())); - } - - protected String baseUrl() { - return instance().baseUrl; - } - - protected int port() { - return instance().port; - } - - public static class Instance { - final String baseUrl; - final int port; - - Instance(int port) { - this.port = port; - this.baseUrl = String.format("http://127.0.0.1:%d", port); - } - } - - protected CloseableHttpClient httpClient() { - return HttpClientBuilder.create() - .setDefaultRequestConfig( - RequestConfig.custom().setRedirectsEnabled(false).setCookieSpec(CookieSpecs.STANDARD).build()) - .setRoutePlanner(new DefaultRoutePlannerImpl(port())) - .build(); - } - - private static class DefaultRoutePlannerImpl extends DefaultRoutePlanner { - private final HttpHost defaultHost; - - DefaultRoutePlannerImpl(final int port) { - super(null); - this.defaultHost = new HttpHost("127.0.0.1", port); - } - - @Override - public HttpRoute determineRoute(final HttpHost target, final HttpRequest request, final HttpContext context) throws HttpException { - return super.determineRoute(target == null ? defaultHost : target, request, context); - } - } -} diff --git a/nab-testbase/src/main/java/ru/hh/nab/testbase/JettyTestContainerFactory.java b/nab-testbase/src/main/java/ru/hh/nab/testbase/JettyTestContainerFactory.java new file mode 100644 index 000000000..382200c83 --- /dev/null +++ b/nab-testbase/src/main/java/ru/hh/nab/testbase/JettyTestContainerFactory.java @@ -0,0 +1,115 @@ +package ru.hh.nab.testbase; + +import org.eclipse.jetty.util.thread.ThreadPool; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.test.DeploymentContext; +import org.glassfish.jersey.test.spi.TestContainer; +import org.glassfish.jersey.test.spi.TestContainerException; +import org.glassfish.jersey.test.spi.TestContainerFactory; +import org.glassfish.jersey.test.spi.TestHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.context.WebApplicationContext; +import ru.hh.nab.common.properties.FileSettings; +import static ru.hh.nab.starter.NabApplicationContext.configureServletContext; +import ru.hh.nab.starter.server.jetty.JettyServer; +import ru.hh.nab.starter.server.jetty.JettyServerFactory; +import ru.hh.nab.starter.servlet.ServletConfig; + +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +final class JettyTestContainerFactory implements TestContainerFactory { + + private static final ConcurrentMap, TestContainer> INSTANCES = new ConcurrentHashMap<>(); + + private final WebApplicationContext springContext; + private final ServletConfig servletConfig; + private final Class testClass; + + JettyTestContainerFactory(WebApplicationContext context, ServletConfig servletConfig, Class testClass) { + this.springContext = context; + this.servletConfig = servletConfig; + this.testClass = testClass; + } + + @Override + public TestContainer create(URI baseUri, DeploymentContext deploymentContext) { + Class baseClass = findMostGenericBaseClass(testClass); + return INSTANCES.computeIfAbsent(baseClass, + key -> new JettyServerTestContainer(baseUri, servletConfig, deploymentContext, springContext)); + } + + private static Class findMostGenericBaseClass(Class clazz) { + Class current = clazz; + while (true) { + try { + current.getDeclaredMethod("getServletConfig"); + return current; + } catch (NoSuchMethodException e) { + current = current.getSuperclass().asSubclass(NabJerseyTestBase.class); + } + } + } + + private static class JettyServerTestContainer implements TestContainer { + private static final Logger LOGGER = LoggerFactory.getLogger(JettyServerTestContainer.class); + + private final JettyServer jettyServer; + private URI baseUri; + + JettyServerTestContainer(URI baseUri, ServletConfig servletConfig, DeploymentContext deploymentContext, WebApplicationContext springContext) { + final URI base = UriBuilder.fromUri(baseUri).path(deploymentContext.getContextPath()).build(); + + if (!"/".equals(base.getRawPath())) { + throw new TestContainerException( + String.format("Cannot deploy on %s. Jetty HTTP container only supports deployment on root path.", base.getRawPath())); + } + + this.baseUri = base; + + LOGGER.info("Creating JettyServerTestContainer configured at the base URI " + TestHelper.zeroPortToAvailablePort(baseUri)); + + final FileSettings fileSettings = springContext.getBean(FileSettings.class); + final ThreadPool threadPool = springContext.getBean(ThreadPool.class); + + jettyServer = JettyServerFactory.create( + fileSettings, + threadPool, + deploymentContext.getResourceConfig(), + servletConfig, + (contextHandler) -> configureServletContext(contextHandler, springContext, servletConfig)); + } + + @Override + public ClientConfig getClientConfig() { + return null; + } + + @Override + public URI getBaseUri() { + return baseUri; + } + + @Override + public void start() { + if (jettyServer.isRunning()) { + LOGGER.warn("Ignoring start request - JettyServerTestContainer is already started."); + return; + } + + LOGGER.info("Starting JettyServerTestContainer..."); + + jettyServer.start(); + baseUri = UriBuilder.fromUri(baseUri).port(jettyServer.getPort()).build(); + + LOGGER.info("Started JettyServerTestContainer at the base URI " + baseUri); + } + + @Override + public void stop() { + } + } +} diff --git a/nab-testbase/src/main/java/ru/hh/nab/testbase/NabJerseyTestBase.java b/nab-testbase/src/main/java/ru/hh/nab/testbase/NabJerseyTestBase.java new file mode 100644 index 000000000..048addc0b --- /dev/null +++ b/nab-testbase/src/main/java/ru/hh/nab/testbase/NabJerseyTestBase.java @@ -0,0 +1,102 @@ +package ru.hh.nab.testbase; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.core.Response; +import static javax.ws.rs.core.Response.Status.OK; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.DeploymentContext; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.ServletDeploymentContext; +import org.glassfish.jersey.test.spi.TestContainerException; +import org.glassfish.jersey.test.spi.TestContainerFactory; +import static org.junit.Assert.assertEquals; +import org.springframework.context.annotation.AnnotationConfigRegistry; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import static ru.hh.nab.starter.NabApplication.configureLogger; +import ru.hh.nab.starter.jersey.DefaultResourceConfig; +import ru.hh.nab.starter.servlet.DefaultServletConfig; +import ru.hh.nab.starter.servlet.ServletConfig; + +public abstract class NabJerseyTestBase extends JerseyTest { + + private static AnnotationConfigWebApplicationContext context; + private static final Object MUTEX = new Object(); + + @Override + protected DeploymentContext configureDeployment() { + configureLogger(); + + synchronized (MUTEX) { + if (context == null) { + context = configureSpringContext(); + } + } + + DeploymentContext context = ServletDeploymentContext.builder(configure()).build(); + context.getResourceConfig().property("contextConfig", context); + return context; + } + + @Override + protected TestContainerFactory getTestContainerFactory() throws TestContainerException { + return new JettyTestContainerFactory(context, getServletConfig(), getClass()); + } + + @Override + protected ResourceConfig configure() { + return new DefaultResourceConfig(); + } + + protected void assertGet(String url, String expectedResponse) { + Response response = target(url).request().get(); + + assertEquals(OK.getStatusCode(), response.getStatus()); + assertEquals(expectedResponse, response.readEntity(String.class)); + } + + protected void assertGet(Invocation.Builder request, String expectedResponse) { + Response response = request.get(); + + assertEquals(OK.getStatusCode(), response.getStatus()); + assertEquals(expectedResponse, response.readEntity(String.class)); + } + + protected Invocation.Builder createRequest(String url) { + return target(url).request(); + } + + protected Response executeGet(String url) { + return createRequest(url).get(); + } + + protected void configureSpringContext(AnnotationConfigRegistry springContext) { + } + + private AnnotationConfigWebApplicationContext configureSpringContext() { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.register(NabTestConfig.class); + configureSpringContext(context); + context.refresh(); + return context; + } + + /** + * Override to provide custom servlet config for Jetty instance + */ + protected ServletConfig getServletConfig() { + return new DefaultServletConfig(); + } + + protected WebApplicationContext getContext() { + return context; + } + + protected T getBean(Class requiredType) { + return context.getBean(requiredType); + } + + protected T getBean(String beanName, Class requiredType) { + return context.getBean(beanName, requiredType); + } +} diff --git a/nab-testbase/src/main/java/ru/hh/nab/testbase/NabTestConfig.java b/nab-testbase/src/main/java/ru/hh/nab/testbase/NabTestConfig.java index 9583c2f49..58d87e789 100644 --- a/nab-testbase/src/main/java/ru/hh/nab/testbase/NabTestConfig.java +++ b/nab-testbase/src/main/java/ru/hh/nab/testbase/NabTestConfig.java @@ -18,6 +18,7 @@ public class NabTestConfig { FileSettings fileSettings() { Properties properties = new Properties(); properties.setProperty("jetty.port", "0"); + properties.setProperty("jetty.maxThreads", "16"); properties.setProperty("serviceName", TEST_SERVICE_NAME); properties.setProperty("datacenter", TEST_DATACENTER); return new FileSettings(properties); diff --git a/nab-tests/pom.xml b/nab-tests/pom.xml index c3411c78a..986b1eb3b 100644 --- a/nab-tests/pom.xml +++ b/nab-tests/pom.xml @@ -25,13 +25,6 @@ ru.hh.nab nab-starter ${project.version} - test-jar - test - - - - org.mockito - mockito-core test diff --git a/nab-tests/src/test/java/ru/hh/nab/starter/NabApplicationTest.java b/nab-tests/src/test/java/ru/hh/nab/starter/NabApplicationTest.java index 5b26928ea..ee8a4e467 100644 --- a/nab-tests/src/test/java/ru/hh/nab/starter/NabApplicationTest.java +++ b/nab-tests/src/test/java/ru/hh/nab/starter/NabApplicationTest.java @@ -9,7 +9,7 @@ public class NabApplicationTest { @Test public void runShouldStartJetty() { - NabApplicationContext context = (NabApplicationContext) NabApplication.run(NabTestConfig.class); + NabApplicationContext context = NabApplication.run(NabTestConfig.class); assertTrue(context.isServerRunning()); assertEquals(NabTestConfig.TEST_SERVICE_NAME, context.getBean("serviceName")); diff --git a/nab-tests/src/test/java/ru/hh/nab/starter/filters/RequestIdLoggingFilterTest.java b/nab-tests/src/test/java/ru/hh/nab/starter/filters/RequestIdLoggingFilterTest.java index 8eae16b9e..343ddeee2 100644 --- a/nab-tests/src/test/java/ru/hh/nab/starter/filters/RequestIdLoggingFilterTest.java +++ b/nab-tests/src/test/java/ru/hh/nab/starter/filters/RequestIdLoggingFilterTest.java @@ -1,33 +1,32 @@ package ru.hh.nab.starter.filters; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.RequestBuilder; +import static javax.ws.rs.core.Response.Status.OK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Test; -import org.springframework.test.context.ContextConfiguration; -import ru.hh.nab.testbase.NabTestConfig; -import ru.hh.nab.testbase.JettyStarterTestBase; +import ru.hh.nab.testbase.NabJerseyTestBase; -@ContextConfiguration(classes = {NabTestConfig.class}) -public class RequestIdLoggingFilterTest extends JettyStarterTestBase { +import javax.ws.rs.core.Response; - @Test - public void testRequestId() throws Exception { - String testRequestId = "123"; +public class RequestIdLoggingFilterTest extends NabJerseyTestBase { - RequestBuilder requestBuilder = RequestBuilder.get("/status"); - requestBuilder.addHeader(RequestHeaders.REQUEST_ID, testRequestId); + @Test + public void testRequestId() { + final String testRequestId = "123"; - HttpResponse response = httpClient().execute(requestBuilder.build()); + Response response = createRequest("status") + .header(RequestHeaders.REQUEST_ID, testRequestId) + .get(); - assertEquals(testRequestId, response.getFirstHeader(RequestHeaders.REQUEST_ID).getValue()); + assertEquals(OK.getStatusCode(), response.getStatus()); + assertEquals(testRequestId, response.getHeaderString(RequestHeaders.REQUEST_ID)); } @Test - public void testNoRequestId() throws Exception { - HttpResponse response = httpClient().execute(RequestBuilder.get("/status").build()); + public void testNoRequestId() { + Response response = executeGet("status"); - assertNull(response.getFirstHeader(RequestHeaders.REQUEST_ID)); + assertEquals(OK.getStatusCode(), response.getStatus()); + assertNull(response.getHeaderString(RequestHeaders.REQUEST_ID)); } } diff --git a/nab-tests/src/test/java/ru/hh/nab/starter/filters/ResourceNameLoggingFilterTest.java b/nab-tests/src/test/java/ru/hh/nab/starter/filters/ResourceNameLoggingFilterTest.java new file mode 100644 index 000000000..0da6d40ad --- /dev/null +++ b/nab-tests/src/test/java/ru/hh/nab/starter/filters/ResourceNameLoggingFilterTest.java @@ -0,0 +1,50 @@ +package ru.hh.nab.starter.filters; + +import static javax.ws.rs.core.Response.Status.OK; +import org.glassfish.jersey.server.ResourceConfig; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import ru.hh.nab.common.mdc.MDC; +import ru.hh.nab.starter.servlet.DefaultServletConfig; +import ru.hh.nab.starter.servlet.ServletConfig; +import ru.hh.nab.testbase.NabJerseyTestBase; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +public class ResourceNameLoggingFilterTest extends NabJerseyTestBase { + + @Override + protected ServletConfig getServletConfig() { + return new DefaultServletConfig() { + @Override + public void registerResources(ResourceConfig resourceConfig) { + resourceConfig.register(TestResource.class); + } + }; + } + + @Test + public void testResourceName() { + assertFalse(MDC.getController().isPresent()); + + Response response = executeGet("test"); + + assertEquals(OK.getStatusCode(), response.getStatus()); + + assertEquals("TestResource.test", response.readEntity(String.class)); + assertFalse(MDC.getController().isPresent()); + } + + @Path("/test") + public static class TestResource { + @GET + public String test() { + assertTrue(MDC.getController().isPresent()); + return MDC.getController().get(); + } + } +} diff --git a/nab-tests/src/test/java/ru/hh/nab/starter/filters/SkippableFilterTest.java b/nab-tests/src/test/java/ru/hh/nab/starter/filters/SkippableFilterTest.java index 8adc3ebc8..750ec3669 100644 --- a/nab-tests/src/test/java/ru/hh/nab/starter/filters/SkippableFilterTest.java +++ b/nab-tests/src/test/java/ru/hh/nab/starter/filters/SkippableFilterTest.java @@ -1,30 +1,25 @@ package ru.hh.nab.starter.filters; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.RequestBuilder; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.springframework.context.ApplicationContext; -import org.springframework.test.context.ContextConfiguration; -import ru.hh.nab.testbase.NabTestConfig; import ru.hh.nab.starter.servlet.DefaultServletConfig; import ru.hh.nab.starter.servlet.ServletConfig; -import ru.hh.nab.testbase.JettyStarterTestBase; +import ru.hh.nab.testbase.NabJerseyTestBase; import javax.servlet.DispatcherType; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; import java.io.IOException; import java.util.EnumSet; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -@ContextConfiguration(classes = {NabTestConfig.class}) -public class SkippableFilterTest extends JettyStarterTestBase { +public class SkippableFilterTest extends NabJerseyTestBase { public static class AddHeaderSkippableFilter extends SkippableFilter { public AddHeaderSkippableFilter() {} @@ -38,7 +33,7 @@ protected void performFilter(HttpServletRequest request, HttpServletResponse res } @Override - protected ServletConfig servletConfig() { + protected ServletConfig getServletConfig() { return new DefaultServletConfig() { @Override public void configureServletContext(ServletContextHandler servletContextHandler, ApplicationContext applicationContext) { @@ -50,16 +45,16 @@ public void configureServletContext(ServletContextHandler servletContextHandler, } @Test - public void testSkippableFilterExclusions() throws IOException { - HttpResponse response = httpClient().execute(RequestBuilder.get("/status").build()); + public void testSkippableFilterExclusions() { + Response response = executeGet("status"); - assertNull(response.getFirstHeader("x-passed-filter")); + assertNull(response.getHeaderString("x-passed-filter")); } @Test - public void testSkippableFilterNoExclusions() throws IOException { - HttpResponse response = httpClient().execute(RequestBuilder.get("/status-not").build()); + public void testSkippableFilterNoExclusions() { + Response response = executeGet("status-not"); - assertEquals(response.getFirstHeader("x-passed-filter").getValue(), "true"); + assertEquals(response.getHeaderString("x-passed-filter"), "true"); } }