diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml
index 21570384404..16606ab78d4 100644
--- a/spring-boot-samples/pom.xml
+++ b/spring-boot-samples/pom.xml
@@ -39,6 +39,7 @@
spring-boot-sample-simple
spring-boot-sample-tomcat
spring-boot-sample-tomcat-multi-connectors
+ spring-boot-sample-tomcat8-jsp
spring-boot-sample-traditional
spring-boot-sample-web-method-security
spring-boot-sample-web-secure
diff --git a/spring-boot-samples/spring-boot-sample-tomcat8-jsp/pom.xml b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/pom.xml
new file mode 100644
index 00000000000..e06cdddf3cf
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-samples
+ 1.0.3.BUILD-SNAPSHOT
+
+ spring-boot-sample-tomcat8-jsp
+ war
+ Spring Boot Tomcat 8 JSP Sample
+ Spring Boot Tomcat 8 JSP Sample
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+ /
+ 8.0.8
+ 1.7
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ provided
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-jasper
+ provided
+
+
+ javax.servlet
+ jstl
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ false
+
+
+
+
+
diff --git a/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/java/sample/jsp/SampleTomcat8JspApplication.java b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/java/sample/jsp/SampleTomcat8JspApplication.java
new file mode 100644
index 00000000000..6ab7e320f38
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/java/sample/jsp/SampleTomcat8JspApplication.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.jsp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.web.SpringBootServletInitializer;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableAutoConfiguration
+@ComponentScan
+public class SampleTomcat8JspApplication extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ return application.sources(SampleTomcat8JspApplication.class);
+ }
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(SampleTomcat8JspApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/java/sample/jsp/WelcomeController.java b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/java/sample/jsp/WelcomeController.java
new file mode 100644
index 00000000000..03d439caa3c
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/java/sample/jsp/WelcomeController.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.jsp;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class WelcomeController {
+
+ @Value("${application.message:Hello World}")
+ private String message = "Hello World";
+
+ @RequestMapping("/")
+ public String welcome(Map model) {
+ model.put("time", new Date());
+ model.put("message", this.message);
+ return "welcome";
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/resources/application.properties
new file mode 100644
index 00000000000..f95f1d3c014
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/resources/application.properties
@@ -0,0 +1,3 @@
+spring.view.prefix: /WEB-INF/jsp/
+spring.view.suffix: .jsp
+application.message: Hello Phil
\ No newline at end of file
diff --git a/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/webapp/WEB-INF/jsp/welcome.jsp b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/webapp/WEB-INF/jsp/welcome.jsp
new file mode 100644
index 00000000000..3196dac625d
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/main/webapp/WEB-INF/jsp/welcome.jsp
@@ -0,0 +1,18 @@
+
+
+<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+
+
+
+
+
+
+ Spring URL: ${springUrl} at ${time}
+
+ JSTL URL: ${url}
+
+ Message: ${message}
+
+
+
diff --git a/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/test/java/sample/jsp/SampleWebJspApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/test/java/sample/jsp/SampleWebJspApplicationTests.java
new file mode 100644
index 00000000000..09d557e12bc
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-tomcat8-jsp/src/test/java/sample/jsp/SampleWebJspApplicationTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.jsp;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Basic integration tests for JSP application.
+ *
+ * @author Phillip Webb
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = SampleTomcat8JspApplication.class)
+@WebAppConfiguration
+@IntegrationTest("server.port:0")
+@DirtiesContext
+public class SampleWebJspApplicationTests {
+
+ @Value("${local.server.port}")
+ private int port;
+
+ @Test
+ public void testJspWithEl() throws Exception {
+ ResponseEntity entity = new TestRestTemplate().getForEntity(
+ "http://localhost:" + this.port, String.class);
+ assertEquals(HttpStatus.OK, entity.getStatusCode());
+ assertTrue("Wrong body:\n" + entity.getBody(),
+ entity.getBody().contains("/resources/text.txt"));
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/CustomSkipPatternJarScanner.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/CustomSkipPatternJarScanner.java
index ad083a9fc4c..454ee0e787c 100644
--- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/CustomSkipPatternJarScanner.java
+++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/CustomSkipPatternJarScanner.java
@@ -25,15 +25,18 @@ import javax.servlet.ServletContext;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;
+import org.apache.tomcat.util.scan.StandardJarScanner;
import org.springframework.util.Assert;
/**
- * {@link JarScanner} decorator allowing alternative default jar pattern matching.
+ * {@link JarScanner} decorator allowing alternative default jar pattern matching. This
+ * class extends {@link StandardJarScanner} rather than implementing the
+ * {@link JarScanner} due to API changes introduced in Tomcat 8.
*
* @author Phillip Webb
* @see #apply(TomcatEmbeddedContext, String)
*/
-class SkipPatternJarScanner implements JarScanner {
+class SkipPatternJarScanner extends StandardJarScanner {
private final JarScanner jarScanner;
@@ -58,7 +61,9 @@ class SkipPatternJarScanner implements JarScanner {
* @param pattern the jar skip pattern or {@code null} for defaults
*/
public static void apply(TomcatEmbeddedContext context, String pattern) {
- context.setJarScanner(new SkipPatternJarScanner(context.getJarScanner(), pattern));
+ SkipPatternJarScanner scanner = new SkipPatternJarScanner(
+ context.getJarScanner(), pattern);
+ context.setJarScanner(scanner);
}
private static class SkipPattern {
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/JasperInitializerLifecycleListener.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/JasperInitializerLifecycleListener.java
deleted file mode 100644
index 6a7e107c782..00000000000
--- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/JasperInitializerLifecycleListener.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.springframework.boot.context.embedded.tomcat;
-
-import javax.servlet.ServletContainerInitializer;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.Lifecycle;
-import org.apache.catalina.LifecycleEvent;
-import org.apache.catalina.LifecycleListener;
-import org.apache.catalina.core.StandardContext;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-
-/**
- * Tomcat {@link LifecycleListener} to initialize Jasper by calling the
- * `JasperInitializer` used in Tomcat 8.
- *
- * @author Phillip Webb
- */
-class JasperInitializerLifecycleListener implements LifecycleListener {
-
- private static final String JASPER_INITIALIZER_CLASS = "org.apache.jasper.servlet.JasperInitializer";
-
- private ServletContainerInitializer initializer;
-
- public JasperInitializerLifecycleListener() {
- this.initializer = getJasperInitializer();
- }
-
- @Override
- public void lifecycleEvent(LifecycleEvent event) {
- if (this.initializer != null
- && Lifecycle.CONFIGURE_START_EVENT.equals(event.getType())) {
- onStartup(event);
- }
- }
-
- private void onStartup(LifecycleEvent event) {
- Assert.isInstanceOf(StandardContext.class, event.getSource());
- StandardContext standardContext = (StandardContext) event.getSource();
- try {
- this.initializer.onStartup(null, standardContext.getServletContext());
- }
- catch (ServletException ex) {
- throw new IllegalStateException(ex);
- }
- }
-
- private ServletContainerInitializer getJasperInitializer() {
- try {
- Class> jasperClass = ClassUtils.forName(JASPER_INITIALIZER_CLASS, null);
- return (ServletContainerInitializer) jasperClass.newInstance();
- }
- catch (Exception ex) {
- return null;
- }
- }
-
-}
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java
index 2fd2463c80b..089175553b5 100644
--- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java
+++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import org.apache.catalina.Context;
@@ -167,6 +168,7 @@ public class TomcatEmbeddedServletContainerFactory extends
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
.getClassLoader())) {
addJspServlet(context);
+ addJasperInitializer(context);
context.addLifecycleListener(new StoreMergedWebXmlListener());
}
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
@@ -199,6 +201,18 @@ public class TomcatEmbeddedServletContainerFactory extends
context.addServletMapping("*.jspx", "jsp");
}
+ private void addJasperInitializer(TomcatEmbeddedContext context) {
+ try {
+ ServletContainerInitializer initializer = (ServletContainerInitializer) ClassUtils
+ .forName("org.apache.jasper.servlet.JasperInitializer", null)
+ .newInstance();
+ context.addServletContainerInitializer(initializer, null);
+ }
+ catch (Exception ex) {
+ // Probably not Tomcat 8
+ }
+ }
+
// Needs to be protected so it can be used by subclasses
protected void customizeConnector(Connector connector) {
int port = (getPort() >= 0 ? getPort() : 0);
@@ -230,7 +244,6 @@ public class TomcatEmbeddedServletContainerFactory extends
ServletContextInitializer[] initializers) {
context.addLifecycleListener(new ServletContextInitializerLifecycleListener(
initializers));
- context.addLifecycleListener(new JasperInitializerLifecycleListener());
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}