diff --git a/airsonic-main/pom.xml b/airsonic-main/pom.xml
index cd6d1fd9..3aad5905 100755
--- a/airsonic-main/pom.xml
+++ b/airsonic-main/pom.xml
@@ -638,6 +638,27 @@
net.nicoulaj.maven.plugins
checksum-maven-plugin
+
+ org.eclipse.jetty
+ jetty-jspc-maven-plugin
+ 9.4.19.v20190610
+
+
+ jspc
+
+ jspc
+
+ compile
+
+
+ false
+ ${project.build.outputDirectory}/precompiled-jsp-web.xml
+
+
+
+
diff --git a/airsonic-main/src/main/java/org/airsonic/player/security/GlobalSecurityConfig.java b/airsonic-main/src/main/java/org/airsonic/player/security/GlobalSecurityConfig.java
index 7415b568..c5297384 100644
--- a/airsonic-main/src/main/java/org/airsonic/player/security/GlobalSecurityConfig.java
+++ b/airsonic-main/src/main/java/org/airsonic/player/security/GlobalSecurityConfig.java
@@ -139,7 +139,7 @@ public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter
// See: https://docs.spring.io/spring-security/site/docs/3.0.x/reference/remember-me.html
String rememberMeKey = settingsService.getRememberMeKey();
- boolean development = settingsService.isDevelopmentMode();
+ boolean development = SettingsService.isDevelopmentMode();
if (StringUtils.isBlank(rememberMeKey) && !development) {
// ...if it is empty, generate a random key on startup (default).
logger.debug("Generating a new ephemeral 'remember me' key in a secure way.");
diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java b/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java
index ac4612de..8ede464d 100644
--- a/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java
+++ b/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java
@@ -791,7 +791,7 @@ public class SettingsService {
*
* @return true if we are in Development mode.
*/
- public boolean isDevelopmentMode() {
+ public static boolean isDevelopmentMode() {
return System.getProperty("airsonic.development") != null;
}
diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/RegisterPrecompiledJSPInitializer.java b/airsonic-main/src/main/java/org/airsonic/player/spring/RegisterPrecompiledJSPInitializer.java
new file mode 100644
index 00000000..1f5779c6
--- /dev/null
+++ b/airsonic-main/src/main/java/org/airsonic/player/spring/RegisterPrecompiledJSPInitializer.java
@@ -0,0 +1,73 @@
+package org.airsonic.player.spring;
+
+import org.airsonic.player.service.SettingsService;
+import org.airsonic.player.spring.webxmldomain.ServletDef;
+import org.airsonic.player.spring.webxmldomain.ServletMappingDef;
+import org.airsonic.player.spring.webxmldomain.WebApp;
+import org.apache.commons.io.IOUtils;
+import org.apache.cxf.jaxb.JAXBDataBinding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.servlet.ServletContextInitializer;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRegistration;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+import java.nio.charset.Charset;
+
+@Component
+public class RegisterPrecompiledJSPInitializer implements ServletContextInitializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(RegisterPrecompiledJSPInitializer.class);
+
+ @Override
+ public void onStartup(ServletContext servletContext) {
+ if(SettingsService.isDevelopmentMode()) {
+ logger.debug("Not registering precompiled jsps");
+ } else {
+ logger.debug("Registering precompiled jsps");
+ registerPrecompiledJSPs(servletContext);
+ }
+ }
+
+ private static void registerPrecompiledJSPs(ServletContext servletContext) {
+ WebApp webApp = parseXmlFragment();
+ for (ServletDef def : webApp.getServletDefs()) {
+ logger.trace("Registering precompiled JSP: {} -> {}", def.getName(), def.getSclass());
+ ServletRegistration.Dynamic reg = servletContext.addServlet(def.getName(), def.getSclass());
+ // Need to set loadOnStartup somewhere between 0 and 128. 0 is highest priority. 99 should be fine
+ reg.setLoadOnStartup(99);
+ }
+
+ for (ServletMappingDef mapping : webApp.getServletMappingDefs()) {
+ logger.trace("Mapping servlet: {} -> {}", mapping.getName(), mapping.getUrlPattern());
+ servletContext.getServletRegistration(mapping.getName()).addMapping(mapping.getUrlPattern());
+ }
+ }
+
+ private static WebApp parseXmlFragment() {
+ InputStream precompiledJspWebXml = RegisterPrecompiledJSPInitializer.class.getResourceAsStream("/precompiled-jsp-web.xml");
+ InputStream webXmlIS = new SequenceInputStream(
+ new SequenceInputStream(
+ IOUtils.toInputStream("", Charset.defaultCharset()),
+ precompiledJspWebXml),
+ IOUtils.toInputStream("", Charset.defaultCharset()));
+
+ JAXBContext jaxbContext;
+ try {
+ jaxbContext = new JAXBDataBinding(WebApp.class).getContext();
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ WebApp webapp = (WebApp) unmarshaller.unmarshal(webXmlIS);
+ return webapp;
+ } catch (JAXBException e) {
+ throw new RuntimeException("Could not parse precompiled-jsp-web.xml", e);
+ }
+ }
+
+}
diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/ServletDef.java b/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/ServletDef.java
new file mode 100644
index 00000000..c7b924e5
--- /dev/null
+++ b/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/ServletDef.java
@@ -0,0 +1,32 @@
+package org.airsonic.player.spring.webxmldomain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ServletDef {
+ @XmlElement(name="servlet-name")
+ private String name;
+
+ @XmlElement(name="servlet-class")
+ private String sclass;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getSclass() {
+ return sclass;
+ }
+
+ public void setSclass(String sclass) {
+ this.sclass = sclass;
+ }
+}
diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/ServletMappingDef.java b/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/ServletMappingDef.java
new file mode 100644
index 00000000..c3274788
--- /dev/null
+++ b/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/ServletMappingDef.java
@@ -0,0 +1,33 @@
+package org.airsonic.player.spring.webxmldomain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ServletMappingDef {
+ @XmlElement(name="servlet-name")
+ private String name;
+
+ @XmlElement(name="url-pattern")
+ private String urlPattern;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUrlPattern() {
+ return urlPattern;
+ }
+
+ public void setUrlPattern(String urlPattern) {
+ this.urlPattern = urlPattern;
+ }
+
+}
diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/WebApp.java b/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/WebApp.java
new file mode 100644
index 00000000..3c5f518e
--- /dev/null
+++ b/airsonic-main/src/main/java/org/airsonic/player/spring/webxmldomain/WebApp.java
@@ -0,0 +1,34 @@
+package org.airsonic.player.spring.webxmldomain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import java.util.List;
+
+@XmlRootElement(name="web-app")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class WebApp {
+ @XmlElement(name="servlet")
+ private List servletDefs;
+
+ @XmlElement(name="servlet-mapping")
+ private List servletMappingDefs;
+
+ public List getServletDefs() {
+ return servletDefs;
+ }
+
+ public void setServletDefs(List servletDefs) {
+ this.servletDefs = servletDefs;
+ }
+
+ public List getServletMappingDefs() {
+ return servletMappingDefs;
+ }
+
+ public void setServletMappingDefs(List servletMappingDefs) {
+ this.servletMappingDefs = servletMappingDefs;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 3a32e056..9293a0c6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -381,6 +381,7 @@
org.springframework.boot:*
org.apache.tomcat.embed:tomcat-embed-core*
org.apache.tomcat:tomcat-annotations-api:*
+ org.apache.tomcat.embed:tomcat-embed-el*
com.sun.mail:javax.mail*