Use HttpStatus in ErrorPage

This commit is contained in:
Dave Syer 2013-05-08 15:33:25 +01:00
parent dc30add6c5
commit 8bfe07c730
9 changed files with 110 additions and 607 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.sw?
.#*
*#
*~
/build
.classpath
.project

177
README.md
View File

@ -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
<!-- FIXME: change the name -->
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
<!-- FIXME: change the name -->
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/<archive>.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

View File

@ -1,387 +0,0 @@
<style>
table
{
border-collapse:collapse;
}
table,th, td
{
border: 1px solid black;
}
</style>
# 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
<!--FIXME: short instructions for Mac.-->
## A basic project
If you are using Maven create a really simple `pom.xml` with 2 dependencies:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>myproject</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-applications</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-web-application</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-service</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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<String, String> 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.
<properties>
<start-class>com.mycompany.sample.SampleController</start-class>
</properties>
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. <!--FIXME: document this maybe.-->
## 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<String, String> 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<String, String> 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:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-javaconfig</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
(Spring Security java config is still work in progress so we have used
a snapshot. Beware of sudden changes.)
<!--FIXME: update Spring Security to full release -->
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:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
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<String, String> 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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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<? extends Throwable> 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;
@ -68,13 +69,22 @@ public class ErrorPage {
return this.exception;
}
/**
* The HTTP status value that this error page matches.
*
* @return the status
*/
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 getStatus() {
return this.status;
public int getStatusCode() {
return this.status == null ? 0 : this.status.value();
}
/**
@ -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;
}

View File

@ -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<ErrorPage> 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());
}
}
}
}
}
}

View File

@ -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);
}
}