Change package names zero->boot

* actuator -> boot-ops
* cli -> boot-cli
* launcher -> boot-load
* autoconfig -> boot-config
* bootstrap -> boot-strap
* starters -> boot-up

[#54095231] [bs-253] Refactor Zero->Boot
This commit is contained in:
Dave Syer
2013-07-26 11:50:02 +01:00
parent b2873fbc2d
commit 2098e23fca
609 changed files with 1686 additions and 1626 deletions

371
spring-boot-ops/README.md Normal file
View File

@@ -0,0 +1,371 @@
# Spring Boot Ops
The aim of this project is minimum fuss for getting applications up
and running in production, and in other environments. There is a
strong emphasis on implementing RESTful web services but many features
are more generic than that.
|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 |
For a quick introduction and to get started quickly with a new
project, carry on reading. For more in depth coverage of the features
of Spring Boot Ops, go to the
[Feature Guide](docs/Features.md).
# 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.boot</groupId>
<artifactId>spring-starter-parent</artifactId>
<version>{{project.version}}</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-package-maven-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 Boot auto
configuration and the Tomcat container to your application, and the
second one adds some more opinionated stuff like the default
management endpoints. If you prefer Jetty you can just add the
embedded Jetty jars to your classpath instead of Tomcat (once you
exclude the `spring-starter-tomcat` dependency).
## 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 Boot 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 package and run:
$ mvn package
$ java -jar target/myproject-1.0.0-SNAPSHOT.jar
$ curl localhost:8080/
{"message": "Hello World"}
There are also some endpoins that you didn't implement by came free
with the Actuator:
$ curl localhost:8080/health
ok
$ curl localhost:8080/metrics
{"counter.status.200.health":1.0,"gauge.response.health":10.0,"mem":120768.0,"mem.free":105012.0,"processors":4.0}
`/health` is the default location for the health endpoint - it tells
you if the application is running and healthy. `/metrics` 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.
## 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 Boot 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 Boot 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/health
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

@@ -0,0 +1,162 @@
# Spring Actuator Feature Guide
Here are some (most, hopefully all) the features of Spring Actuator
with some commentary to help you start using them. We
recommend you first build a project with the Actuator (e.g. the
getting started project from the main README), and then try each
feature in turn there.
Many useful features of
[Spring Bootstrap](../../spring-bootstrap/README.md) are all available
in an Actuator application.
TODO: group things together and break them out into separate files.
## Customizing Management Endpoints
The `ManagementProperties` are bound to application properties, and
can be used to specify
* The port that the application listens on for the management
endpoints (defaults to 8080)
* The address that the management endpoints are available on (if the
port is different to the main server port). Use this to listen only
on an internal or ops-facing network, for instance, or to only
listen for connections from localhost (by specifying "127.0.0.1")
* The context root of the management endpoints
## Error Handling
The Actuator provides an `/error` mapping by default that handles all
errors in a sensible way. If you want more specific error pages for
some conditions, the embedded servlet containers support a uniform
Java DSL for customizing the error handling. To do this you have to
have picked a container implementation (by including either Tomcat or
Jetty on the classpath), but then the API is the same. TODO: finish
this.
## Logging
Spring Actuator uses SLF4J for logging, but leaves the implementation
open. The Starter projects and the Actuator use logback logging by
default because it has the richest feature set.
## Info Endpoint
By default the Actuator adds an `/info` endpoint to the main server.
It contains the commit and timestamp information from `git.properties`
(if that file exists) and also any properties it finds in the
environment with prefix "info".
To populate `git.properties` in a
Maven build you can use the excellent
[git-commit-id-plugin](https://github.com/ktoso/maven-git-commit-id-plugin).
To populate the "info" map all you need to do is add some stuff to
`application.properties`, e.g.
info.app.name: MyService
info.app.description: My awesome service
info.app.version: 1.0.0
If you are using Maven you can automcatically populate info properties
from the project using resource filtering. In your `pom.xml` you
have (inside the `<build/>` element):
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
and then in the `application.properties` you can refer to project
properties via placeholders, e.g.
project.artifactId: myproject
project.name: Demo
project.version: X.X.X.X
project.description: Demo project for info endpoint
info.build.artifact: ${project.artifactId}
info.build.name: ${project.name}
info.build.description: ${project.description}
info.build.version: ${project.version}
(notice that in the example we used `project.*` to set some values to
be used as fallbacks if the Maven resource filtering has for some
reason not been switched on).
## Security - Basic Authentication
To secure your endpoints just add Spring Security Javaconfig to the
classpath. By default HTTP Basic authentication will be applied to
every request in the main server (and the management server if it is
running on the same port). There is a single account by default, and
you can test it like this:
$ mvn user:password@localhost:8080/metrics
... stuff comes out
If the management server is running on a different port it is
unsecured by default. If you want to secure it you can add a security
auto configuration explicitly
## Security - HTTPS
Ensuring that all your main endpoints are only available over HTTPS is
an important chore for any application. If you are using Tomcat as a
servlet container, then the Actuator will add Tomcat's own
`RemoteIpValve` automatically if it detects some environment settings,
and you should be able to rely on the `HttpServletRequest` to report
whether or not it is secure (even downstream of the real SSL
termination endpoint). The standard behaviour is determined by the
presence or absence of certain request headers ("x-forwarded-for" and
"x-forwarded-proto"), whose names are conventional, so it should work
with most front end proxies. You switch on the valve by adding some
entries to `application.properties`, e.g.
server.tomcat.remote_ip_header: x-forwarded-for
server.tomcat.protocol_header: x-forwarded-proto
(The presence of either of those properties will switch on the
valve. Or you can add the `RemoteIpValve` yourself by adding a
`TomcatEmbeddedServletContainerFactory` bean.)
Spring Security can also be configured to require a secure channel for
all (or some requests). To switch that on in an Actuator application
you just need to set `security.require_https: true` in
`application.properties`.
## Audit Events
The Actuator has a flexible audit framework that will publish events
once Spring Security is in play (authentication success and failure
and access denied exceptions by default). This can be very useful for
reporting, and also to implement a lock-out policy based on
authentication failures.
You can also choose to use the audit services for your own business
events. To do that you can either inject the existing
`AuditEventRepository` into your own components and use that directly,
or you can simply publish `AuditApplicationEvent` via the Spring
`ApplicationContext` (using `ApplicationEventPublisherAware`).
## Metrics Customization
Metrics come out on the `/metrics` endpoint. You can add additional
metrics by injecting a `MetricsRepository` into your application
components and adding metrics whenever you need to. To customize the
`MetricsRepository` itself, just add a bean definition of that type to
the application context (only in memory is supported out of the box
for now).
## Customizing the Health Indicator
The application always tells you if it's healthy via the `/health`
endpoint. By default it just responds to a GET witha 200 status and a
plain text body containing "ok". If you want to add more detailed
information (e.g. a description of the current state of the
application), just add a bean of type `HealthIndicator` to your
application context, and it will take the place of the default one.

83
spring-boot-ops/pom.xml Normal file
View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-ops</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${basedir}/..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Optional -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-strap</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,138 @@
/*
* 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.boot.ops.audit;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.util.Assert;
/**
* A value object representing an audit event: at a particular time, a particular user or
* agent carried out an action of a particular type. This object records the details of
* such an event.
*
* <p>
* Users can inject a {@link AuditEventRepository} to publish their own events or
* alternatively use Springs {@link AuthenticationEventPublisher} (usually obtained by
* implementing {@link ApplicationEventPublisherAware}).
*
* @author Dave Syer
* @see AuditEventRepository
*/
public class AuditEvent implements Serializable {
private final Date timestamp;
private final String principal;
private final String type;
private final Map<String, Object> data;
/**
* Create a new audit event for the current time.
* @param principal The user principal responsible
* @param type the event type
* @param data The event data
*/
public AuditEvent(String principal, String type, Map<String, Object> data) {
this(new Date(), principal, type, data);
}
/**
* Create a new audit event for the current time from data provided as name-value
* pairs
* @param principal The user principal responsible
* @param type the event type
* @param data The event data in the form 'key=value' or simply 'key'
*/
public AuditEvent(String principal, String type, String... data) {
this(new Date(), principal, type, convert(data));
}
/**
* Create a new audit event.
* @param timestamp The date/time of the event
* @param principal The user principal responsible
* @param type the event type
* @param data The event data
*/
public AuditEvent(Date timestamp, String principal, String type,
Map<String, Object> data) {
Assert.notNull(timestamp, "Timestamp must not be null");
Assert.notNull(type, "Type must not be null");
this.timestamp = timestamp;
this.principal = principal;
this.type = type;
this.data = Collections.unmodifiableMap(data);
}
private static Map<String, Object> convert(String[] data) {
Map<String, Object> result = new HashMap<String, Object>();
for (String entry : data) {
if (entry.contains("=")) {
int index = entry.indexOf("=");
result.put(entry.substring(0, index), entry.substring(index + 1));
}
else {
result.put(entry, null);
}
}
return result;
}
/**
* Returns the date/time that the even was logged.
*/
public Date getTimestamp() {
return this.timestamp;
}
/**
* Returns the user principal responsible for the event or {@code null}.
*/
public String getPrincipal() {
return this.principal;
}
/**
* Returns the type of event.
*/
public String getType() {
return this.type;
}
/**
* Returns the event data.
*/
public Map<String, Object> getData() {
return this.data;
}
@Override
public String toString() {
return "AuditEvent [timestamp=" + this.timestamp + ", principal="
+ this.principal + ", type=" + this.type + ", data=" + this.data + "]";
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.boot.ops.audit;
import java.util.Date;
import java.util.List;
/**
* Repository for {@link AuditEvent}s.
*
* @author Dave Syer
*/
public interface AuditEventRepository {
/**
* Find audit events relating to the specified principal since the time provided.
*
* @param principal the principal name to search for
* @param after timestamp of earliest result required
* @return audit events relating to the principal
*/
List<AuditEvent> find(String principal, Date after);
/**
* Log an event.
*
* @param event the audit event to log
*/
void add(AuditEvent event);
}

View File

@@ -0,0 +1,69 @@
/*
* 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.boot.ops.audit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* In-memory {@link AuditEventRepository} implementation.
*
* @author Dave Syer
*/
public class InMemoryAuditEventRepository implements AuditEventRepository {
private int capacity = 100;
private Map<String, List<AuditEvent>> events = new HashMap<String, List<AuditEvent>>();
/**
* @param capacity the capacity to set
*/
public void setCapacity(int capacity) {
this.capacity = capacity;
}
@Override
public List<AuditEvent> find(String principal, Date after) {
synchronized (this.events) {
return Collections.unmodifiableList(getEvents(principal));
}
}
private List<AuditEvent> getEvents(String principal) {
if (!this.events.containsKey(principal)) {
this.events.put(principal, new ArrayList<AuditEvent>());
}
return this.events.get(principal);
}
@Override
public void add(AuditEvent event) {
synchronized (this.events) {
List<AuditEvent> list = getEvents(event.getPrincipal());
while (list.size() >= this.capacity) {
list.remove(0);
}
list.add(event);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.boot.ops.audit.listener;
import java.util.Date;
import java.util.Map;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.util.Assert;
/**
* Spring {@link ApplicationEvent} to encapsulate {@link AuditEvent}s.
*
* @author Dave Syer
*/
public class AuditApplicationEvent extends ApplicationEvent {
private AuditEvent auditEvent;
/**
* Create a new {@link AuditApplicationEvent} that wraps a newly created
* {@link AuditEvent}.
* @see AuditEvent#AuditEvent(String, String, Map)
*/
public AuditApplicationEvent(String principal, String type, Map<String, Object> data) {
this(new AuditEvent(principal, type, data));
}
/**
* Create a new {@link AuditApplicationEvent} that wraps a newly created
* {@link AuditEvent}.
* @see AuditEvent#AuditEvent(String, String, String...)
*/
public AuditApplicationEvent(String principal, String type, String... data) {
this(new AuditEvent(principal, type, data));
}
/**
* Create a new {@link AuditApplicationEvent} that wraps a newly created
* {@link AuditEvent}.
* @see AuditEvent#AuditEvent(Date, String, String, Map)
*/
public AuditApplicationEvent(Date timestamp, String principal, String type,
Map<String, Object> data) {
this(new AuditEvent(timestamp, principal, type, data));
}
/**
* Create a new {@link AuditApplicationEvent} that wraps the specified
* {@link AuditEvent}.
* @param auditEvent the source of this event
*/
public AuditApplicationEvent(AuditEvent auditEvent) {
super(auditEvent);
Assert.notNull(auditEvent, "AuditEvent must not be null");
this.auditEvent = auditEvent;
}
/**
* @return the audit event
*/
public AuditEvent getAuditEvent() {
return this.auditEvent;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boot.ops.audit.listener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.boot.ops.audit.AuditEventRepository;
import org.springframework.context.ApplicationListener;
/**
* {@link ApplicationListener} that listens for {@link AuditEvent}s and stores them in a
* {@link AuditEventRepository}.
*
* @author Dave Syer
*/
public class AuditListener implements ApplicationListener<AuditApplicationEvent> {
private static Log logger = LogFactory.getLog(AuditListener.class);
private final AuditEventRepository auditEventRepository;
public AuditListener(AuditEventRepository auditEventRepository) {
this.auditEventRepository = auditEventRepository;
}
@Override
public void onApplicationEvent(AuditApplicationEvent event) {
logger.info(event.getAuditEvent());
this.auditEventRepository.add(event.getAuditEvent());
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.boot.ops.autoconfigure;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.boot.ops.audit.AuditEventRepository;
import org.springframework.boot.ops.audit.InMemoryAuditEventRepository;
import org.springframework.boot.ops.audit.listener.AuditListener;
import org.springframework.boot.ops.security.AuthenticationAuditListener;
import org.springframework.boot.ops.security.AuthorizationAuditListener;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link AuditEvent}s.
*
* @author Dave Syer
*/
@Configuration
public class AuditAutoConfiguration {
@Autowired(required = false)
private AuditEventRepository auditEventRepository = new InMemoryAuditEventRepository();
@Bean
public AuditListener auditListener() throws Exception {
return new AuditListener(this.auditEventRepository);
}
@Bean
@ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent")
public AuthenticationAuditListener authenticationAuditListener() throws Exception {
return new AuthenticationAuditListener();
}
@Bean
@ConditionalOnClass(name = "org.springframework.security.access.event.AbstractAuthorizationEvent")
public AuthorizationAuditListener authorizationAuditListener() throws Exception {
return new AuthorizationAuditListener();
}
@ConditionalOnMissingBean(AuditEventRepository.class)
protected static class AuditEventRepositoryConfiguration {
@Bean
public AuditEventRepository auditEventRepository() throws Exception {
return new InMemoryAuditEventRepository();
}
}
}

View File

@@ -0,0 +1,204 @@
/*
* 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.boot.ops.autoconfigure;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.endpoint.BeansEndpoint;
import org.springframework.boot.ops.endpoint.DumpEndpoint;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.boot.ops.endpoint.EnvironmentEndpoint;
import org.springframework.boot.ops.endpoint.HealthEndpoint;
import org.springframework.boot.ops.endpoint.InfoEndpoint;
import org.springframework.boot.ops.endpoint.MetricsEndpoint;
import org.springframework.boot.ops.endpoint.PublicMetrics;
import org.springframework.boot.ops.endpoint.ShutdownEndpoint;
import org.springframework.boot.ops.endpoint.TraceEndpoint;
import org.springframework.boot.ops.endpoint.VanillaPublicMetrics;
import org.springframework.boot.ops.health.HealthIndicator;
import org.springframework.boot.ops.health.VanillaHealthIndicator;
import org.springframework.boot.ops.metrics.InMemoryMetricRepository;
import org.springframework.boot.ops.metrics.MetricRepository;
import org.springframework.boot.ops.trace.InMemoryTraceRepository;
import org.springframework.boot.ops.trace.TraceRepository;
import org.springframework.boot.strap.bind.PropertiesConfigurationFactory;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for common management
* {@link Endpoint}s.
*
* @author Dave Syer
* @author Phillip Webb
*/
@Configuration
public class EndpointAutoConfiguration {
@Autowired(required = false)
private HealthIndicator<? extends Object> healthIndicator = new VanillaHealthIndicator();
@Autowired
private InfoPropertiesConfiguration properties;
@Autowired(required = false)
private MetricRepository metricRepository = new InMemoryMetricRepository();
@Autowired(required = false)
private PublicMetrics metrics;
@Autowired(required = false)
private TraceRepository traceRepository = new InMemoryTraceRepository();
@Bean
@ConditionalOnMissingBean
public EnvironmentEndpoint environmentEndpoint() {
return new EnvironmentEndpoint();
}
@Bean
@ConditionalOnMissingBean
public HealthEndpoint<Object> healthEndpoint() {
return new HealthEndpoint<Object>(this.healthIndicator);
}
@Bean
@ConditionalOnMissingBean
public BeansEndpoint beansEndpoint() {
return new BeansEndpoint();
}
@Bean
@ConditionalOnMissingBean
public InfoEndpoint infoEndpoint() throws Exception {
LinkedHashMap<String, Object> info = new LinkedHashMap<String, Object>();
info.putAll(this.properties.infoMap());
GitInfo gitInfo = this.properties.gitInfo();
if (gitInfo.getBranch() != null) {
info.put("git", gitInfo);
}
return new InfoEndpoint(info);
}
@Bean
@ConditionalOnMissingBean
public MetricsEndpoint metricsEndpoint() {
if (this.metrics == null) {
this.metrics = new VanillaPublicMetrics(this.metricRepository);
}
return new MetricsEndpoint(this.metrics);
}
@Bean
@ConditionalOnMissingBean
public TraceEndpoint traceEndpoint() {
return new TraceEndpoint(this.traceRepository);
}
@Bean
@ConditionalOnMissingBean
public DumpEndpoint dumpEndpoint() {
return new DumpEndpoint();
}
@Bean
@ConditionalOnMissingBean
public ShutdownEndpoint shutdownEndpoint() {
return new ShutdownEndpoint();
}
@Configuration
protected static class InfoPropertiesConfiguration {
@Autowired
private ConfigurableEnvironment environment = new StandardEnvironment();
@Value("${spring.git.properties:classpath:git.properties}")
private Resource gitProperties;
public GitInfo gitInfo() throws Exception {
PropertiesConfigurationFactory<GitInfo> factory = new PropertiesConfigurationFactory<GitInfo>(
new GitInfo());
factory.setTargetName("git");
Properties properties = new Properties();
if (this.gitProperties.exists()) {
properties = PropertiesLoaderUtils.loadProperties(this.gitProperties);
}
factory.setProperties(properties);
return factory.getObject();
}
public Map<String, Object> infoMap() throws Exception {
PropertiesConfigurationFactory<Map<String, Object>> factory = new PropertiesConfigurationFactory<Map<String, Object>>(
new LinkedHashMap<String, Object>());
factory.setTargetName("info");
factory.setPropertySources(this.environment.getPropertySources());
return factory.getObject();
}
}
public static class GitInfo {
private String branch;
private Commit commit = new Commit();
public String getBranch() {
return this.branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public Commit getCommit() {
return this.commit;
}
public static class Commit {
private String id;
private String time;
public String getId() {
return this.id == null ? "" : (this.id.length() > 7 ? this.id.substring(
0, 7) : this.id);
}
public void setId(String id) {
this.id = id;
}
public String getTime() {
return this.time;
}
public void setTime(String time) {
this.time = time;
}
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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.boot.ops.autoconfigure;
import javax.servlet.Servlet;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.config.AutoConfigureAfter;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.config.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.config.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.config.web.WebMvcAutoConfiguration;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerAdapter;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.boot.strap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.strap.context.embedded.properties.ServerProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} to enable Spring MVC to handle
* {@link Endpoint} requests. If the {@link ManagementServerProperties} specifies a
* different port to {@link ServerProperties} a new child context is created, otherwise it
* is assumed that endpoint requests will be mapped and handled via an already registered
* {@link DispatcherServlet}.
*
* @author Dave Syer
* @author Phillip Webb
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent> {
private static final Integer DISABLED_PORT = Integer.valueOf(0);
private ApplicationContext applicationContext;
@Autowired(required = false)
private ManagementServerProperties managementServerProperties = new ManagementServerProperties();
@Bean
@ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() {
EndpointHandlerMapping mapping = new EndpointHandlerMapping();
mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME);
mapping.setPrefix(this.managementServerProperties.getContextPath());
return mapping;
}
@Bean
@ConditionalOnMissingBean
public EndpointHandlerAdapter endpointHandlerAdapter() {
return new EndpointHandlerAdapter();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext() == this.applicationContext) {
if (ManagementServerPort.get(this.applicationContext) == ManagementServerPort.DIFFERENT) {
createChildManagementContext();
}
}
}
private void createChildManagementContext() {
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
childContext.setParent(this.applicationContext);
childContext.setId(this.applicationContext.getId() + ":management");
// Register the ManagementServerChildContextConfiguration first followed
// by various specific AutoConfiguration classes. NOTE: The child context
// is intentionally not completely auto-configured.
childContext.register(EndpointWebMvcChildContextConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class);
// Ensure close on the parent also closes the child
if (this.applicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) this.applicationContext)
.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (event.getApplicationContext() == EndpointWebMvcAutoConfiguration.this.applicationContext) {
childContext.close();
}
}
});
}
childContext.refresh();
}
private enum ManagementServerPort {
DISABLE, SAME, DIFFERENT;
public static ManagementServerPort get(BeanFactory beanFactory) {
ServerProperties serverProperties;
try {
serverProperties = beanFactory.getBean(ServerProperties.class);
}
catch (NoSuchBeanDefinitionException ex) {
serverProperties = new ServerProperties();
}
ManagementServerProperties managementServerProperties;
try {
managementServerProperties = beanFactory
.getBean(ManagementServerProperties.class);
}
catch (NoSuchBeanDefinitionException ex) {
managementServerProperties = new ManagementServerProperties();
}
if (DISABLED_PORT.equals(managementServerProperties.getPort())) {
return DISABLE;
}
return managementServerProperties.getPort() == null
|| serverProperties.getPort() == managementServerProperties.getPort() ? SAME
: DIFFERENT;
}
};
}

View File

@@ -0,0 +1,101 @@
/*
* 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.boot.ops.autoconfigure;
import javax.servlet.Filter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerAdapter;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.boot.strap.context.condition.ConditionalOnBean;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.boot.strap.context.condition.SearchStrategy;
import org.springframework.boot.strap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.boot.strap.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.strap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
/**
* Configuration for triggered from {@link EndpointWebMvcAutoConfiguration} when a new
* {@link EmbeddedServletContainer} running on a different port is required.
*
* @see EndpointWebMvcAutoConfiguration
*/
@Configuration
public class EndpointWebMvcChildContextConfiguration implements
EmbeddedServletContainerCustomizer {
@Autowired
private ManagementServerProperties managementServerProperties;
@Override
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
factory.setPort(this.managementServerProperties.getPort());
factory.setAddress(this.managementServerProperties.getAddress());
factory.setContextPath(this.managementServerProperties.getContextPath());
// TODO: Disable sessions
}
@Bean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// Ensure the parent configuration does not leak down to us
dispatcherServlet.setDetectAllHandlerAdapters(false);
dispatcherServlet.setDetectAllHandlerExceptionResolvers(false);
dispatcherServlet.setDetectAllHandlerMappings(false);
dispatcherServlet.setDetectAllViewResolvers(false);
return dispatcherServlet;
}
@Bean
public HandlerMapping handlerMapping() {
return new EndpointHandlerMapping();
}
@Bean
public HandlerAdapter handlerAdapter() {
return new EndpointHandlerAdapter();
}
@Configuration
@ConditionalOnClass({ EnableWebSecurity.class, Filter.class })
@ConditionalOnBean(name = "springSecurityFilterChain", search = SearchStrategy.PARENTS)
public static class EndpointWebMvcChildContextSecurityConfiguration {
// FIXME reuse of security filter here is not good. What if totally different
// security config is required. Perhaps we can just drop it on the management
// port?
@Bean
public Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) {
BeanFactory parent = beanFactory.getParentBeanFactory();
return parent.getBean("springSecurityFilterChain", Filter.class);
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.boot.ops.autoconfigure;
import javax.servlet.Servlet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.web.BasicErrorController;
import org.springframework.boot.ops.web.ErrorController;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.boot.strap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.boot.strap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.strap.context.embedded.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} to render errors via a MVC error
* controller.
*
* @author Dave Syer
*/
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustomizer {
@Value("${error.path:/error}")
private String errorPath = "/error";
@Bean
@ConditionalOnMissingBean(ErrorController.class)
public BasicErrorController basicErrorController() {
return new BasicErrorController();
}
@Override
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
factory.addErrorPages(new ErrorPage(this.errorPath));
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.boot.ops.autoconfigure;
import org.springframework.boot.config.AutoConfigureAfter;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.config.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the
* {@link ManagementServerProperties} bean.
*
* @author Dave Syer
*/
@Configuration
@AutoConfigureAfter(ServerPropertiesAutoConfiguration.class)
@EnableConfigurationProperties
public class ManagementServerPropertiesAutoConfiguration {
@Bean(name = "org.springframework.actuate.properties.ManagementServerProperties")
@ConditionalOnMissingBean
public ManagementServerProperties serverProperties() {
return new ManagementServerProperties();
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.boot.ops.autoconfigure;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.config.AutoConfigureAfter;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.metrics.CounterService;
import org.springframework.boot.ops.metrics.GaugeService;
import org.springframework.boot.strap.context.condition.ConditionalOnBean;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StopWatch;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.UrlPathHelper;
/**
* {@link EnableAutoConfiguration Auto-configuration} that records Servlet interactions
* with a {@link CounterService} and {@link GaugeService}.
*
* @author Dave Syer
* @author Phillip Webb
*/
@Configuration
@ConditionalOnBean({ CounterService.class, GaugeService.class })
@ConditionalOnClass({ Servlet.class })
@AutoConfigureAfter(MetricRepositoryAutoConfiguration.class)
public class MetricFilterAutoConfiguration {
private static final int UNDEFINED_HTTP_STATUS = 999;
@Autowired
private CounterService counterService;
@Autowired
private GaugeService gaugeService;
@Bean
public Filter metricFilter() {
return new MetricsFilter();
}
/**
* Filter that counts requests and measures processing times.
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
private final class MetricsFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if ((request instanceof HttpServletRequest)
&& (response instanceof HttpServletResponse)) {
doFilter((HttpServletRequest) request, (HttpServletResponse) response,
chain);
}
else {
chain.doFilter(request, response);
}
}
public void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
UrlPathHelper helper = new UrlPathHelper();
String suffix = helper.getPathWithinApplication(request);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
chain.doFilter(request, response);
}
finally {
stopWatch.stop();
String gaugeKey = getKey("response" + suffix);
MetricFilterAutoConfiguration.this.gaugeService.set(gaugeKey,
stopWatch.getTotalTimeMillis());
String counterKey = getKey("status." + getStatus(response) + suffix);
MetricFilterAutoConfiguration.this.counterService.increment(counterKey);
}
}
private int getStatus(HttpServletResponse response) {
try {
return response.getStatus();
}
catch (Exception ex) {
return UNDEFINED_HTTP_STATUS;
}
}
private String getKey(String string) {
// graphite compatible metric names
String value = string.replace("/", ".");
value = value.replace("..", ".");
if (value.endsWith(".")) {
value = value + "root";
}
if (value.startsWith("_")) {
value = value.substring(1);
}
return value;
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.boot.ops.autoconfigure;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.metrics.CounterService;
import org.springframework.boot.ops.metrics.DefaultCounterService;
import org.springframework.boot.ops.metrics.DefaultGaugeService;
import org.springframework.boot.ops.metrics.GaugeService;
import org.springframework.boot.ops.metrics.InMemoryMetricRepository;
import org.springframework.boot.ops.metrics.MetricRepository;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics services.
*
* @author Dave Syer
*/
@Configuration
public class MetricRepositoryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CounterService counterService() {
return new DefaultCounterService(metricRepository());
}
@Bean
@ConditionalOnMissingBean
public GaugeService gaugeService() {
return new DefaultGaugeService(metricRepository());
}
@Bean
@ConditionalOnMissingBean
protected MetricRepository metricRepository() {
return new InMemoryMetricRepository();
}
}

View File

@@ -0,0 +1,219 @@
/*
* 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.boot.ops.autoconfigure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.ops.properties.SecurityProperties;
import org.springframework.boot.ops.web.ErrorController;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
/**
* {@link EnableAutoConfiguration Auto-configuration} for security of a web application or
* service. By default everything is secured with HTTP Basic authentication except the
* {@link SecurityProperties#getIgnored() explicitly ignored} paths (defaults to
* <code>&#47;css&#47;**, &#47;js&#47;**, &#47;images&#47;**, &#47;**&#47;favicon.ico</code>
* ). Many aspects of the behavior can be controller with {@link SecurityProperties} via
* externalized application properties (or via an bean definition of that type to set the
* defaults). The user details for authentication are just placeholders
* <code>(username=user,
* password=password)</code> but can easily be customized by providing a bean definition
* of type {@link AuthenticationManager}. Also provides audit logging of authentication
* events.
*
* <p>
* The framework {@link Endpoint}s (used to expose application information to operations)
* include a {@link Endpoint#isSensitive() sensitive} configuration option which will be
* used as a security hint by the filter created here.
*
* <p>
* Some common simple customizations:
* <ul>
* <li>Switch off security completely and permanently: remove Spring Security from the
* classpath or {@link EnableAutoConfiguration#exclude() exclude} this configuration.</li>
* <li>Switch off security temporarily (e.g. for a dev environment): set
* <code>security.basic.enabled: false</code></li>
* <li>Customize the user details: add an AuthenticationManager bean</li>
* <li>Add form login for user facing resources: add a
* {@link WebSecurityConfigurerAdapter} and use {@link HttpSecurity#formLogin()}</li>
* </ul>
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ EnableWebSecurity.class })
@EnableWebSecurity
@EnableConfigurationProperties
public class SecurityAutoConfiguration {
@Bean(name = "org.springframework.actuate.properties.SecurityProperties")
@ConditionalOnMissingBean
public SecurityProperties securityProperties() {
return new SecurityProperties();
}
@Bean
@ConditionalOnMissingBean
public AuthenticationEventPublisher authenticationEventPublisher() {
return new DefaultAuthenticationEventPublisher();
}
@Bean
@ConditionalOnMissingBean({ BoostrapWebSecurityConfigurerAdapter.class })
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
return new BoostrapWebSecurityConfigurerAdapter();
}
// Give user-supplied filters a chance to be last in line
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class BoostrapWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
private static final String[] NO_PATHS = new String[0];
@Autowired
private SecurityProperties security;
@Autowired(required = false)
private EndpointHandlerMapping endpointHandlerMapping;
@Autowired
private AuthenticationEventPublisher authenticationEventPublisher;
@Autowired(required = false)
private ErrorController errorController;
@Override
protected void configure(HttpSecurity http) throws Exception {
if (this.security.isRequireSsl()) {
http.requiresChannel().anyRequest().requiresSecure();
}
if (this.security.getBasic().isEnabled()) {
String[] paths = getSecurePaths();
http.exceptionHandling().authenticationEntryPoint(entryPoint()).and()
.requestMatchers().antMatchers(paths);
http.httpBasic().and().anonymous().disable();
http.authorizeUrls().anyRequest()
.hasRole(this.security.getBasic().getRole());
}
// No cookies for service endpoints by default
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
}
private String[] getSecurePaths() {
List<String> list = new ArrayList<String>();
for (String path : this.security.getBasic().getPath()) {
path = (path == null ? "" : path.trim());
if (path.equals("/**")) {
return new String[] { path };
}
if (!path.equals("")) {
list.add(path);
}
}
// FIXME makes more sense to secure endpoints with a different role
list.addAll(Arrays.asList(getEndpointPaths(true)));
return list.toArray(new String[list.size()]);
}
private AuthenticationEntryPoint entryPoint() {
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName(this.security.getBasic().getRealm());
return entryPoint;
}
@Override
public void configure(WebSecurity builder) throws Exception {
IgnoredRequestConfigurer ignoring = builder.ignoring();
ignoring.antMatchers(this.security.getIgnored());
ignoring.antMatchers(getEndpointPaths(false));
if (this.errorController != null) {
ignoring.antMatchers(this.errorController.getErrorPath());
}
}
private String[] getEndpointPaths(boolean secure) {
if (this.endpointHandlerMapping == null) {
return NO_PATHS;
}
// FIXME this will still open up paths on the server when a management port is
// being used.
List<Endpoint<?>> endpoints = this.endpointHandlerMapping.getEndpoints();
List<String> paths = new ArrayList<String>(endpoints.size());
for (Endpoint<?> endpoint : endpoints) {
if (endpoint.isSensitive() == secure) {
paths.add(endpoint.getPath());
}
}
return paths.toArray(new String[paths.size()]);
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
AuthenticationManager manager = super.authenticationManager();
if (manager instanceof ProviderManager) {
((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
}
return manager;
}
}
@ConditionalOnMissingBean(AuthenticationManager.class)
@Configuration
public static class AuthenticationManagerConfiguration {
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new AuthenticationManagerBuilder().inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and().and()
.build();
}
}
}

View File

@@ -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 org.springframework.boot.ops.autoconfigure;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.trace.InMemoryTraceRepository;
import org.springframework.boot.ops.trace.TraceRepository;
import org.springframework.boot.strap.context.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link TraceRepository tracing}.
*
* @author Dave Syer
*/
@Configuration
public class TraceRepositoryAutoConfiguration {
@ConditionalOnMissingBean
@Bean
public TraceRepository traceRepository() {
return new InMemoryTraceRepository();
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.boot.ops.autoconfigure;
import javax.servlet.Servlet;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.config.AutoConfigureAfter;
import org.springframework.boot.config.EnableAutoConfiguration;
import org.springframework.boot.ops.trace.TraceRepository;
import org.springframework.boot.ops.trace.WebRequestTraceFilter;
import org.springframework.boot.strap.context.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link WebRequestTraceFilter
* tracing}.
*
* @author Dave Syer
*/
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@AutoConfigureAfter(TraceRepositoryAutoConfiguration.class)
public class TraceWebFilterAutoConfiguration {
@Autowired
private TraceRepository traceRepository;
@Value("${management.dump_requests:false}")
private boolean dumpRequests;
@Bean
public WebRequestTraceFilter webRequestLoggingFilter(BeanFactory beanFactory) {
WebRequestTraceFilter filter = new WebRequestTraceFilter(this.traceRepository);
filter.setDumpRequests(this.dumpRequests);
return filter;
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.boot.ops.endpoint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.http.MediaType;
/**
* Abstract base for {@link Endpoint} implementations.
*
* @author Phillip Webb
*/
public abstract class AbstractEndpoint<T> implements Endpoint<T> {
private static final MediaType[] NO_MEDIA_TYPES = new MediaType[0];
@NotNull
@Pattern(regexp = "/[^/]*", message = "Path must start with /")
private String path;
private boolean sensitive;
public AbstractEndpoint(String path) {
this(path, true);
}
public AbstractEndpoint(String path, boolean sensitive) {
this.path = path;
this.sensitive = sensitive;
}
@Override
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public boolean isSensitive() {
return this.sensitive;
}
public void setSensitive(boolean sensitive) {
this.sensitive = sensitive;
}
@Override
public MediaType[] getProduces() {
return NO_MEDIA_TYPES;
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.boot.ops.endpoint;
/**
* Tagging interface used to indicate that {@link Endpoint} that performs some action.
* Allows mappings to refine the types of request supported.
*
* @author Phillip Webb
*/
public interface ActionEndpoint<T> extends Endpoint<T> {
}

View File

@@ -0,0 +1,62 @@
/*
* 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.boot.ops.endpoint;
import org.springframework.beans.BeansException;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.LiveBeansView;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
/**
* Exposes JSON view of Spring beans. If the {@link Environment} contains a key setting
* the {@link LiveBeansView#MBEAN_DOMAIN_PROPERTY_NAME} then all application contexts in
* the JVM will be shown (and the corresponding MBeans will be registered per the standard
* behavior of LiveBeansView). Otherwise only the current application context.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false)
public class BeansEndpoint extends AbstractEndpoint<String> implements
ApplicationContextAware {
private LiveBeansView liveBeansView = new LiveBeansView();
public BeansEndpoint() {
super("/beans");
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context.getEnvironment()
.getProperty(LiveBeansView.MBEAN_DOMAIN_PROPERTY_NAME) == null) {
this.liveBeansView.setApplicationContext(context);
}
}
@Override
public MediaType[] getProduces() {
return new MediaType[] { MediaType.APPLICATION_JSON };
}
@Override
public String invoke() {
return this.liveBeansView.getSnapshotAsJson();
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boot.ops.endpoint;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
/**
* {@link Endpoint} to expose thread info.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.dump", ignoreUnknownFields = false)
public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
/**
* Create a new {@link DumpEndpoint} instance.
*/
public DumpEndpoint() {
super("/dump");
}
@Override
public List<ThreadInfo> invoke() {
return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true,
true));
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.boot.ops.endpoint;
import org.springframework.http.MediaType;
/**
* An endpoint that can be used to expose useful information to operations. Usually
* exposed via Spring MVC but could also be exposed using some other technique.
*
* @author Phillip Webb
* @author Dave Syer
*/
public interface Endpoint<T> {
/**
* Returns the path of the endpoint. Must start with '/' and should not include
* wildcards.
*/
String getPath();
/**
* Returns if the endpoint is sensitive, i.e. may return data that the average user
* should not see. Mappings can use this as a security hint.
*/
boolean isSensitive();
/**
* Returns the {@link MediaType}s that this endpoint produces or {@code null}.
*/
MediaType[] getProduces();
/**
* Called to invoke the endpoint.
* @return the results of the invocation
*/
T invoke();
}

View File

@@ -0,0 +1,86 @@
/*
* 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.boot.ops.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
/**
* {@link Endpoint} to expose {@link ConfigurableEnvironment environment} information.
*
* @author Dave Syer
* @author Phillip Webb
*/
@ConfigurationProperties(name = "endpoints.env", ignoreUnknownFields = false)
public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> implements
EnvironmentAware {
private Environment environment;
/**
* Create a new {@link EnvironmentEndpoint} instance.
*/
public EnvironmentEndpoint() {
super("/env");
}
@Override
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
for (PropertySource<?> source : getPropertySources()) {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
Map<String, Object> map = new LinkedHashMap<String, Object>();
for (String name : enumerable.getPropertyNames()) {
map.put(name, sanitize(name, enumerable.getProperty(name)));
}
result.put(source.getName(), map);
}
}
return result;
}
private Iterable<PropertySource<?>> getPropertySources() {
if (this.environment != null
&& this.environment instanceof ConfigurableEnvironment) {
return ((ConfigurableEnvironment) this.environment).getPropertySources();
}
return new StandardEnvironment().getPropertySources();
}
private Object sanitize(String name, Object object) {
if (name.toLowerCase().endsWith("password")
|| name.toLowerCase().endsWith("secret")) {
return object == null ? null : "******";
}
return object;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.boot.ops.endpoint;
import org.springframework.boot.ops.health.HealthIndicator;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/**
* {@link Endpoint} to expose application health.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.health", ignoreUnknownFields = false)
public class HealthEndpoint<T> extends AbstractEndpoint<T> {
private HealthIndicator<? extends T> indicator;
/**
* Create a new {@link HealthIndicator} instance.
*
* @param indicator the health indicator
*/
public HealthEndpoint(HealthIndicator<? extends T> indicator) {
super("/health", false);
Assert.notNull(indicator, "Indicator must not be null");
this.indicator = indicator;
}
@Override
public T invoke() {
return this.indicator.health();
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/**
* {@link Endpoint} to expose arbitrary application information.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.info", ignoreUnknownFields = false)
public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
private Map<String, ? extends Object> info;
/**
* Create a new {@link InfoEndpoint} instance.
*
* @param info the info to expose
*/
public InfoEndpoint(Map<String, ? extends Object> info) {
super("/info", true);
Assert.notNull(info, "Info must not be null");
this.info = info;
}
@Override
public Map<String, Object> invoke() {
Map<String, Object> info = new LinkedHashMap<String, Object>(this.info);
info.putAll(getAdditionalInfo());
return info;
}
protected Map<String, Object> getAdditionalInfo() {
return Collections.emptyMap();
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.boot.ops.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.ops.metrics.Metric;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/**
* {@link Endpoint} to expose {@link PublicMetrics}.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.metrics", ignoreUnknownFields = false)
public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
private PublicMetrics metrics;
/**
* Create a new {@link MetricsEndpoint} instance.
*
* @param metrics the metrics to expose
*/
public MetricsEndpoint(PublicMetrics metrics) {
super("/metrics");
Assert.notNull(metrics, "Metrics must not be null");
this.metrics = metrics;
}
@Override
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Metric metric : this.metrics.metrics()) {
result.put(metric.getName(), metric.getValue());
}
return result;
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collection;
import org.springframework.boot.ops.metrics.Metric;
/**
* Interface to expose specific {@link Metric}s via a {@link MetricsEndpoint}.
*
* @author Dave Syer
* @see VanillaPublicMetrics
*/
public interface PublicMetrics {
/**
* @return an indication of current state through metrics
*/
Collection<Metric> metrics();
}

View File

@@ -0,0 +1,83 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collections;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
/**
* {@link ActionEndpoint} to shutdown the {@link ApplicationContext}.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.shutdown", ignoreUnknownFields = false)
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> implements
ApplicationContextAware, ActionEndpoint<Map<String, Object>> {
private ConfigurableApplicationContext context;
@Autowired(required = false)
private ManagementServerProperties configuration = new ManagementServerProperties();
/**
* Create a new {@link ShutdownEndpoint} instance.
*/
public ShutdownEndpoint() {
super("/shutdown");
}
@Override
public Map<String, Object> invoke() {
if (this.configuration == null || !this.configuration.isAllowShutdown()
|| this.context == null) {
return Collections.<String, Object> singletonMap("message",
"Shutdown not enabled, sorry.");
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
// Swallow exception and continue
}
ShutdownEndpoint.this.context.close();
}
}).start();
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context instanceof ConfigurableApplicationContext) {
this.context = (ConfigurableApplicationContext) context;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.boot.ops.endpoint;
import java.util.List;
import org.springframework.boot.ops.trace.Trace;
import org.springframework.boot.ops.trace.TraceRepository;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/**
* {@link Endpoint} to expose {@link Trace} information.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.trace", ignoreUnknownFields = false)
public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
private TraceRepository repository;
/**
* Create a new {@link TraceEndpoint} instance.
*
* @param repository the trace repository
*/
public TraceEndpoint(TraceRepository repository) {
super("/trace");
Assert.notNull(repository, "Repository must not be null");
this.repository = repository;
}
@Override
public List<Trace> invoke() {
return this.repository.findAll();
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collection;
import java.util.LinkedHashSet;
import org.springframework.boot.ops.metrics.Metric;
import org.springframework.boot.ops.metrics.MetricRepository;
import org.springframework.util.Assert;
/**
* Default implementation of {@link PublicMetrics} that exposes all metrics from the
* {@link MetricRepository} along with memory information.
*
* @author Dave Syer
*/
public class VanillaPublicMetrics implements PublicMetrics {
private MetricRepository metricRepository;
public VanillaPublicMetrics(MetricRepository metricRepository) {
Assert.notNull(metricRepository, "MetricRepository must not be null");
this.metricRepository = metricRepository;
}
@Override
public Collection<Metric> metrics() {
Collection<Metric> result = new LinkedHashSet<Metric>(
this.metricRepository.findAll());
result.add(new Metric("mem", new Long(Runtime.getRuntime().totalMemory()) / 1024));
result.add(new Metric("mem.free",
new Long(Runtime.getRuntime().freeMemory()) / 1024));
result.add(new Metric("processors", Runtime.getRuntime().availableProcessors()));
return result;
}
}

View File

@@ -0,0 +1,227 @@
/*
* 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.boot.ops.endpoint.mvc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* MVC {@link HandlerAdapter} for {@link Endpoint}s. Similar in may respects to
* {@link AbstractMessageConverterMethodProcessor} but not tied to annotated methods.
*
* @author Phillip Webb
* @see EndpointHandlerMapping
*/
public final class EndpointHandlerAdapter implements HandlerAdapter {
private final Log logger = LogFactory.getLog(getClass());
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
private List<HttpMessageConverter<?>> messageConverters;
private List<MediaType> allSupportedMediaTypes;
public EndpointHandlerAdapter() {
WebMvcConfigurationSupportConventions conventions = new WebMvcConfigurationSupportConventions();
setMessageConverters(conventions.getDefaultHttpMessageConverters());
}
@Override
public boolean supports(Object handler) {
return handler instanceof Endpoint;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
handle(request, response, (Endpoint<?>) handler);
return null;
}
@SuppressWarnings("unchecked")
private void handle(HttpServletRequest request, HttpServletResponse response,
Endpoint<?> endpoint) throws Exception {
Object result = endpoint.invoke();
Class<?> resultClass = result.getClass();
List<MediaType> mediaTypes = getMediaTypes(request, endpoint, resultClass);
MediaType selectedMediaType = selectMediaType(mediaTypes);
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
try {
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter.canWrite(resultClass, selectedMediaType)) {
((HttpMessageConverter<Object>) messageConverter).write(result,
selectedMediaType, outputMessage);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Written [" + result + "] as \""
+ selectedMediaType + "\" using [" + messageConverter
+ "]");
}
return;
}
}
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
finally {
outputMessage.close();
}
}
private List<MediaType> getMediaTypes(HttpServletRequest request,
Endpoint<?> endpoint, Class<?> resultClass)
throws HttpMediaTypeNotAcceptableException {
List<MediaType> requested = getAcceptableMediaTypes(request);
List<MediaType> producible = getProducibleMediaTypes(endpoint, resultClass);
Set<MediaType> compatible = new LinkedHashSet<MediaType>();
for (MediaType r : requested) {
for (MediaType p : producible) {
if (r.isCompatibleWith(p)) {
compatible.add(getMostSpecificMediaType(r, p));
}
}
}
if (compatible.isEmpty()) {
throw new HttpMediaTypeNotAcceptableException(producible);
}
List<MediaType> mediaTypes = new ArrayList<MediaType>(compatible);
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
throws HttpMediaTypeNotAcceptableException {
List<MediaType> mediaTypes = this.contentNegotiationManager
.resolveMediaTypes(new ServletWebRequest(request));
return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL)
: mediaTypes;
}
private List<MediaType> getProducibleMediaTypes(Endpoint<?> endpoint,
Class<?> returnValueClass) {
MediaType[] mediaTypes = endpoint.getProduces();
if (mediaTypes != null && mediaTypes.length != 0) {
return Arrays.asList(mediaTypes);
}
if (this.allSupportedMediaTypes.isEmpty()) {
return Collections.singletonList(MediaType.ALL);
}
List<MediaType> result = new ArrayList<MediaType>();
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canWrite(returnValueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
return result;
}
private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) {
produceType = produceType.copyQualityValue(acceptType);
return MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceType) <= 0 ? acceptType
: produceType;
}
private MediaType selectMediaType(List<MediaType> mediaTypes) {
MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.equals(MediaType.ALL)
|| mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
return selectedMediaType;
}
public void setContentNegotiationManager(
ContentNegotiationManager contentNegotiationManager) {
this.contentNegotiationManager = contentNegotiationManager;
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
for (HttpMessageConverter<?> messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
this.allSupportedMediaTypes = new ArrayList<MediaType>(allSupportedMediaTypes);
MediaType.sortBySpecificity(this.allSupportedMediaTypes);
}
/**
* Default conventions, taken from {@link WebMvcConfigurationSupport} with a few minor
* tweaks.
*/
private static class WebMvcConfigurationSupportConventions extends
WebMvcConfigurationSupport {
public List<HttpMessageConverter<?>> getDefaultHttpMessageConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
addDefaultHttpMessageConverters(converters);
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
jacksonConverter.getObjectMapper().disable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
return converters;
}
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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.boot.ops.endpoint.mvc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.ops.endpoint.ActionEndpoint;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
/**
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getPath()}.
* Standard {@link Endpoint}s are mapped to GET requests, {@link ActionEndpoint}s are
* mapped to POST requests.
*
* @author Phillip Webb
* @see EndpointHandlerAdapter
*/
public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements
InitializingBean, ApplicationContextAware {
private List<Endpoint<?>> endpoints;
private String prefix = "";
private boolean disabled = false;
/**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}.
*/
public EndpointHandlerMapping() {
setOrder(HIGHEST_PRECEDENCE);
}
/**
* Create a new {@link EndpointHandlerMapping} with the specified endpoints.
* @param endpoints the endpoints
*/
public EndpointHandlerMapping(Collection<? extends Endpoint<?>> endpoints) {
Assert.notNull(endpoints, "Endpoints must not be null");
this.endpoints = new ArrayList<Endpoint<?>>(endpoints);
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.endpoints == null) {
this.endpoints = findEndpointBeans();
}
if (!this.disabled) {
for (Endpoint<?> endpoint : this.endpoints) {
registerHandler(this.prefix + endpoint.getPath(), endpoint);
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private List<Endpoint<?>> findEndpointBeans() {
return new ArrayList(BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), Endpoint.class).values());
}
@Override
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
Object handler = super.lookupHandler(urlPath, request);
if (handler != null) {
Object endpoint = (handler instanceof HandlerExecutionChain ? ((HandlerExecutionChain) handler)
.getHandler() : handler);
String method = (endpoint instanceof ActionEndpoint<?> ? "POST" : "GET");
if (request.getMethod().equals(method)) {
return endpoint;
}
}
return null;
}
/**
* @param prefix the prefix to set
*/
public void setPrefix(String prefix) {
Assert.isTrue("".equals(prefix) || StringUtils.startsWithIgnoreCase(prefix, "/"),
"prefix must start with '/'");
this.prefix = prefix;
}
/**
* Sets if this mapping is disabled.
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
/**
* Returns if this mapping is disabled.
*/
public boolean isDisabled() {
return this.disabled;
}
/**
* Return the endpoints
*/
public List<Endpoint<?>> getEndpoints() {
return Collections.unmodifiableList(this.endpoints);
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.boot.ops.health;
/**
* Strategy interface used to provide an indication of application health.
*
* @author Dave Syer
* @see VanillaHealthIndicator
*/
public interface HealthIndicator<T> {
/**
* @return an indication of health
*/
T health();
}

View File

@@ -0,0 +1,31 @@
/*
* 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.boot.ops.health;
/**
* Default implementation of {@link HealthIndicator} that simply returns "ok".
*
* @author Dave Syer
*/
public class VanillaHealthIndicator implements HealthIndicator<String> {
@Override
public String health() {
return "ok";
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.boot.ops.metrics;
/**
* A service that can be used to increment, decrement and reset a {@link Metric}.
*
* @author Dave Syer
*/
public interface CounterService {
/**
* Increment the specified metric by 1.
* @param metricName the name of the metric
*/
void increment(String metricName);
/**
* Decrement the specified metric by 1.
* @param metricName the name of the metric
*/
void decrement(String metricName);
/**
* Reset the specified metric to 0.
* @param metricName the name of the metric
*/
void reset(String metricName);
}

View File

@@ -0,0 +1,63 @@
/*
* 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.boot.ops.metrics;
import java.util.Date;
/**
* Default implementation of {@link CounterService}.
*
* @author Dave Syer
*/
public class DefaultCounterService implements CounterService {
private MetricRepository repository;
/**
* Create a {@link DefaultCounterService} instance.
* @param repository the underlying repository used to manage metrics
*/
public DefaultCounterService(MetricRepository repository) {
super();
this.repository = repository;
}
@Override
public void increment(String metricName) {
this.repository.increment(wrap(metricName), 1, new Date());
}
@Override
public void decrement(String metricName) {
this.repository.increment(wrap(metricName), -1, new Date());
}
@Override
public void reset(String metricName) {
this.repository.set(wrap(metricName), 0, new Date());
}
private String wrap(String metricName) {
if (metricName.startsWith("counter")) {
return metricName;
}
else {
return "counter." + metricName;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.boot.ops.metrics;
import java.util.Date;
/**
* Default implementation of {@link GaugeService}.
*
* @author Dave Syer
*/
public class DefaultGaugeService implements GaugeService {
private MetricRepository metricRepository;
/**
* @param counterRepository
*/
public DefaultGaugeService(MetricRepository counterRepository) {
super();
this.metricRepository = counterRepository;
}
@Override
public void set(String metricName, double value) {
this.metricRepository.set(wrap(metricName), value, new Date());
}
private String wrap(String metricName) {
if (metricName.startsWith("gauge")) {
return metricName;
}
else {
return "gauge." + metricName;
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.boot.ops.metrics;
/**
* A service that can be used to manage a {@link Metric} as a gauge.
*
* @author Dave Syer
*/
public interface GaugeService {
/**
* Set the specified metric value
* @param metricName the metric to set
* @param value the value of the metric
*/
void set(String metricName, double value);
}

View File

@@ -0,0 +1,83 @@
/*
* 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.boot.ops.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author Dave Syer
*/
public class InMemoryMetricRepository implements MetricRepository {
private ConcurrentMap<String, Measurement> metrics = new ConcurrentHashMap<String, Measurement>();
@Override
public void increment(String metricName, int amount, Date timestamp) {
// FIXME this might not be thread safe
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
new Measurement(timestamp, metric.increment(amount)));
}
else {
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, amount)));
}
}
@Override
public void set(String metricName, double value, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
new Measurement(timestamp, metric.set(value)));
}
else {
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, value)));
}
}
@Override
public void delete(String metricName) {
this.metrics.remove(metricName);
}
@Override
public Metric findOne(String metricName) {
if (this.metrics.containsKey(metricName)) {
return this.metrics.get(metricName).getMetric();
}
return new Metric(metricName, 0);
}
@Override
public Collection<Metric> findAll() {
ArrayList<Metric> result = new ArrayList<Metric>();
for (Measurement measurement : this.metrics.values()) {
result.add(measurement.getMetric());
}
return result;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.boot.ops.metrics;
import java.util.Date;
import org.springframework.util.ObjectUtils;
/**
* A {@link Metric} at a given point in time.
*
* @author Dave Syer
*/
public final class Measurement {
private Date timestamp;
private Metric metric;
public Measurement(Date timestamp, Metric metric) {
this.timestamp = timestamp;
this.metric = metric;
}
public Date getTimestamp() {
return this.timestamp;
}
public Metric getMetric() {
return this.metric;
}
@Override
public String toString() {
return "Measurement [dateTime=" + this.timestamp + ", metric=" + this.metric
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.timestamp);
result = prime * result + ObjectUtils.nullSafeHashCode(this.metric);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() == obj.getClass()) {
Measurement other = (Measurement) obj;
boolean result = ObjectUtils.nullSafeEquals(this.timestamp, other.timestamp);
result &= ObjectUtils.nullSafeEquals(this.metric, other.metric);
return result;
}
return super.equals(obj);
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.boot.ops.metrics;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Immutable class that can be used to hold any arbitrary system measurement value. For
* example a metric might record the number of active connections.
*
* @author Dave Syer
* @see MetricRepository
* @see CounterService
*/
public final class Metric {
private final String name;
private final double value;
/**
* Create a new {@link Metric} instance.
* @param name the name of the metric
* @param value the value of the metric
*/
public Metric(String name, double value) {
super();
Assert.notNull(name, "Name must not be null");
this.name = name;
this.value = value;
}
/**
* Returns the name of the metric.
*/
public String getName() {
return this.name;
}
/**
* Returns the value of the metric.
*/
public double getValue() {
return this.value;
}
/**
* Create a new {@link Metric} with an incremented value.
* @param amount the amount that the new metric will differ from this one
* @return a new {@link Metric} instance
*/
public Metric increment(int amount) {
return new Metric(this.name, new Double(((int) this.value) + amount));
}
/**
* Create a new {@link Metric} with a different value.
* @param value the value of the new metric
* @return a new {@link Metric} instance
*/
public Metric set(double value) {
return new Metric(this.name, value);
}
@Override
public String toString() {
return "Metric [name=" + this.name + ", value=" + this.value + "]";
}
@Override
public int hashCode() {
int valueHashCode = ObjectUtils.hashCode(this.value);
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
result = prime * result + (valueHashCode ^ (valueHashCode >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() == obj.getClass()) {
Metric other = (Metric) obj;
boolean result = ObjectUtils.nullSafeEquals(this.name, other.name);
result &= Double.doubleToLongBits(this.value) == Double
.doubleToLongBits(other.value);
return result;
}
return super.equals(obj);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.boot.ops.metrics;
import java.util.Collection;
import java.util.Date;
/**
* A Repository used to manage {@link Metric}s.
*
* @author Dave Syer
*/
public interface MetricRepository {
// FIXME perhaps revisit this, there is no way to get timestamps
// could also simply, leaving increment to counter service
// Perhaps findAll, findOne should return Measurements
// put(String name, Callback -> process(Metric)
void increment(String metricName, int amount, Date timestamp);
void set(String metricName, double value, Date timestamp);
void delete(String metricName);
Metric findOne(String metricName);
Collection<Metric> findAll();
}

View File

@@ -0,0 +1,85 @@
/*
* 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.boot.ops.properties;
import java.net.InetAddress;
import javax.validation.constraints.NotNull;
import org.springframework.boot.strap.context.embedded.properties.ServerProperties;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
/**
* Properties for the management server (e.g. port and path settings).
*
* @author Dave Syer
* @see ServerProperties
*/
@ConfigurationProperties(name = "management", ignoreUnknownFields = false)
public class ManagementServerProperties {
private Integer port;
private InetAddress address;
@NotNull
private String contextPath = "";
private boolean allowShutdown = false;
public boolean isAllowShutdown() {
return this.allowShutdown;
}
public void setAllowShutdown(boolean allowShutdown) {
this.allowShutdown = allowShutdown;
}
/**
* Returns the management port or {@code null} if the
* {@link ServerProperties#getPort() server port} should be used.
* @see #setPort(Integer)
*/
public Integer getPort() {
return this.port;
}
/**
* Sets the port of the management server, use {@code null} if the
* {@link ServerProperties#getPort() server port} should be used. To disable use 0.
*/
public void setPort(Integer port) {
this.port = port;
}
public InetAddress getAddress() {
return this.address;
}
public void setAddress(InetAddress address) {
this.address = address;
}
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.boot.ops.properties;
import org.springframework.boot.strap.context.properties.ConfigurationProperties;
import org.springframework.security.config.annotation.web.configurers.SessionCreationPolicy;
/**
* Properties for the security aspects of an application.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "security", ignoreUnknownFields = false)
public class SecurityProperties {
private boolean requireSsl;
private Basic basic = new Basic();
private SessionCreationPolicy sessions = SessionCreationPolicy.stateless;
private String[] ignored = new String[] { "/css/**", "/js/**", "/images/**",
"/**/favicon.ico" };
public SessionCreationPolicy getSessions() {
return this.sessions;
}
public void setSessions(SessionCreationPolicy sessions) {
this.sessions = sessions;
}
public Basic getBasic() {
return this.basic;
}
public void setBasic(Basic basic) {
this.basic = basic;
}
public boolean isRequireSsl() {
return this.requireSsl;
}
public void setRequireSsl(boolean requireSsl) {
this.requireSsl = requireSsl;
}
public void setIgnored(String... ignored) {
this.ignored = ignored;
}
public String[] getIgnored() {
return this.ignored;
}
public static class Basic {
private boolean enabled = true;
private String realm = "Spring";
private String[] path = new String[] { "/**" };
private String role = "USER";
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getRealm() {
return this.realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public String[] getPath() {
return this.path;
}
public void setPath(String... paths) {
this.path = paths;
}
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.boot.ops.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.boot.ops.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent;
/**
* {@link ApplicationListener} expose Spring Security {@link AbstractAuthenticationEvent
* authentication events} as {@link AuditEvent}s.
*
* @author Dave Syer
*/
public class AuthenticationAuditListener implements
ApplicationListener<AbstractAuthenticationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
if (event instanceof AbstractAuthenticationFailureEvent) {
onAuthenticationFailureEvent((AbstractAuthenticationFailureEvent) event);
}
else if (event instanceof AuthenticationSwitchUserEvent) {
onAuthenticationSwitchUserEvent((AuthenticationSwitchUserEvent) event);
}
else {
onAuthenticationEvent(event);
}
}
private void onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
data.put("type", event.getException().getClass().getName());
data.put("message", event.getException().getMessage());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_FAILURE", data));
}
private void onAuthenticationSwitchUserEvent(AuthenticationSwitchUserEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());
}
data.put("target", event.getTargetUser().getUsername());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_SWITCH", data));
}
private void onAuthenticationEvent(AbstractAuthenticationEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());
}
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_SUCCESS", data));
}
private void publish(AuditEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(new AuditApplicationEvent(event));
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.boot.ops.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.boot.ops.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.access.event.AbstractAuthorizationEvent;
import org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;
import org.springframework.security.access.event.AuthorizationFailureEvent;
/**
* {@link ApplicationListener} expose Spring Security {@link AbstractAuthorizationEvent
* authorization events} as {@link AuditEvent}s.
*
* @author Dave Syer
*/
public class AuthorizationAuditListener implements
ApplicationListener<AbstractAuthorizationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
if (event instanceof AuthenticationCredentialsNotFoundEvent) {
onAuthenticationCredentialsNotFoundEvent((AuthenticationCredentialsNotFoundEvent) event);
}
else if (event instanceof AuthorizationFailureEvent) {
onAuthorizationFailureEvent((AuthorizationFailureEvent) event);
}
}
private void onAuthenticationCredentialsNotFoundEvent(
AuthenticationCredentialsNotFoundEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
data.put("type", event.getCredentialsNotFoundException().getClass().getName());
data.put("message", event.getCredentialsNotFoundException().getMessage());
publish(new AuditEvent("<unknown>", "AUTHENTICATION_FAILURE", data));
}
private void onAuthorizationFailureEvent(AuthorizationFailureEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
data.put("type", event.getAccessDeniedException().getClass().getName());
data.put("message", event.getAccessDeniedException().getMessage());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHORIZATION_FAILURE", data));
}
private void publish(AuditEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(new AuditApplicationEvent(event));
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.boot.ops.trace;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* In-memory implementation of {@link TraceRepository}.
*
* @author Dave Syer
*/
public class InMemoryTraceRepository implements TraceRepository {
private int capacity = 100;
private List<Trace> traces = new ArrayList<Trace>();
/**
* @param capacity the capacity to set
*/
public void setCapacity(int capacity) {
this.capacity = capacity;
}
@Override
public List<Trace> findAll() {
synchronized (this.traces) {
return Collections.unmodifiableList(this.traces);
}
}
@Override
public void add(Map<String, Object> map) {
Trace trace = new Trace(new Date(), map);
synchronized (this.traces) {
while (this.traces.size() >= this.capacity) {
this.traces.remove(0);
}
this.traces.add(trace);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.boot.ops.trace;
import java.util.Date;
import java.util.Map;
import org.springframework.util.Assert;
/**
* A value object representing a trace event: at a particular time with a simple (map)
* information. Can be used for analyzing contextual information such as HTTP headers.
*
* @author Dave Syer
*/
public final class Trace {
private Date timestamp;
private Map<String, Object> info;
public Trace(Date timestamp, Map<String, Object> info) {
super();
Assert.notNull(timestamp, "Timestamp must not be null");
Assert.notNull(info, "Info must not be null");
this.timestamp = timestamp;
this.info = info;
}
public Date getTimestamp() {
return this.timestamp;
}
public Map<String, Object> getInfo() {
return this.info;
}
}

View File

@@ -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 org.springframework.boot.ops.trace;
import java.util.List;
import java.util.Map;
/**
* A repository for {@link Trace}s.
*
* @author Dave Syer
*/
public interface TraceRepository {
/**
* Find all {@link Trace} objects contained in the repository.
*/
List<Trace> findAll();
/**
* Add a new {@link Trace} object at the current time.
* @param traceInfo trace information
*/
void add(Map<String, Object> traceInfo);
}

View File

@@ -0,0 +1,147 @@
/*
* 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.boot.ops.trace;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Servlet {@link Filter} that logs all requests to a {@link TraceRepository}.
*
* @author Dave Syer
*/
public class WebRequestTraceFilter implements Filter, Ordered {
private final Log logger = LogFactory.getLog(WebRequestTraceFilter.class);
private boolean dumpRequests = false;
private final TraceRepository traceRepository;
private int order = Integer.MAX_VALUE;
private ObjectMapper objectMapper = new ObjectMapper();
/**
* @param traceRepository
*/
public WebRequestTraceFilter(TraceRepository traceRepository) {
this.traceRepository = traceRepository;
}
/**
* @param order the order to set
*/
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
/**
* Debugging feature. If enabled, and trace logging is enabled then web request
* headers will be logged.
*/
public void setDumpRequests(boolean dumpRequests) {
this.dumpRequests = dumpRequests;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
Map<String, Object> trace = getTrace(request);
this.traceRepository.add(trace);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Processing request " + request.getMethod() + " "
+ request.getRequestURI());
if (this.dumpRequests) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> headers = (Map<String, Object>) trace
.get("headers");
this.logger.trace("Headers: "
+ this.objectMapper.writeValueAsString(headers));
}
catch (JsonProcessingException ex) {
throw new IllegalStateException("Cannot create JSON", ex);
}
}
}
chain.doFilter(request, response);
}
protected Map<String, Object> getTrace(HttpServletRequest request) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
List<String> values = Collections.list(request.getHeaders(name));
Object value = values;
if (values.size() == 1) {
value = values.get(0);
}
else if (values.isEmpty()) {
value = "";
}
map.put(name, value);
}
Map<String, Object> trace = new LinkedHashMap<String, Object>();
trace.put("method", request.getMethod());
trace.put("path", request.getRequestURI());
trace.put("headers", map);
return trace;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.boot.ops.web;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.strap.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
/**
* Basic global error {@link Controller}, rendering servlet container error codes and
* messages where available. More specific errors can be handled either using Spring MVC
* abstractions (e.g. {@code @ExceptionHandler}) or by adding servlet
* {@link AbstractEmbeddedServletContainerFactory#setErrorPages(java.util.Set) container
* error pages}.
*
* @author Dave Syer
*/
@Controller
public class BasicErrorController implements ErrorController {
private static final String ERROR_KEY = "error";
private Log logger = LogFactory.getLog(BasicErrorController.class);
@Value("${error.path:/error}")
private String errorPath;
@Override
public String getErrorPath() {
return this.errorPath;
}
@RequestMapping(value = "${error.path:/error}", produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request) {
Map<String, Object> map = error(request);
return new ModelAndView(ERROR_KEY, map);
}
@RequestMapping(value = "${error.path:/error}")
@ResponseBody
public Map<String, Object> error(HttpServletRequest request) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("timestamp", new Date());
try {
Throwable error = (Throwable) request
.getAttribute("javax.servlet.error.exception");
Object obj = request.getAttribute("javax.servlet.error.status_code");
int status = 999;
if (obj != null) {
status = (Integer) obj;
map.put(ERROR_KEY, HttpStatus.valueOf(status).getReasonPhrase());
}
else {
map.put(ERROR_KEY, "None");
}
map.put("status", status);
if (error != null) {
while (error instanceof ServletException) {
error = ((ServletException) error).getCause();
}
map.put("exception", error.getClass().getName());
map.put("message", error.getMessage());
String trace = request.getParameter("trace");
if (trace != null && !"false".equals(trace.toLowerCase())) {
StringWriter stackTrace = new StringWriter();
error.printStackTrace(new PrintWriter(stackTrace));
stackTrace.flush();
map.put("trace", stackTrace.toString());
}
this.logger.error(error);
}
else {
Object message = request.getAttribute("javax.servlet.error.message");
map.put("message", message == null ? "No message available" : message);
}
return map;
}
catch (Exception ex) {
map.put(ERROR_KEY, ex.getClass().getName());
map.put("message", ex.getMessage());
this.logger.error(ex);
return map;
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.boot.ops.web;
import org.springframework.stereotype.Controller;
/**
* Marker interface used to indicate that a {@link Controller @Controller} is used to
* render errors.
*
* @author Phillip Webb
*/
public interface ErrorController {
public String getErrorPath();
}

View File

@@ -0,0 +1,11 @@
org.springframework.boot.config.EnableAutoConfiguration=\
org.springframework.boot.ops.autoconfigure.AuditAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.ErrorMvcAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.MetricFilterAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.MetricRepositoryAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.SecurityAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.TraceRepositoryAutoConfiguration,\
org.springframework.boot.ops.autoconfigure.TraceWebFilterAutoConfiguration

View File

@@ -0,0 +1,51 @@
/*
* 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.boot.ops.audit;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.ops.audit.AuditEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Tests for {@link AuditEvent}.
*
* @author Dave Syer
*/
public class AuditEventTests {
@Test
public void testNowEvent() throws Exception {
AuditEvent event = new AuditEvent("phil", "UNKNOWN", Collections.singletonMap(
"a", (Object) "b"));
assertEquals("b", event.getData().get("a"));
assertEquals("UNKNOWN", event.getType());
assertEquals("phil", event.getPrincipal());
assertNotNull(event.getTimestamp());
}
@Test
public void testConvertStringsToData() throws Exception {
AuditEvent event = new AuditEvent("phil", "UNKNOWN", "a=b", "c=d");
assertEquals("b", event.getData().get("a"));
assertEquals("d", event.getData().get("c"));
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boot.ops.audit;
import java.util.Date;
import org.junit.Test;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.boot.ops.audit.InMemoryAuditEventRepository;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link InMemoryAuditEventRepository}.
*
* @author Dave Syer
*/
public class InMemoryAuditEventRepositoryTests {
private InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository();
@Test
public void testAddToCapacity() throws Exception {
this.repository.setCapacity(2);
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
this.repository.add(new AuditEvent("dave", "UNKNOWN"));
this.repository.add(new AuditEvent("dave", "UNKNOWN"));
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
assertEquals(2, this.repository.find("phil", new Date(0L)).size());
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boot.ops.audit.listener;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.ops.audit.AuditEvent;
import org.springframework.boot.ops.audit.AuditEventRepository;
import org.springframework.boot.ops.audit.listener.AuditApplicationEvent;
import org.springframework.boot.ops.audit.listener.AuditListener;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AuditListener}.
*
* @author Phillip Webb
*/
public class AuditListenerTests {
@Test
public void testStoredEvents() {
AuditEventRepository repository = mock(AuditEventRepository.class);
AuditEvent event = new AuditEvent("principal", "type",
Collections.<String, Object> emptyMap());
AuditListener listener = new AuditListener(repository);
listener.onApplicationEvent(new AuditApplicationEvent(event));
verify(repository).add(event);
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Test;
import org.springframework.boot.ops.audit.AuditEventRepository;
import org.springframework.boot.ops.audit.InMemoryAuditEventRepository;
import org.springframework.boot.ops.autoconfigure.AuditAutoConfiguration;
import org.springframework.boot.ops.security.AuthenticationAuditListener;
import org.springframework.boot.ops.security.AuthorizationAuditListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link AuditAutoConfiguration}.
*
* @author Dave Syer
*/
public class AuditAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Test
public void testTraceConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(AuditAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(AuditEventRepository.class));
assertNotNull(this.context.getBean(AuthenticationAuditListener.class));
assertNotNull(this.context.getBean(AuthorizationAuditListener.class));
}
@Test
public void ownAutoRepository() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(Config.class, AuditAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(AuditEventRepository.class),
instanceOf(TestAuditEventRepository.class));
}
@Configuration
public static class Config {
@Bean
public TestAuditEventRepository testAuditEventRepository() {
return new TestAuditEventRepository();
}
}
public static class TestAuditEventRepository extends InMemoryAuditEventRepository {
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.ops.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.ops.endpoint.BeansEndpoint;
import org.springframework.boot.ops.endpoint.DumpEndpoint;
import org.springframework.boot.ops.endpoint.EnvironmentEndpoint;
import org.springframework.boot.ops.endpoint.HealthEndpoint;
import org.springframework.boot.ops.endpoint.InfoEndpoint;
import org.springframework.boot.ops.endpoint.MetricsEndpoint;
import org.springframework.boot.ops.endpoint.ShutdownEndpoint;
import org.springframework.boot.ops.endpoint.TraceEndpoint;
import org.springframework.boot.strap.TestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* Tests for {@link EndpointAutoConfiguration}.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class EndpointAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(EndpointAutoConfiguration.class);
this.context.refresh();
}
@Test
public void endpoints() throws Exception {
assertNotNull(this.context.getBean(BeansEndpoint.class));
assertNotNull(this.context.getBean(DumpEndpoint.class));
assertNotNull(this.context.getBean(EnvironmentEndpoint.class));
assertNotNull(this.context.getBean(HealthEndpoint.class));
assertNotNull(this.context.getBean(InfoEndpoint.class));
assertNotNull(this.context.getBean(MetricsEndpoint.class));
assertNotNull(this.context.getBean(ShutdownEndpoint.class));
assertNotNull(this.context.getBean(TraceEndpoint.class));
}
@Test
public void testInfoEndpointConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestUtils.addEnviroment(this.context, "info.foo:bar");
this.context.register(EndpointAutoConfiguration.class);
this.context.refresh();
InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class);
assertNotNull(endpoint);
assertNotNull(endpoint.invoke().get("git"));
assertEquals("bar", endpoint.invoke().get("foo"));
}
@Test
public void testNoGitProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestUtils.addEnviroment(this.context,
"spring.git.properties:classpath:nonexistent");
this.context.register(EndpointAutoConfiguration.class);
this.context.refresh();
InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class);
assertNotNull(endpoint);
assertNull(endpoint.invoke().get("git"));
}
}

View File

@@ -0,0 +1,243 @@
/*
* 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.boot.ops.autoconfigure;
import java.io.FileNotFoundException;
import java.net.SocketException;
import java.net.URI;
import java.nio.charset.Charset;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.config.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.config.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.config.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.config.web.WebMvcAutoConfiguration;
import org.springframework.boot.ops.autoconfigure.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.ops.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.ops.endpoint.AbstractEndpoint;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.boot.strap.TestUtils;
import org.springframework.boot.strap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link EndpointWebMvcAutoConfiguration}.
*
* @author Phillip Webb
* @author Greg Turnquist
*/
public class EndpointWebMvcAutoConfigurationTests {
private AnnotationConfigEmbeddedWebApplicationContext applicationContext = new AnnotationConfigEmbeddedWebApplicationContext();
@After
public void close() {
try {
this.applicationContext.close();
}
catch (Exception ex) {
}
}
@Test
public void onSamePort() throws Exception {
this.applicationContext.register(RootConfig.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", 8080, "controlleroutput");
assertContent("/endpoint", 8080, "endpointoutput");
assertContent("/controller", 8081, null);
assertContent("/endpoint", 8081, null);
this.applicationContext.close();
assertAllClosed();
}
@Test
public void onDifferentPort() throws Exception {
this.applicationContext.register(RootConfig.class, DifferentPortConfig.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", 8080, "controlleroutput");
assertContent("/endpoint", 8080, null);
assertContent("/controller", 8081, null);
assertContent("/endpoint", 8081, "endpointoutput");
this.applicationContext.close();
assertAllClosed();
}
@Test
public void disabled() throws Exception {
this.applicationContext.register(RootConfig.class, DisableConfig.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", 8080, "controlleroutput");
assertContent("/endpoint", 8080, null);
assertContent("/controller", 8081, null);
assertContent("/endpoint", 8081, null);
this.applicationContext.close();
assertAllClosed();
}
@Test
public void specificPortsViaProperties() throws Exception {
TestUtils.addEnviroment(this.applicationContext, "server.port:7070",
"management.port:7071");
this.applicationContext.register(RootConfig.class,
PropertyPlaceholderAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
WebMvcAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", 7070, "controlleroutput");
assertContent("/endpoint", 7070, null);
assertContent("/controller", 7071, null);
assertContent("/endpoint", 7071, "endpointoutput");
this.applicationContext.close();
assertAllClosed();
}
@Test
public void contextPath() throws Exception {
TestUtils.addEnviroment(this.applicationContext, "management.contextPath:/test");
this.applicationContext.register(RootConfig.class,
PropertyPlaceholderAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
WebMvcAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class);
this.applicationContext.refresh();
assertContent("/controller", 8080, "controlleroutput");
assertContent("/test/endpoint", 8080, "endpointoutput");
this.applicationContext.close();
assertAllClosed();
}
private void assertAllClosed() throws Exception {
assertContent("/controller", 8080, null);
assertContent("/endpoint", 8080, null);
assertContent("/controller", 8081, null);
assertContent("/endpoint", 8081, null);
}
public void assertContent(String url, int port, Object expected) throws Exception {
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
ClientHttpRequest request = clientHttpRequestFactory.createRequest(new URI(
"http://localhost:" + port + url), HttpMethod.GET);
try {
ClientHttpResponse response = request.execute();
try {
String actual = StreamUtils.copyToString(response.getBody(),
Charset.forName("UTF-8"));
assertThat(actual, equalTo(expected));
}
finally {
response.close();
}
}
catch (Exception ex) {
if (expected == null) {
if (SocketException.class.isInstance(ex)
|| FileNotFoundException.class.isInstance(ex)) {
return;
}
}
throw ex;
}
}
@Configuration
public static class RootConfig {
@Bean
public TestController testController() {
return new TestController();
}
@Bean
public Endpoint<String> testEndpoint() {
return new AbstractEndpoint<String>("/endpoint", false) {
@Override
public String invoke() {
return "endpointoutput";
}
};
}
}
@Controller
public static class TestController {
@RequestMapping("/controller")
@ResponseBody
public String requestMappedMethod() {
return "controlleroutput";
}
}
@Configuration
public static class DifferentPortConfig {
@Bean
public ManagementServerProperties managementServerProperties() {
ManagementServerProperties properties = new ManagementServerProperties();
properties.setPort(8081);
return properties;
}
}
@Configuration
public static class DisableConfig {
@Bean
public ManagementServerProperties managementServerProperties() {
ManagementServerProperties properties = new ManagementServerProperties();
properties.setPort(0);
return properties;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Test;
import org.springframework.boot.ops.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link ManagementServerPropertiesAutoConfiguration}.
*
* @author Phillip Webb
*/
public class ManagementServerPropertiesAutoConfigurationTests {
@Test
public void defaultManagementServerProperties() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
ManagementServerPropertiesAutoConfiguration.class);
assertThat(context.getBean(ManagementServerProperties.class).getPort(),
nullValue());
context.close();
}
@Test
public void definedManagementServerProperties() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
Config.class, ManagementServerPropertiesAutoConfiguration.class);
assertThat(context.getBean(ManagementServerProperties.class).getPort(),
equalTo(Integer.valueOf(123)));
context.close();
}
@Configuration
public static class Config {
@Bean
public ManagementServerProperties managementServerProperties() {
ManagementServerProperties properties = new ManagementServerProperties();
properties.setPort(123);
return properties;
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.boot.ops.autoconfigure;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.boot.ops.autoconfigure.MetricFilterAutoConfiguration;
import org.springframework.boot.ops.metrics.CounterService;
import org.springframework.boot.ops.metrics.GaugeService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link MetricFilterAutoConfiguration}.
*
* @author Phillip Webb
*/
public class MetricFilterAutoConfigurationTests {
@Test
public void recordsHttpInteractions() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
Config.class, MetricFilterAutoConfiguration.class);
Filter filter = context.getBean(Filter.class);
final MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/test/path");
final MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
willAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
response.setStatus(200);
return null;
}
}).given(chain).doFilter(request, response);
filter.doFilter(request, response, chain);
verify(context.getBean(CounterService.class)).increment("status.200.test.path");
verify(context.getBean(GaugeService.class)).set(eq("response.test.path"),
anyDouble());
context.close();
}
@Test
public void skipsFilterIfMissingServices() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MetricFilterAutoConfiguration.class);
assertThat(context.getBeansOfType(Filter.class).size(), equalTo(0));
context.close();
}
@Configuration
public static class Config {
@Bean
public CounterService counterService() {
return mock(CounterService.class);
}
@Bean
public GaugeService gaugeService() {
return mock(GaugeService.class);
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Test;
import org.springframework.boot.ops.autoconfigure.MetricRepositoryAutoConfiguration;
import org.springframework.boot.ops.metrics.CounterService;
import org.springframework.boot.ops.metrics.DefaultCounterService;
import org.springframework.boot.ops.metrics.DefaultGaugeService;
import org.springframework.boot.ops.metrics.GaugeService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MetricRepositoryAutoConfiguration}.
*
* @author Phillip Webb
*/
public class MetricRepositoryAutoConfigurationTests {
@Test
public void createServices() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MetricRepositoryAutoConfiguration.class);
assertNotNull(context.getBean(DefaultGaugeService.class));
assertNotNull(context.getBean(DefaultCounterService.class));
context.close();
}
@Test
public void skipsIfBeansExist() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
Config.class, MetricRepositoryAutoConfiguration.class);
assertThat(context.getBeansOfType(DefaultGaugeService.class).size(), equalTo(0));
assertThat(context.getBeansOfType(DefaultCounterService.class).size(), equalTo(0));
context.close();
}
@Configuration
public static class Config {
@Bean
public GaugeService gaugeService() {
return mock(GaugeService.class);
}
@Bean
public CounterService counterService() {
return mock(CounterService.class);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Test;
import org.springframework.boot.config.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.ops.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.ops.autoconfigure.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Tests for {@link SecurityAutoConfiguration}.
*
* @author Dave Syer
*/
public class SecurityAutoConfigurationTests {
private AnnotationConfigWebApplicationContext context;
@Test
public void testWebConfiguration() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityAutoConfiguration.class,
EndpointAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(AuthenticationManager.class));
}
@Test
public void testOverrideAuthenticationManager() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(TestConfiguration.class, SecurityAutoConfiguration.class,
EndpointAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertEquals(this.context.getBean(TestConfiguration.class).authenticationManager,
this.context.getBean(AuthenticationManager.class));
}
@Configuration
protected static class TestConfiguration {
private AuthenticationManager authenticationManager;
@Bean
public AuthenticationManager myAuthenticationManager() {
this.authenticationManager = new AuthenticationManager() {
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return new TestingAuthenticationToken("foo", "bar");
}
};
return this.authenticationManager;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Test;
import org.springframework.boot.ops.autoconfigure.TraceRepositoryAutoConfiguration;
import org.springframework.boot.ops.trace.InMemoryTraceRepository;
import org.springframework.boot.ops.trace.TraceRepository;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link TraceRepositoryAutoConfiguration}.
*
* @author Phillip Webb
*/
public class TraceRepositoryAutoConfigurationTests {
@Test
public void configuresInMemoryTraceRepository() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TraceRepositoryAutoConfiguration.class);
assertNotNull(context.getBean(InMemoryTraceRepository.class));
context.close();
}
@Test
public void skipsIfRepositoryExists() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
Config.class, TraceRepositoryAutoConfiguration.class);
assertThat(context.getBeansOfType(InMemoryTraceRepository.class).size(),
equalTo(0));
assertThat(context.getBeansOfType(TraceRepository.class).size(), equalTo(1));
context.close();
}
@Configuration
public static class Config {
@Bean
public TraceRepository traceRepository() {
return mock(TraceRepository.class);
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.boot.ops.autoconfigure;
import org.junit.Test;
import org.springframework.boot.config.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.ops.autoconfigure.TraceRepositoryAutoConfiguration;
import org.springframework.boot.ops.autoconfigure.TraceWebFilterAutoConfiguration;
import org.springframework.boot.ops.trace.WebRequestTraceFilter;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.junit.Assert.assertNotNull;
/**
* Tests for {@link TraceWebFilterAutoConfiguration}.
*
* @author Phillip Webb
*/
public class TraceWebFilterAutoConfigurationTest {
@Test
public void configureFilter() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
PropertyPlaceholderAutoConfiguration.class,
TraceRepositoryAutoConfiguration.class,
TraceWebFilterAutoConfiguration.class);
assertNotNull(context.getBean(WebRequestTraceFilter.class));
context.close();
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.boot.strap.TestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.http.MediaType;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Abstract base class for endpoint tests.
*
* @author Phillip Webb
*/
public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
protected AnnotationConfigApplicationContext context;
private final Class<?> configClass;
private final Class<?> type;
private final String path;
private final boolean sensitive;
private final String property;
private MediaType[] produces;
public AbstractEndpointTests(Class<?> configClass, Class<?> type, String path,
boolean sensitive, String property, MediaType... produces) {
this.configClass = configClass;
this.type = type;
this.path = path;
this.sensitive = sensitive;
this.property = property;
this.produces = produces;
}
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(this.configClass);
this.context.refresh();
}
@Test
public void producesMediaType() {
assertThat(getEndpointBean().getProduces(), equalTo(this.produces));
}
@Test
public void getPath() throws Exception {
assertThat(getEndpointBean().getPath(), equalTo(this.path));
}
@Test
public void isSensitive() throws Exception {
assertThat(getEndpointBean().isSensitive(), equalTo(this.sensitive));
}
@Test
public void pathOverride() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestUtils.addEnviroment(this.context, this.property + ".path:/mypath");
this.context.register(this.configClass);
this.context.refresh();
assertThat(getEndpointBean().getPath(), equalTo("/mypath"));
}
@Test
public void isSensitiveOverride() throws Exception {
this.context = new AnnotationConfigApplicationContext();
PropertySource<?> propertySource = new MapPropertySource("test",
Collections.<String, Object> singletonMap(this.property + ".sensitive",
String.valueOf(!this.sensitive)));
this.context.getEnvironment().getPropertySources().addFirst(propertySource);
this.context.register(this.configClass);
this.context.refresh();
assertThat(getEndpointBean().isSensitive(), equalTo(!this.sensitive));
}
@SuppressWarnings("unchecked")
protected T getEndpointBean() {
return (T) this.context.getBean(this.type);
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.boot.ops.endpoint;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.BeansEndpoint;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link BeansEndpoint}.
*
* @author Phillip Webb
*/
public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> {
public BeansEndpointTests() {
super(Config.class, BeansEndpoint.class, "/beans", true, "endpoints.beans",
MediaType.APPLICATION_JSON);
}
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke(), containsString("\"bean\": \"endpoint\""));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public BeansEndpoint endpoint() {
return new BeansEndpoint();
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.boot.ops.endpoint;
import java.lang.management.ThreadInfo;
import java.util.List;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.DumpEndpoint;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link DumpEndpoint}.
*
* @author Phillip Webb
*/
public class DumpEndpointTests extends AbstractEndpointTests<DumpEndpoint> {
public DumpEndpointTests() {
super(Config.class, DumpEndpoint.class, "/dump", true, "endpoints.dump");
}
@Test
public void invoke() throws Exception {
List<ThreadInfo> threadInfo = getEndpointBean().invoke();
assertThat(threadInfo.size(), greaterThan(0));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public DumpEndpoint endpoint() {
return new DumpEndpoint();
}
}
}

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.boot.ops.endpoint;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.EnvironmentEndpoint;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link EnvironmentEndpoint}.
*
* @author Phillip Webb
*/
public class EnvironmentEndpointTests extends AbstractEndpointTests<EnvironmentEndpoint> {
public EnvironmentEndpointTests() {
super(Config.class, EnvironmentEndpoint.class, "/env", true, "endpoints.env");
}
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke().size(), greaterThan(0));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public EnvironmentEndpoint endpoint() {
return new EnvironmentEndpoint();
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.boot.ops.endpoint;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.HealthEndpoint;
import org.springframework.boot.ops.health.HealthIndicator;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link HealthEndpoint}.
*
* @author Phillip Webb
*/
public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint<String>> {
public HealthEndpointTests() {
super(Config.class, HealthEndpoint.class, "/health", false, "endpoints.health");
}
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke(), equalTo("fine"));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public HealthEndpoint<String> endpoint() {
return new HealthEndpoint<String>(new HealthIndicator<String>() {
@Override
public String health() {
return "fine";
}
});
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.InfoEndpoint;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link InfoEndpoint}.
*
* @author Phillip Webb
*/
public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> {
public InfoEndpointTests() {
super(Config.class, InfoEndpoint.class, "/info", true, "endpoints.info");
}
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke().get("a"), equalTo((Object) "b"));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public InfoEndpoint endpoint() {
return new InfoEndpoint(Collections.singletonMap("a", "b"));
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collection;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.MetricsEndpoint;
import org.springframework.boot.ops.endpoint.PublicMetrics;
import org.springframework.boot.ops.metrics.Metric;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link MetricsEndpoint}.
*
* @author Phillip Webb
*/
public class MetricsEndpointTests extends AbstractEndpointTests<MetricsEndpoint> {
public MetricsEndpointTests() {
super(Config.class, MetricsEndpoint.class, "/metrics", true, "endpoints.metrics");
}
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke().get("a"), equalTo((Object) 0.5));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public MetricsEndpoint endpoint() {
final Metric metric = new Metric("a", 0.5f);
PublicMetrics metrics = new PublicMetrics() {
@Override
public Collection<Metric> metrics() {
return Collections.singleton(metric);
}
};
return new MetricsEndpoint(metrics);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.boot.ops.endpoint;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.ShutdownEndpoint;
import org.springframework.boot.ops.properties.ManagementServerProperties;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link ShutdownEndpoint}.
*
* @author Phillip Webb
*/
public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoint> {
public ShutdownEndpointTests() {
super(Config.class, ShutdownEndpoint.class, "/shutdown", true,
"endpoints.shutdown");
}
@Test
public void invoke() throws Exception {
assertThat((String) getEndpointBean().invoke().get("message"),
startsWith("Shutting down"));
assertTrue(this.context.isActive());
Thread.sleep(600);
assertFalse(this.context.isActive());
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public ManagementServerProperties managementServerProperties() {
ManagementServerProperties properties = new ManagementServerProperties();
properties.setAllowShutdown(true);
return properties;
}
@Bean
public ShutdownEndpoint endpoint() {
return new ShutdownEndpoint();
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.boot.ops.endpoint;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.TraceEndpoint;
import org.springframework.boot.ops.trace.InMemoryTraceRepository;
import org.springframework.boot.ops.trace.Trace;
import org.springframework.boot.ops.trace.TraceRepository;
import org.springframework.boot.strap.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link TraceEndpoint}.
*
* @author Phillip Webb
*/
public class TraceEndpointTests extends AbstractEndpointTests<TraceEndpoint> {
public TraceEndpointTests() {
super(Config.class, TraceEndpoint.class, "/trace", true, "endpoints.trace");
}
@Test
public void invoke() throws Exception {
Trace trace = getEndpointBean().invoke().get(0);
assertThat(trace.getInfo().get("a"), equalTo((Object) "b"));
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public TraceEndpoint endpoint() {
TraceRepository repository = new InMemoryTraceRepository();
repository.add(Collections.<String, Object> singletonMap("a", "b"));
return new TraceEndpoint(repository);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.boot.ops.endpoint;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.VanillaPublicMetrics;
import org.springframework.boot.ops.metrics.InMemoryMetricRepository;
import org.springframework.boot.ops.metrics.Metric;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link VanillaPublicMetrics}.
*
* @author Phillip Webb
*/
public class VanillaPublicMetricsTests {
@Test
public void testMetrics() throws Exception {
InMemoryMetricRepository repository = new InMemoryMetricRepository();
repository.set("a", 0.5, new Date());
VanillaPublicMetrics publicMetrics = new VanillaPublicMetrics(repository);
Map<String, Metric> results = new HashMap<String, Metric>();
for (Metric metric : publicMetrics.metrics()) {
results.put(metric.getName(), metric);
}
assertTrue(results.containsKey("mem"));
assertTrue(results.containsKey("mem.free"));
assertThat(results.get("a").getValue(), equalTo(0.5));
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.boot.ops.endpoint.mvc;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.Endpoint;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerAdapter;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link EndpointHandlerAdapter}.
*
* @author Phillip Webb
*/
public class EndpointHandlerAdapterTests {
private EndpointHandlerAdapter adapter = new EndpointHandlerAdapter();
@Test
public void onlySupportsEndpoints() throws Exception {
assertTrue(this.adapter.supports(mock(Endpoint.class)));
assertFalse(this.adapter.supports(mock(Object.class)));
}
// FIXME tests
}

View File

@@ -0,0 +1,123 @@
/*
* 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.boot.ops.endpoint.mvc;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.boot.ops.endpoint.AbstractEndpoint;
import org.springframework.boot.ops.endpoint.ActionEndpoint;
import org.springframework.boot.ops.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link EndpointHandlerMapping}.
*
* @author Phillip Webb
*/
public class EndpointHandlerMappingTests {
@Test
public void withoutPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestEndpoint endpointB = new TestEndpoint("/b");
EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA, endpointB));
mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))
.getHandler(), equalTo((Object) endpointA));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/b"))
.getHandler(), equalTo((Object) endpointB));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/c")),
nullValue());
}
@Test
public void withPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestEndpoint endpointB = new TestEndpoint("/b");
EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA, endpointB));
mapping.setPrefix("/a");
mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/a"))
.getHandler(), equalTo((Object) endpointA));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/b"))
.getHandler(), equalTo((Object) endpointB));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue());
}
@Test
public void onlyGetHttpMethodForNonActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a");
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint));
mapping.afterPropertiesSet();
assertNotNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
}
@Test
public void onlyPostHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a");
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint));
mapping.afterPropertiesSet();
assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
}
@Test
public void disabled() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpointA));
mapping.setDisabled(true);
mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue());
}
private static class TestEndpoint extends AbstractEndpoint<Object> {
public TestEndpoint(String path) {
super(path);
}
@Override
public Object invoke() {
return null;
}
}
private static class TestActionEndpoint extends TestEndpoint implements
ActionEndpoint<Object> {
public TestActionEndpoint(String path) {
super(path);
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.boot.ops.fixme;
/**
* @author Dave Syer
*/
public class ErrorConfigurationTests {
// private AnnotationConfigApplicationContext context;
//
// @Test
// public void testErrorEndpointConfiguration() throws Exception {
// this.context = new AnnotationConfigApplicationContext();
// this.context.register(ErrorConfiguration.class,
// ActuatorServerPropertiesConfiguration.class,
// PropertyPlaceholderAutoConfiguration.class);
// this.context.refresh();
// assertNotNull(this.context.getBean(ErrorEndpoint.class));
// ConfigurableEmbeddedServletContainerFactory factory = Mockito
// .mock(ConfigurableEmbeddedServletContainerFactory.class);
// this.context.getBean(EmbeddedServletContainerCustomizer.class).customize(factory);
// Mockito.verify(factory).addErrorPages(Mockito.any(ErrorPage.class));
// }
}

View File

@@ -0,0 +1,38 @@
/*
* 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.boot.ops.health;
import org.junit.Test;
import org.springframework.boot.ops.health.VanillaHealthIndicator;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link VanillaHealthIndicator}.
*
* @author Phillip Webb
*/
public class VanillaHealthIndicatorTests {
@Test
public void ok() throws Exception {
VanillaHealthIndicator healthIndicator = new VanillaHealthIndicator();
assertThat(healthIndicator.health(), equalTo("ok"));
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.boot.ops.metrics;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.ops.metrics.DefaultCounterService;
import static org.junit.Assert.fail;
/**
* Tests for {@link DefaultCounterService}.
*/
@Ignore
public class DefaultCounterServiceTests {
// FIXME
@Test
public void test() {
fail("Not yet implemented");
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.boot.ops.metrics;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.ops.metrics.DefaultGaugeService;
import static org.junit.Assert.fail;
/**
* Tests for {@link DefaultGaugeService}.
*/
@Ignore
public class DefaultGaugeServiceTests {
// FIXME
@Test
public void test() {
fail("Not yet implemented");
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.boot.ops.metrics;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.ops.metrics.InMemoryMetricRepository;
import static org.junit.Assert.fail;
/**
* Tests for {@link InMemoryMetricRepository}.
*/
@Ignore
public class InMemoryMetricRepositoryTests {
// FIXME write tests
// FIXME possibly also add Metric/Measurement tests
@Test
public void test() {
fail("Not yet implemented");
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.boot.ops.properties;
import java.util.Collections;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.ops.properties.SecurityProperties;
import org.springframework.boot.strap.bind.RelaxedDataBinder;
import org.springframework.core.convert.support.DefaultConversionService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
* Tests for {@link SecurityProperties}.
*
* @author Dave Syer
*/
public class SecurityPropertiesTests {
@Test
public void testBindingIgnoredSingleValued() {
SecurityProperties security = new SecurityProperties();
RelaxedDataBinder binder = new RelaxedDataBinder(security, "security");
binder.bind(new MutablePropertyValues(Collections.singletonMap(
"security.ignored", "/css/**")));
assertFalse(binder.getBindingResult().hasErrors());
assertEquals(1, security.getIgnored().length);
}
@Test
public void testBindingIgnoredMultiValued() {
SecurityProperties security = new SecurityProperties();
RelaxedDataBinder binder = new RelaxedDataBinder(security, "security");
binder.setConversionService(new DefaultConversionService());
binder.bind(new MutablePropertyValues(Collections.singletonMap(
"security.ignored", "/css/**,/images/**")));
assertFalse(binder.getBindingResult().hasErrors());
assertEquals(2, security.getIgnored().length);
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.boot.ops.security;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.ops.security.AuthenticationAuditListener;
import static org.junit.Assert.fail;
/**
* Tests for {@link AuthenticationAuditListener}.
*/
@Ignore
public class AuthenticationAuditListenerTests {
// FIXME
@Test
public void test() {
fail("Not yet implemented");
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.boot.ops.security;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.ops.security.AuthenticationAuditListener;
import static org.junit.Assert.fail;
/**
* Tests for {@link AuthenticationAuditListener}.
*/
@Ignore
public class AuthorizationAuditListenerTests {
// FIXME
@Test
public void test() {
fail("Not yet implemented");
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.boot.ops.trace;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.springframework.boot.ops.trace.InMemoryTraceRepository;
import org.springframework.boot.ops.trace.Trace;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link InMemoryTraceRepository}.
*
* @author Dave Syer
*/
public class InMemoryTraceRepositoryTests {
private InMemoryTraceRepository repository = new InMemoryTraceRepository();
@Test
public void capacityLimited() {
this.repository.setCapacity(2);
this.repository.add(Collections.<String, Object> singletonMap("foo", "bar"));
this.repository.add(Collections.<String, Object> singletonMap("bar", "foo"));
this.repository.add(Collections.<String, Object> singletonMap("bar", "bar"));
List<Trace> traces = this.repository.findAll();
assertEquals(2, traces.size());
assertEquals("bar", traces.get(1).getInfo().get("bar"));
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boot.ops.trace;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.ops.trace.InMemoryTraceRepository;
import org.springframework.boot.ops.trace.WebRequestTraceFilter;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link WebRequestTraceFilter}.
*
* @author Dave Syer
*/
public class WebRequestTraceFilterTests {
private WebRequestTraceFilter filter = new WebRequestTraceFilter(
new InMemoryTraceRepository());
@Test
public void filterDumpsRequest() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.addHeader("Accept", "application/json");
Map<String, Object> trace = this.filter.getTrace(request);
assertEquals("GET", trace.get("method"));
assertEquals("/foo", trace.get("path"));
assertEquals("{Accept=application/json}", trace.get("headers").toString());
}
}

View File

@@ -0,0 +1,13 @@
#Generated by Git-Commit-Id-Plugin
#Thu May 23 09:26:42 BST 2013
git.commit.id.abbrev=e02a4f3
git.commit.user.email=dsyer@vmware.com
git.commit.message.full=Update Spring
git.commit.id=e02a4f3b6f452cdbf6dd311f1362679eb4c31ced
git.commit.message.short=Update Spring
git.commit.user.name=Dave Syer
git.build.user.name=Dave Syer
git.build.user.email=dsyer@vmware.com
git.branch=develop
git.commit.time=2013-04-24T08\:42\:13+0100
git.build.time=2013-05-23T09\:26\:42+0100