From 97a54658c6433008f784545ff7713beb0775801b Mon Sep 17 00:00:00 2001 From: Andrew DeMaria Date: Tue, 7 Mar 2017 21:23:12 -0700 Subject: [PATCH] Avoid direct dependency on tomcat - Use reflection to invoke tomcat configs Signed-off-by: Andrew DeMaria --- .../libresonic/player/boot/Application.java | 54 +++++++++---------- .../player/boot/TomcatApplication.java | 34 ++++++++++++ 2 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 libresonic-main/src/main/java/org/libresonic/player/boot/TomcatApplication.java diff --git a/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java b/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java index 25d35e7b..fd1ca19b 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java +++ b/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java @@ -1,10 +1,8 @@ package org.libresonic.player.boot; import net.sf.ehcache.constructs.web.ShutdownListener; -import org.apache.catalina.Container; -import org.apache.catalina.Wrapper; -import org.apache.catalina.webresources.StandardRoot; import org.directwebremoting.servlet.DwrServlet; +import org.libresonic.player.Logger; import org.libresonic.player.filter.*; import org.libresonic.player.spring.LibresonicPropertySourceConfigurer; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -16,18 +14,19 @@ import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfigurati import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; -import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer; -import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; +import org.springframework.util.ReflectionUtils; import javax.servlet.Filter; import javax.servlet.ServletContextListener; +import java.lang.reflect.Method; + @SpringBootApplication(exclude = { JmxAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, @@ -41,6 +40,8 @@ import javax.servlet.ServletContextListener; "classpath:/libresonic-servlet.xml"}) public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer { + private static final Logger LOG = Logger.getLogger(Application.class); + /** * Registers the DWR servlet. * @@ -186,29 +187,28 @@ public class Application extends SpringBootServletInitializer implements Embedde @Override public void customize(ConfigurableEmbeddedServletContainer container) { - if (container instanceof TomcatEmbeddedServletContainerFactory) { - TomcatEmbeddedServletContainerFactory tomcatFactory = (TomcatEmbeddedServletContainerFactory) container; - tomcatFactory.addContextCustomizers((TomcatContextCustomizer) context -> { - - // Increase the size and time before eviction of the Tomcat - // cache so that resources aren't uncompressed too often. - // See https://github.com/jhipster/generator-jhipster/issues/3995 - StandardRoot resources = new StandardRoot(); - resources.setCacheMaxSize(100000); - resources.setCacheObjectMaxSize(4000); - resources.setCacheTtl(24 * 3600 * 1000); // 1 day, in milliseconds - context.setResources(resources); - - // Put Jasper in production mode so that JSP aren't recompiled - // on each request. - // See http://stackoverflow.com/questions/29653326/spring-boot-application-slow-because-of-jsp-compilation - Container jsp = context.findChild("jsp"); - if (jsp instanceof Wrapper) { - ((Wrapper)jsp).addInitParameter("development", "false"); - } - }); + // Yes, there is a good reason we do this. + // We cannot count on the tomcat classes being on the classpath which will + // happen if the war is deployed to another app server like Jetty. So, we + // ensure this class does not have any direct dependencies on any Tomcat + // specific classes. + try { + Class tomcatESCF = Class.forName("org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory"); + if(tomcatESCF.isInstance(container)) { + LOG.debug("Attempting to optimize tomcat"); + Object tomcatESCFInstance = tomcatESCF.cast(container); + Class tomcatApplicationClass = Class.forName("org.libresonic.player.boot.TomcatApplication"); + Method configure = ReflectionUtils.findMethod(tomcatApplicationClass, "configure", tomcatESCF); + configure.invoke(null, tomcatESCFInstance); + LOG.debug("Tomcat optimizations complete"); + } else { + LOG.debug("Skipping tomcat optimization as we are not running on tomcat"); + } + } catch (ClassNotFoundException e) { + LOG.debug("Skipping tomcat optimization as the tomcat classes are not available"); + } catch (Exception e) { + LOG.warn("An error happened while trying to optimize tomcat", e); } - } public static void main(String[] args) { diff --git a/libresonic-main/src/main/java/org/libresonic/player/boot/TomcatApplication.java b/libresonic-main/src/main/java/org/libresonic/player/boot/TomcatApplication.java new file mode 100644 index 00000000..c1e22296 --- /dev/null +++ b/libresonic-main/src/main/java/org/libresonic/player/boot/TomcatApplication.java @@ -0,0 +1,34 @@ +package org.libresonic.player.boot; + +import org.apache.catalina.Container; +import org.apache.catalina.Wrapper; +import org.apache.catalina.webresources.StandardRoot; +import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; + +public class TomcatApplication { + + public static void configure(TomcatEmbeddedServletContainerFactory tomcatFactory) { + + tomcatFactory.addContextCustomizers((TomcatContextCustomizer) context -> { + + // Increase the size and time before eviction of the Tomcat + // cache so that resources aren't uncompressed too often. + // See https://github.com/jhipster/generator-jhipster/issues/3995 + + StandardRoot resources = new StandardRoot(); + resources.setCacheMaxSize(100000); + resources.setCacheObjectMaxSize(4000); + resources.setCacheTtl(24 * 3600 * 1000); // 1 day, in milliseconds + context.setResources(resources); + + // Put Jasper in production mode so that JSP aren't recompiled + // on each request. + // See http://stackoverflow.com/questions/29653326/spring-boot-application-slow-because-of-jsp-compilation + Container jsp = context.findChild("jsp"); + if (jsp instanceof Wrapper) { + ((Wrapper) jsp).addInitParameter("development", "false"); + } + }); + } +}