diff --git a/.gitignore b/.gitignore index 67bd5155b2..20c2c5b5e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.sw? .#* *# +*~ /build .classpath .project diff --git a/README.md b/README.md deleted file mode 100644 index c6607e460c..0000000000 --- a/README.md +++ /dev/null @@ -1,177 +0,0 @@ -# Spring Bootstrap - -Spring Bootstrap is "Spring for Snowboarders". If you are kewl, or -just impatient, and you want to use Spring, then this is the place to -be. Spring Bootstrap is a toolkit and runtime platform that will get -you up and running with Spring-powered, production-grade applications -and services with absolute minimum fuss. It takes an opinionated view -of the Spring family so that new and existing users can quickly get to -the bits they need. Assumes no knowledge of the Java development -ecosystem. Absolutely no code generation and no XML (unless you really -want it). - -The goals are: - -* Radically faster and widely accessible getting started experience - for Spring development -* Be opinionated out of the box, but get out of the way quickly as - requirements start to diverge from the defaults -* Provide a range of non-functional features that are common to large - classes of projects (e.g. embedded servers, security, metrics, - health checks, externalized configuration) -* First class support for REST-ful services, modern web applications, - batch jobs, and enterprise integration -* Applications that adapt their behaviour or configuration to their - environment -* Optionally use Groovy features like DSLs and AST transformations to - accelerate the implementation of basic business requirements - -## Installing -You need to build from source for now, but when it's done instructions will look like this: - -1) Get Java. Download and install the Java SDK from www.java.com - -2) Get Spring - - $ curl -s try.spring.io | bash - - or use the Windows installer - -3) Get to Work! - - $ cat > app.groovy - @Controller - class ThisWillActuallyRun { - @RequestMapping("/") - @ResponseBody - String home() { - return "Hello World!" - } - } - $ spring run app.groovy - $ curl localhost:8080 - Hello World! - - -## What? It's Groovy then? or like Grails? or another Roo? - -There is a command line tool that uses Groovy underneath so that we -can present simple snippets that can just run just like the slimline -`app.groovy` example above. Groovy makes this really easy. - -If you don't want to use the command line tool, or you would rather -work using Java and an IDE you can. Just add a `main()` method that -calls `SpringApplication` and add `@EnableAutoConfiguration`: - - - import org.springframework.stereotype.*; - import org.springframework.web.bind.annotation.*; - import org.springframework.bootstrap.context.annotation.*; - - @Controller - @EnableAutoConfiguration - public class SampleController { - - @RequestMapping("/") - @ResponseBody - String home() { - return "Hello World!" - } - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleController.class, args); - } - - } - -## Spring Bootstrap Themes - -There are a number of themes in Bootstrap. Here are the important -ones: - -### The Spring CLI - -The 'spring' command line application compiles and runs Groovy source, -making it super easy to write the absolute minimum of code to get an -application running. Spring CLI can also watch files, automatically -recompiling and restarting when they change. - -### Bootstrap Core - -The main library providing features that support the other parts of -Spring Bootstrap. Features include: - -* `SpringApplication` - a class with static convenience methods that - make it really easy to write a standalone Spring Application. Its - sole job is to create and refresh an appropriate Spring - `ApplicationContext`. -* Embedded web applications with a choice of container (Tomcat or - Jetty for now) -* `@EnableAutoConfigure` is an annotation that triggers - auto-configuration of the Spring context. Auto-configuration - attempts to guess what beans a user might want based on their - classpath. For example, If a 'HSQLDB' is on the classpath the user - probably wants an in-memory database to be - defined. Auto-configuration will back away as the user starts to - define their own beans. -* `@Conditional` is an annotation in Spring 4.0 that allows you to - control which parts of an application are used at runtime. Spring - Bootstrap provides some concrete implementations of conditional - configuration, e.g. `@ConditionalOnBean`, - `@ConditionalOnMissingBean` and `@ConditionalOnClass`. - -### Spring Bootstrap Service - - - -Spring Bootstrap Service uses auto-configuration features to decorate -your application with features that make it instantly deployable and -supportable in production. For instance if you are writing a JSON web -service then it will provide a server, security, logging, externalized -configuration, management endpoints, an audit abstraction, and more. -If you want to switch off the built in features, or extend or replace -them, it makes that really easy as well. - -### Spring Bootstrap Applications - - - -Spring Bootstrap Applications are a set of convenient dependency -descriptors that you can include in your application. You get a -one-stop-shop for all the Spring and related technology that you need -without having to hunt through sample code and copy paste loads of -dependency descriptors. For example, if you want to get started using -Spring and JPA for database access just include one dependency in your -project, and you are good to go. - -## Building the code -Use maven to build the source code. - - $ mvn clean install - -## Importing into eclipse -You can use m2e or `maven eclipse:eclipse`. - -Project specific settings are configured for source formatting. If you -are using m2e you can follow these steps to install eclipse support -for formatting: - -* Select `Install new software` from the `help` menu -* Click `Add...` to add a new repository -* Click the `Archive...` button -* Select `org.eclipse.m2e.maveneclipse.site-0.0.1-SNAPSHOT-site.zip` - from the `eclipse` folder in this checkout -* Install "Maven Integration for the maven-eclipse-plugin" - -Or if you prefer you can import settings manually from the `/eclipse` folder. - -## Samples -The following samples are included. To run use `java -jar target/.jar` - -* spring-bootstrap-simple-sample - A simple command line application -* spring-bootstrap-jetty-sample - Embedded Jetty -* spring-bootstrap-tomcat-sample - Embedded Tomcat -* spring-bootstrap-service-sample - Simple REST service with production features -* spring-batch-sample - Define and run a Batch job in a few lines of code -* spring-bootstrap-data-sample - Spring Data JPA + Hibernate + HSQLDB - diff --git a/spring-bootstrap-actuator/README.md b/spring-bootstrap-actuator/README.md deleted file mode 100644 index 539044abc9..0000000000 --- a/spring-bootstrap-actuator/README.md +++ /dev/null @@ -1,387 +0,0 @@ - - -# Spring Bootstrap Services - -Minimum fuss for getting RESTful services up and running in -production, and in other environments. - -|Feature |Implementation |Notes | -|---|---|---| -|Server |Tomcat or Jetty | Whatever is on the classpath | -|REST |Spring MVC | | -|Security |Spring Security | If on the classpath | -|Logging |Logback, Log4j or JDK | Whatever is on the classpath. Sensible defaults. | -|Database |HSQLDB or H2 | Per classpath, or define a DataSource to override | -|Externalized configuration | Properties or YAML | Support for Spring profiles. Bind automatically to @Bean. | -|Audit | Spring Security and Spring ApplicationEvent |Flexible abstraction with sensible defaults for security events | -|Validation | JSR-303 |If on the classpath | -|Management endpoints | Spring MVC | Health, basic metrics, request tracing, shutdown, thread dumps | -|Error pages | Spring MVC | Sensible defaults based on exception and status code | -|JSON |Jackson 2 | | -|ORM |Spring Data JPA | If on the classpath | -|Batch |Spring Batch | If enabled and on the classpath | -|Integration Patterns |Spring Integration | If on the classpath | - -# Getting Started - -You will need Java (6 at least) and a build tool (Maven is what we use -below, but you are more than welcome to use gradle). These can be -downloaded or installed easily in most operating systems. For Ubuntu: - - $ sudo apt-get install openjdk-6-jdk maven - - - -## A basic project - -If you are using Maven create a really simple `pom.xml` with 2 dependencies: - - - 4.0.0 - com.mycompany - myproject - 1.0.0-SNAPSHOT - jar - - org.springframework.bootstrap - spring-bootstrap-applications - 0.0.1-SNAPSHOT - - - - org.springframework.bootstrap - spring-bootstrap-web-application - - - org.springframework.bootstrap - spring-bootstrap-service - - - - - - org.apache.maven.plugins - maven-shade-plugin - - - - - -If you like Gradle, that's fine, and you will know what to do with -those dependencies. The first dependency adds Spring Bootstrap auto -configuration and the Jetty container to your application, and the -second one adds some more opinionated stuff like the default -management endpoints. If you prefer Tomcat you can just add the -embedded Tomcat jars to your classpath instead of Jetty. - -You should be able to run it already: - - $ mvn package - $ java -jar target/myproject-1.0.0-SNAPSHOT.jar - -Then in another terminal - - $ curl localhost:8080/healthz - ok - $ curl localhost:8080/varz - {"counter.status.200.healthz":1.0,"gauge.response.healthz":10.0,"mem":120768.0,"mem.free":105012.0,"processors":4.0} - -`/healthz` is the default location for the health endpoint - it tells -you if the application is running and healthy. `/varz` is the default -location for the metrics endpoint - it gives you basic counts and -response timing data by default but there are plenty of ways to -customize it. You can also try `/trace` and `/dump` to get some -interesting information about how and what your app is doing. - -What about the home page? - - $ curl localhost:8080/ - {"status": 404, "error": "Not Found", "message": "Not Found"} - -That's OK, we haven't added any business content yet. But it shows -that there are sensible defaults built in for rendering HTTP and -server-side errors. - -## Adding a business endpoint - -To do something useful to your business you need to add at least one -endpoint. An endpoint can be implemented as a Spring MVC -`@Controller`, e.g. - - @Controller - @EnableAutoConfiguration - public class SampleController { - - @RequestMapping("/") - @ResponseBody - public Map helloWorld() { - return Collections.singletonMap("message", "Hello World"); - } - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleController.class, args); - } - - } - -You can launch that straight away using the Spring Bootstrap CLI -(without the `@EnableAutoConfiguration` and even without the import -statements that your IDE will add if you are using one), or you can -use the main method to launch it from your project jar. Just add a -`start-class` in the properties section of the `pom` above pointing to -the fully qualified name of your `SampleController`, e.g. - - - com.mycompany.sample.SampleController - - -and re-package: - - $ mvn package - $ java -jar target/myproject-1.0.0-SNAPSHOT.jar - $ curl localhost:8080/ - {"message": "Hello World"} - -## Running the application - -You can package the app and run it as a jar (as above) and that's very -convenient for production usage. Or there are other options, many of -which are more convenient at development time. Here are a few: - -1. Use the Maven exec plugin, e.g. - - $ mvn exec:java - -2. Run directly in your IDE, e.g. Eclipse or IDEA let you right click -on a class and run it. - -3. Use a different Maven plugin. - -4. Find feature in Gradle that does the same thing. - -5. Use the Spring executable. - -## Externalizing configuration - -Spring Bootstrap likes you to externalize your configuration so you -can work with the same application code in different environments. To -get started with this you create a file in the root of your classpath -(`src/main/resources` if using Maven) - if you like YAML you can call -it `application.yml`, e.g.: - - server: - port: 9000 - management: - port: 9001 - logging: - file: target/log.out - -or if you like Java `Properties` files, you can call it -`application.properties`, e.g.: - - server.port: 9000 - management.port: 9001 - logging.file: target/log.out - -Those examples are properties that Spring Bootstrap itself binds to -out of the box, so if you make that change and run the app again, you -will find the home page on port 9000 instead of 8080: - - $ curl localhost:9000/ - {"message": "Hello World"} - -and the management endpoints on port 9001 instead of 8080: - - $ curl localhost:9001/healthz - ok - -To externalize business configuration you can simply add a default -value to your configuration file, e.g. - - server: - port: 9000 - management: - port: 9001 - logging: - file: target/log.out - service: - message: Awesome Message - -and then bind to it in the application code. The simplest way to do -that is to simply refer to it in an `@Value` annotation, e.g. - - @Controller - @EnableAutoConfiguration - public class SampleController { - - @Value("${service.message:Hello World}") - private String value = "Goodbye Everypone" - - @RequestMapping("/") - @ResponseBody - public Map helloWorld() { - return Collections.singletonMap("message", message); - } - - ... - } - -That's a little bit confusing because we have provided a message value -in three different places - in the external configuration ("Awesome -Message"), in the `@Value` annotation after the colon ("Hello World"), -and in the filed initializer ("Goodbye Everyone"). That was only to -show you how and you only need it once, so it's your choice (it's -useful for unit testing to have the Java initializer as well as the -external value). Note that the YAML object is flattened using period -separators. - -For simple Strings where you have sensible defaults `@Value` is -perfect, but if you want more and you like everything strongly typed -then you can have Spring bind the properties and validate them -automatically in a separate value object. For instance: - - // ServiceProperties.java - @ConfigurationProperties(name="service") - public class ServiceProperties { - private String message; - private int value = 0; - ... getters and setters - } - - // SampleController.java - @Controller - @EnableAutoConfiguration - @EnableConfigurationProperties(ServiceProperties.class) - public class SampleController { - - @Autowired - private ServiceProperties properties; - - @RequestMapping("/") - @ResponseBody - public Map helloWorld() { - return Collections.singletonMap("message", properties.getMessage()); - } - - ... - } - -When you ask to -`@EnableConfigurationProperties(ServiceProperties.class)` you are -saying you want a bean of type `ServiceProperties` and that you want -to bind it to the Spring Environment. The Spring Environment is a -collection of name-value pairs taken from (in order of decreasing -precedence) 1) the command line, 2) the external configuration file, -3) System properties, 4) the OS environment. Validation is done based -on JSR-303 annotations by default provided that library (and an -implementation) is on the classpath. - -## Adding security - -If you add Spring Security java config to your runtime classpath you -will enable HTTP basic authentication by default on all the endpoints. -In the `pom.xml` it would look like this: - - - org.springframework.security - spring-security-javaconfig - 1.0.0.BUILD-SNAPSHOT - - -(Spring Security java config is still work in progress so we have used -a snapshot. Beware of sudden changes.) - - - -Try it out: - - $ curl localhost:8080/ - {"status": 403, "error": "Forbidden", "message": "Access Denied"} - $ curl user:password@localhost:8080/ - {"message": "Hello World"} - -The default auto configuration has an in-memory user database with one -entry. If you want to extend or expand that, or point to a database -or directory server, you only need to provide a `@Bean` definition for -an `AuthenticationManager`, e.g. in your `SampleController`: - - @Bean - public AuthenticationManager authenticationManager() throws Exception { - return new AuthenticationBuilder().inMemoryAuthentication().withUser("client") - .password("secret").roles("USER").and().and().build(); - } - -Try it out: - - $ curl user:password@localhost:8080/ - {"status": 403, "error": "Forbidden", "message": "Access Denied"} - $ curl client:secret@localhost:8080/ - {"message": "Hello World"} - -## Adding a database - -Just add `spring-jdbc` and an embedded database to your dependencies: - - - org.springframework - spring-jdbc - - - org.hsqldb - hsqldb - - -Then you will be able to inject a `DataSource` into your controller: - - @Controller - @EnableAutoConfiguration - @EnableConfigurationProperties(ServiceProperties.class) - public class SampleController { - - private JdbcTemplate jdbcTemplate; - - @Autowired - public SampleController(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } - - @RequestMapping("/") - @ResponseBody - public Map helloWorld() { - return jdbcTemplate.queryForMap("SELECT * FROM MESSAGES WHERE ID=?", 0); - } - - ... - } - - The app will run (going back to the default security configuration): - - $ curl user:password@localhost:8080/ - {"error":"Internal Server Error", "status":500, "exception":...} - - but there's no data in the database yet and the `MESSAGES` table - doesn't even exist, so there's an error. One easy way to fix it is - to provide a `schema.sql` script in the root of the classpath, e.g. - - create table MESSAGES ( - ID BIGINT NOT NULL PRIMARY KEY, - MESSAGE VARCHAR(255) - ); - INSERT INTO MESSAGES (ID, MESSAGE) VALUES (0, 'Hello Phil'); - -Now when you run the app you get a sensible response: - - $ curl user:password@localhost:8080/ - {"ID":0, "MESSAGE":"Hello Phil"} - -Obviously, this is only the start, but hopefully you have a good grasp -of the basics and are ready to try it out yourself. diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementServerConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementServerConfiguration.java index 37960ca2b4..0a9d47b5cf 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementServerConfiguration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementServerConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.bootstrap.actuate.autoconfigure; -import java.util.Collections; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -107,8 +105,7 @@ public class ManagementServerConfiguration implements BeanPostProcessor { factory.setAddress(this.configuration.getAddress()); factory.setContextPath(this.configuration.getContextPath()); - factory.setErrorPages(Collections - .singleton(new ErrorPage(this.errorPath))); + factory.addErrorPages(new ErrorPage(this.errorPath)); this.initialized = true; } diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ServerConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ServerConfiguration.java index aaec6c0f50..8b6b1cda53 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ServerConfiguration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ServerConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.bootstrap.actuate.autoconfigure; -import java.util.Collections; - import javax.servlet.Servlet; import org.apache.catalina.valves.AccessLogValve; @@ -104,8 +102,7 @@ public class ServerConfiguration implements BeanPostProcessor, BeanFactoryAware server); } - factory.setErrorPages(Collections - .singleton(new ErrorPage(this.errorPath))); + factory.addErrorPages(new ErrorPage(this.errorPath)); this.initialized = true; } diff --git a/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/properties/ServerPropertiesTests.java b/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/properties/ServerPropertiesTests.java new file mode 100644 index 0000000000..bcf296fb14 --- /dev/null +++ b/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/properties/ServerPropertiesTests.java @@ -0,0 +1,54 @@ +/* + * 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 org.springframework.bootstrap.actuate.properties; + +import java.net.InetAddress; +import java.util.Collections; + +import org.junit.Test; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.bootstrap.bind.RelaxedDataBinder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Externalized configuration for server properties + * + * @author Dave Syer + * + */ +public class ServerPropertiesTests { + + private ServerProperties properties = new ServerProperties(); + + @Test + public void testAddressBinding() throws Exception { + RelaxedDataBinder binder = new RelaxedDataBinder(this.properties, "server"); + binder.bind(new MutablePropertyValues(Collections.singletonMap("server.address", + "127.0.0.1"))); + assertFalse(binder.getBindingResult().hasErrors()); + assertEquals(InetAddress.getLocalHost(), this.properties.getAddress()); + } + + @Test + public void testPortBinding() throws Exception { + new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues( + Collections.singletonMap("server.port", "9000"))); + assertEquals(9000, this.properties.getPort()); + } + +} diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/ErrorPage.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/ErrorPage.java index 8cd30cd930..1882bf8866 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/ErrorPage.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/ErrorPage.java @@ -16,6 +16,7 @@ package org.springframework.bootstrap.context.embedded; +import org.springframework.http.HttpStatus; import org.springframework.util.ObjectUtils; /** @@ -31,14 +32,14 @@ public class ErrorPage { private Class exception = null; - private int status = 0; + private HttpStatus status = null; public ErrorPage(String path) { super(); this.path = path; } - public ErrorPage(int status, String path) { + public ErrorPage(HttpStatus status, String path) { super(); this.status = status; this.path = path; @@ -71,12 +72,21 @@ public class ErrorPage { /** * The HTTP status value that this error page matches. * - * @return the status value (or 0 for a page that matches any status) + * @return the status */ - public int getStatus() { + public HttpStatus getStatus() { return this.status; } + /** + * The HTTP status value that this error page matches. + * + * @return the status value (or 0 for a page that matches any status) + */ + public int getStatusCode() { + return this.status == null ? 0 : this.status.value(); + } + /** * The exception type name. * @@ -91,7 +101,7 @@ public class ErrorPage { * types)? */ public boolean isGlobal() { - return this.status == 0 && this.exception == null; + return this.status == null && this.exception == null; } @Override @@ -100,7 +110,7 @@ public class ErrorPage { int result = 1; result = prime * result + ObjectUtils.nullSafeHashCode(getExceptionName()); result = prime * result + ObjectUtils.nullSafeHashCode(this.path); - result = prime * result + this.status; + result = prime * result + this.getStatusCode(); return result; } diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java index f8462ea2b1..bc859b9c3c 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java @@ -67,6 +67,8 @@ public class JettyEmbeddedServletContainerFactory extends private ResourceLoader resourceLoader; + private WebAppContext context = new WebAppContext(); + /** * Create a new {@link JettyEmbeddedServletContainerFactory} instance. */ @@ -101,29 +103,29 @@ public class JettyEmbeddedServletContainerFactory extends } Server server = new Server(new InetSocketAddress(getAddress(), getPort())); - WebAppContext context = new WebAppContext(); if (this.resourceLoader != null) { - context.setClassLoader(this.resourceLoader.getClassLoader()); + this.context.setClassLoader(this.resourceLoader.getClassLoader()); } String contextPath = getContextPath(); - context.setContextPath(StringUtils.hasLength(contextPath) ? contextPath : "/"); - configureDocumentRoot(context); + this.context.setContextPath(StringUtils.hasLength(contextPath) ? contextPath + : "/"); + configureDocumentRoot(this.context); if (getRegisterDefaultServlet()) { - addDefaultServlet(context); + addDefaultServlet(this.context); } if (getRegisterJspServlet() && ClassUtils.isPresent(getJspServletClassName(), getClass() .getClassLoader())) { - addJspServlet(context); + addJspServlet(this.context); } ServletContextInitializer[] initializersToUse = mergeInitializers(initializers); - Configuration[] configurations = getWebAppContextConfigurations(context, + Configuration[] configurations = getWebAppContextConfigurations(this.context, initializersToUse); - context.setConfigurations(configurations); - postProcessWebAppContext(context); + this.context.setConfigurations(configurations); + postProcessWebAppContext(this.context); - server.setHandler(context); + server.setHandler(this.context); return getJettyEmbeddedServletContainer(server); } @@ -187,23 +189,7 @@ public class JettyEmbeddedServletContainerFactory extends @Override public void configure(WebAppContext context) throws Exception { ErrorHandler errorHandler = context.getErrorHandler(); - if (errorHandler instanceof ErrorPageErrorHandler) { - ErrorPageErrorHandler handler = (ErrorPageErrorHandler) errorHandler; - for (ErrorPage errorPage : getErrorPages()) { - if (errorPage.isGlobal()) { - handler.addErrorPage(ErrorPageErrorHandler.GLOBAL_ERROR_PAGE, - errorPage.getPath()); - } else { - if (errorPage.getExceptionName() != null) { - handler.addErrorPage(errorPage.getExceptionName(), - errorPage.getPath()); - } else { - handler.addErrorPage(errorPage.getStatus(), - errorPage.getPath()); - } - } - } - } + addJettyErrorPages(errorHandler, getErrorPages()); } }; } @@ -269,11 +255,33 @@ public class JettyEmbeddedServletContainerFactory extends /** * Add {@link Configuration}s that will be applied to the {@link WebAppContext} before - * the server is create. + * the server is started. + * * @param configurations the configurations to add */ public void addConfigurations(Configuration... configurations) { Assert.notNull(configurations, "Configurations must not be null"); this.configurations.addAll(Arrays.asList(configurations)); } + + private void addJettyErrorPages(ErrorHandler errorHandler, + Collection errorPages) { + if (errorHandler instanceof ErrorPageErrorHandler) { + ErrorPageErrorHandler handler = (ErrorPageErrorHandler) errorHandler; + for (ErrorPage errorPage : errorPages) { + if (errorPage.isGlobal()) { + handler.addErrorPage(ErrorPageErrorHandler.GLOBAL_ERROR_PAGE, + errorPage.getPath()); + } else { + if (errorPage.getExceptionName() != null) { + handler.addErrorPage(errorPage.getExceptionName(), + errorPage.getPath()); + } else { + handler.addErrorPage(errorPage.getStatusCode(), + errorPage.getPath()); + } + } + } + } + } } diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index a980c1cac7..e9f422977a 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -209,7 +209,7 @@ public class TomcatEmbeddedServletContainerFactory extends org.apache.catalina.deploy.ErrorPage tomcatPage = new org.apache.catalina.deploy.ErrorPage(); tomcatPage.setLocation(errorPage.getPath()); tomcatPage.setExceptionType(errorPage.getExceptionName()); - tomcatPage.setErrorCode(errorPage.getStatus()); + tomcatPage.setErrorCode(errorPage.getStatusCode()); context.addErrorPage(tomcatPage); } }