Commit e8085016 authored by Phillip Webb's avatar Phillip Webb

Polish Actuator hypermedia support

parent 67dd164d
......@@ -2,25 +2,24 @@
<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>
<artifactId>spring-boot-actuator-docs</artifactId>
<packaging>jar</packaging>
<name>spring-boot-actuator-docs</name>
<description>Docs project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>1.3.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-boot-parent</relativePath>
</parent>
<artifactId>spring-boot-actuator-docs</artifactId>
<name>Spring Boot Actuator Docs</name>
<description>Spring Boot Actuator Docs</description>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<main.basedir>${basedir}/..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
......@@ -36,6 +35,7 @@
<artifactId>spring-boot-starter-hateoas</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs</artifactId>
......@@ -53,7 +53,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
......@@ -113,5 +112,4 @@
</plugin>
</plugins>
</build>
</project>
=== /autoconfig
This endpoint is a report on the Spring Boot auto-configuration process that happened when
your application started up. It lists all the `@Conditional` annotations that were
evaluated as the context started and in each case it gives an indication of if (and why)
the condition matched. A positive match results in a bean being included in the context,
and a negative result means the opposite (the beans's class may not even be loaded).
This endpoint is a report on the Spring Boot Autoconfiguration process
that happened when your application started up. It lists all the
`@Conditional` annotations that were evaluated as the context started
and in each case it gives an indication of if (and why) the condition
matched. A positive match results in a bean being included in the context,
and a negative result means the opposite (the beans's class may not even
be loaded).
The report is split into 2 parts, positive matches first, and then negative. If the
context is a hierarchy, there is also a separate report on the parent context with the
same format (and recursively up to the top of the hierarchy).
The report is split into 2 parts, positive matches first, and then negative.
If the context is a hierarchy, there is also a separate report on the parent
context with the same format (and recursively up to the top of the hierarchy).
NOTE: the report is actually about `@Conditional` evaluation not autoconfiguration
per se, but most autoconfiguration features use `@Conditional` heavily, so there is
a lot of overlap.
NOTE: the report is actually about `@Conditional` evaluation not auto-configuration
per se, but most autoconfiguration features use `@Conditional` heavily, so there is a lot
of overlap.
Example curl request:
include::{generated}/autoconfig/curl-request.adoc[]
......
=== /beans
This endpoint is a report on the Spring Boot `ApplicationContext`. It lists the beans in
the context and their dependencies, detailing the names and concrete classes of each bean.
This endpoint is a report on the Spring Boot `ApplicationContext`. It lists
the beans in the context and their dependencies, detailing the names and
concrete classes of each bean.
NOTE: some beans are pure configuration (any class that is annotated
`@Configuration`).
NOTE: some beans are pure configuration (any class that is annotated `@Configuration`).
Example curl request:
include::{generated}/beans/curl-request.adoc[]
......
=== /configprops
This endpoint is a report on the Spring Boot `@ConfigurationProperties`
beans. Beans with this annotation are bound to the `Environment` on
startup, so they reflect the externalised configuration of the application.
Beans are listed by name.
A bean that is added using `@EnableConfigurationProperties` will have
a conventional name: `<prefix>.CONFIGURATION_PROPERTIES`, where
`<prefix>` is the environment key prefix specified in the
`@ConfigurationProperties` annotation.
This endpoint is a report on the Spring Boot `@ConfigurationProperties` beans. Beans with
this annotation are bound to the `Environment` on startup, so they reflect the
externalised configuration of the application. Beans are listed by name. A bean that is
added using `@EnableConfigurationProperties` will have a conventional name:
`<prefix>.CONFIGURATION_PROPERTIES`, where `<prefix>` is the environment key prefix
specified in the `@ConfigurationProperties` annotation.
Example curl request:
include::{generated}/configprops/curl-request.adoc[]
......
=== /dump
This endpoint is a thread dump: the result is a list of threads each with their name,
monitor state and stack. It is the same information as you would get from `kill -3` of a
running Java process. Can be very useful for detecting issues at runtime, especially
sluggish behaviour caused by threads blocked by slow or unavailable I/O (e.g. if a
connection pool is exhausted).
This endpoint is a thread dump: the result is a list of threads each with
their name, monitor state and stack. It is the same information as you would
get from `kill -3` of a running Java process. Can be very useful for detecting
issues at runtime, especially sluggish behaviour caused by threads blocked
by slow or unavailable I/O (e.g. if a connection pool is exhausted).
NOTE: some `SecurityManager` implementations might prevent this endpoint
from working.
NOTE: some `SecurityManager` implementations might prevent this endpoint from working.
Example curl request:
include::{generated}/dump/curl-request.adoc[]
......
=== /env
This endpoint is a dump of the Spring `Environment`. It lists the active
profiles and all the `PropertySources` in the `Environment` (the ones that
are listed first take precedence when binding to `@ConfigurationProperties`
or `@Value`). Normally you will see the Java `System` properties and the
OS environment variables in their own `PropertySources` plus any `.properties`
or `.yml` files used to configure the application on start up.
This endpoint is a dump of the Spring `Environment`. It lists the active profiles and all
the `PropertySources` in the `Environment` (the ones that are listed first take precedence
when binding to `@ConfigurationProperties` or `@Value`). Normally you will see the Java
`System` properties and the OS environment variables in their own `PropertySources` plus
any `.properties` or `.yml` files used to configure the application on start up.
Example curl request:
include::{generated}/env/curl-request.adoc[]
......
=== /health
This endpoint is an indication of the health of the application. It has an overall status
("UP", "DOWN" etc.), which is the only thing you see unless either you are authenticated
or the endpoint is marked as `sensitive=false` (`endpoints.health.sensitive=false`).
This endpoint is an indication of the health of the application.
It has an overall status ("UP", "DOWN" etc.), which is the only thing
you see unless either you are authenticated or the endpoint is marked
as `sensitive=false` (`endpoints.health.sensitive=false`).
The HTTP code in the response reflects the status (e.g. "UP" = 200,
"OUT_OF_SERVICE"=503, "DOWN"=503). The mappings can be changed by
configuring `endpoints.health.mapping.<STATUS>=XXX`.
The HTTP code in the response reflects the status (e.g. "`UP`"=200,
"`OUT_OF_SERVICE`"=503, "`DOWN`"=503). The mappings can be changed by configuring
`endpoints.health.mapping.<STATUS>=XXX`.
Example curl request:
include::{generated}/health/curl-request.adoc[]
......
......@@ -10,12 +10,14 @@ The way that endpoints are exposed will depend on the type of technology that yo
Most applications choose HTTP monitoring, where the ID of the endpoint is mapped
to a URL. For example, by default, the `health` endpoint will be mapped to `/health`.
== List of Endpoints
== List of Endpoints
include::{generated}/endpoints.adoc[]
=== /logfile
=== /logfile
This endpoint (if available) contains the plain text logfile configured by the user
using `logging.file` or `logging.path` (by default logs are only emitted on stdout
so one of these properties has to be set for this endpoint to be active).
......@@ -29,21 +31,23 @@ include::{generated}/logfile/http-request.adoc[]
Example HTTP response:
include::{generated}/logfile/http-response.adoc[]
=== /docs
=== /docs
This endpoint (if available) contains HTML documemtation for the other endpoints. Its path
can be "/docs" (if there is an existing home page) or "/" (otherwise, including if the
can be "/docs" (if there is an existing home page) or "/" (otherwise, including if the
HAL browser is not active).
== Hypermedia Support
If https://projects.spring.io/spring-hateoas[Spring HATEOAS] is enabled
(i.e. if it is on the classpath by default) then the Actuator
endpoint responses are enhanced with hypermedia in the form of "links". The default
media type for responses is http://stateless.co/hal_specification.html[HAL], resulting
in each resource having an extra property called "_links". You can change the
media type to another one supported by Spring HATEOAS by providing your own
`@EnableHypermedia` annotation and custom providers as necessary.
== Hypermedia Support
If https://projects.spring.io/spring-hateoas[Spring HATEOAS] is enabled (i.e. if it is
on the classpath by default) then the Actuator endpoint responses are enhanced with
hypermedia in the form of "links". The default media type for responses is
http://stateless.co/hal_specification.html[HAL], resulting in each resource having an
extra property called "_links". You can change the media type to another one supported by
Spring HATEOAS by providing your own `@EnableHypermedia` annotation and custom providers
as necessary.
Example enhanced "/metrics" endpoint with additional "_links":
......@@ -55,94 +59,103 @@ The easiest way to avoid that is to use a `management.contextPath`, e.g. "/admin
TIP: You can disable the hypermedia support in Actuator endpoints by setting
`endpoints.links.enabled=false`.
=== Default home page
If the `management.contextPath` is empty, or if the home page provided
by the application happens to be a response body of type `ResourceSupport`, then it will
be enhanced with links to the actuator endpoints. The latter would happen for instance
if you use Spring Data REST to expose `Repository` endpoints.
If the `management.contextPath` is empty, or if the home page provided by the application
happens to be a response body of type `ResourceSupport`, then it will be enhanced with
links to the actuator endpoints. The latter would happen for instance if you use Spring
Data REST to expose `Repository` endpoints.
Example vanilla "/" endpoint if the `management.contextPath` is empty (the "/admin"
page would be the same with different links if `management.contextPath=/admin`):
Example vanilla "/" endpoint if the `management.contextPath` is empty (the "/admin" page
would be the same with different links if `management.contextPath=/admin`):
include::{generated}/admin/http-response.adoc[]
=== Endpoints with format changes
Some endpoints in their "raw" form consist of an array (e.g. the "/beans" and the "/trace" endpoints).
These need to be converted to objects (maps) before they can be enhanced with
links, so their contents are inserted as a field named "content".
Example enhanced "/beans" endpoint with additional "_links":
=== Endpoints with format changes
Some endpoints in their "`raw`" form consist of an array (e.g. the `/beans` and the
`/trace` endpoints). These need to be converted to objects (maps) before they can be
enhanced with links, so their contents are inserted as a field named "`content`".
Example enhanced `/beans` endpoint with additional `_links`:
include::{generated}/beans/hypermedia/http-response.adoc[]
== HAL Browser
If Hypermedia is enabled and the HAL format is in use (which is the default), then you
can provide a browser for the resources by including a dependency on the
https://github.com/mikekelly/hal-browser[HAL browser] webjar.
If Hypermedia is enabled and the HAL format is in use (which is the default), then
you can provide a browser for the resources by including a dependency
on the https://github.com/mikekelly/hal-browser[HAL browser] webjar.
For example in Maven:
[source,xml]
[source,xml,indent=0]
----
<dependency>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
</dependency>
----
or in Gradle
[source,groovy]
[source,groovy,indent=0]
----
dependencies {
...
compile('org.webjars:hal-browser')
...
}
dependencies {
...
compile('org.webjars:hal-browser')
...
}
----
NOTE: if you are using Spring Data REST, then a dependency on the `spring-data-rest-hal-browser`
will have an equivalent effect.
NOTE: if you are using Spring Data REST, then a dependency on the
`spring-data-rest-hal-browser` will have an equivalent effect.
If you do that then a new endpoint will appear at `/` or `/hal` (relative to the
`management.contextPath`) serving up a static HTML page with some JavaScript that lets you
browse the available resources. The default endpoint path depends on whether or not there
is already a static home page ("`index.html`") - if there is not and the
`management.contextPath` is empty, then the HAL browser shows up on the home page.
If you do that then a new endpoint will appear at "/" or "/hal" (relative to the `management.contextPath`)
serving up a static HTML page with some JavaScript that lets you browse the available
resources. The default endpoint path depends on whether or not there is already a static home page
("index.html") - if there is not and the `management.contextPath` is empty, then the HAL browser
shows up on the home page. Example:
Example:
image::hal-browser.png[HAL Browser]
TIP: The endpoint path can always, as with all MVC endpoints, be overridden using
`endpoints.hal.path=/yourpath` (note the leading slash).
`endpoints.hal.path=/yourpath` (note the leading slash).
== Actuator Documentation Browser
You can also provide a browser for the standard generated documentation for the Actuator
endpoints by including a dependency on the documentation jar.
You can also provide a browser for the standard generated documentation
for the Actuator endpoints by including a dependency on the documentation jar.
For example in Maven:
[source,xml]
[source,xml,indent=0]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-hypermedia-docs</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-hypermedia-docs</artifactId>
</dependency>
----
or in Gradle
[source,groovy]
[source,groovy,indent=0]
----
dependencies {
...
compile('org.springframework.boot:spring-boot-hypermedia-docs')
...
}
dependencies {
...
compile('org.springframework.boot:spring-boot-hypermedia-docs')
...
}
----
If you do that then a new endpoint at "/" or "/docs" (relative to the `management.contextPath`)
will serve up a static HTML page with this documentation in it. The default endpoint path depends
on whether or not there is already a static home page
("index.html" or a HAL browser) - if there is not and the `management.contextPath` is empty,
then the docs browser shows up on the home page.
\ No newline at end of file
If you do that then a new endpoint at `/` or `/docs` (relative to the
`management.contextPath`) will serve up a static HTML page with this documentation in it.
The default endpoint path depends on whether or not there is already a static home page
("index.html" or a HAL browser) - if there is not and the `management.contextPath` is
empty, then the docs browser shows up on the home page.
=== /info
This endpoint is empty and marked as `sensitive=false`
by default (so it is unauthenticated by default if Spring
Security is in use). It reflects the content of the `info.*` properties
in the `Environment`, as well as the properties in `git.properties`
This endpoint is empty and marked as `sensitive=false` by default (so it is
unauthenticated by default if Spring Security is in use). It reflects the content of the
`info.*` properties in the `Environment`, as well as the properties in `git.properties`
if such a file exists in the root of the classpath.
Example curl request:
......
=== /mappings
This endpoint lists the Spring MVC request mappings, so users can
see the handlers registered for requests by path, method, media type,
etc.
This endpoint lists the Spring MVC request mappings, so users can see the handlers
registered for requests by path, method, media type, etc.
Example curl request:
include::{generated}/mappings/curl-request.adoc[]
......
=== /metrics
This endpoint lists the public metrics exposed by the application.
By default this includes all the counters in the `CounterService`
and all the gauges in the `GaugeService`, plus a few JVM metrics about
memory and uptime. Users can register additional sources by creating
beans of type `PublicMetrics` and/or by registering counters and
gauges.
This endpoint lists the public metrics exposed by the application. By default this
includes all the counters in the `CounterService` and all the gauges in the
`GaugeService`, plus a few JVM metrics about memory and uptime. Users can register
additional sources by creating beans of type `PublicMetrics` and/or by registering
counters and gauges.
Example curl request:
include::{generated}/metrics/curl-request.adoc[]
......
=== /trace
This endpoint lists contents of the `TraceRepository` (which
users can override by providing a bean of that type, or by
injecting that bean and adding stuff to it). By default
it is the last 100 HTTP requests, including all headers in the
request and response, and the path and HTTP status.
This endpoint lists contents of the `TraceRepository` (which users can override by
providing a bean of that type, or by injecting that bean and adding stuff to it). By
default it is the last 100 HTTP requests, including all headers in the request and
response, and the path and HTTP status.
Example curl request:
include::{generated}/trace/curl-request.adoc[]
......
/*
* Copyright 2012-2015 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.actuate.hypermedia.test;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import groovy.text.Template;
import groovy.text.TemplateEngine;
......@@ -40,6 +52,11 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -82,8 +99,7 @@ public class EndpointDocumentation {
@Test
public void logfile() throws Exception {
this.mockMvc.perform(get("/logfile").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("logfile"));
.andExpect(status().isOk()).andDo(document("logfile"));
}
@Test
......@@ -98,10 +114,10 @@ public class EndpointDocumentation {
final String endpointPath = StringUtils.hasText(endpoint.getPath()) ? endpoint
.getPath() : "/";
if (!endpointPath.equals("/docs") && !endpointPath.equals("/logfile")) {
String output = endpointPath.substring(1);
output = output.length() > 0 ? output : "./";
this.mockMvc
if (!endpointPath.equals("/docs") && !endpointPath.equals("/logfile")) {
String output = endpointPath.substring(1);
output = output.length() > 0 ? output : "./";
this.mockMvc
.perform(get(endpointPath).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document(output))
.andDo(new ResultHandler() {
......@@ -111,7 +127,7 @@ public class EndpointDocumentation {
endpoints.add(endpoint);
}
});
}
}
}
File file = new File(this.restdocsOutputDir + "/endpoints.adoc");
file.getParentFile().mkdirs();
......
/*
* Copyright 2012-2015 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.actuate.hypermedia.test;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import groovy.text.TemplateEngine;
import org.junit.Before;
......@@ -22,11 +34,16 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"endpoints.health.sensitive=false" })
"endpoints.health.sensitive=false" })
@DirtiesContext
public class HealthEndpointDocumentation {
......@@ -49,15 +66,13 @@ public class HealthEndpointDocumentation {
System.setProperty("org.springframework.restdocs.outputDir",
this.restdocsOutputDir);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration())
.build();
.apply(documentationConfiguration()).build();
}
@Test
public void health() throws Exception {
this.mockMvc.perform(get("/health").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("health/unsensitive"));
.andExpect(status().isOk()).andDo(document("health/unsensitive"));
}
}
/*
* Copyright 2012-2015 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.actuate.hypermedia.test;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import groovy.text.TemplateEngine;
import org.junit.Before;
......@@ -22,6 +34,11 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -48,29 +65,25 @@ public class HypermediaEndpointDocumentation {
System.setProperty("org.springframework.restdocs.outputDir",
this.restdocsOutputDir);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration())
.build();
.apply(documentationConfiguration()).build();
}
@Test
public void beans() throws Exception {
this.mockMvc.perform(get("/beans").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("beans/hypermedia"));
.andExpect(status().isOk()).andDo(document("beans/hypermedia"));
}
@Test
public void metrics() throws Exception {
this.mockMvc.perform(get("/metrics").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("metrics/hypermedia"));
.andExpect(status().isOk()).andDo(document("metrics/hypermedia"));
}
@Test
public void home() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("admin"));
.andExpect(status().isOk()).andDo(document("admin"));
}
}
/*
* Copyright 2012-2015 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.actuate.hypermedia.test;
import groovy.text.GStringTemplateEngine;
......@@ -18,4 +34,5 @@ public class SpringBootHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
......@@ -88,9 +88,9 @@ import org.springframework.web.servlet.DispatcherServlet;
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnWebApplication
@AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
HypermediaAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class })
EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
HypermediaAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class })
public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
SmartInitializingSingleton {
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -16,21 +16,17 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorDocsEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalBrowserEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HypermediaDisabled;
import org.springframework.boot.actuate.endpoint.mvc.LinksEnhancer;
import org.springframework.boot.actuate.endpoint.mvc.LinksMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
......@@ -78,11 +74,13 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* Autoconfiguration for hypermedia in HTTP endpoints.
* Configuration for hypermedia in HTTP endpoints.
*
* @author Dave Syer
*
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass(Link.class)
......@@ -124,10 +122,14 @@ public class EndpointWebMvcHypermediaConfiguration {
return new DefaultCurieProvider("boot", new UriTemplate(path));
}
/**
* {@link SpringBootCondition} to detect the Spring Data REST HAL browser.
*/
@Configuration("EndpointHypermediaAutoConfiguration.MissingResourceCondition")
@ConditionalOnResource(resources = "classpath:/META-INF/spring-data-rest/hal-browser/index.html")
protected static class MissingSpringDataRestResourceCondition extends
SpringBootCondition {
SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
......@@ -136,44 +138,46 @@ public class EndpointWebMvcHypermediaConfiguration {
return ConditionOutcome.noMatch("Spring Data REST HAL browser found");
}
return ConditionOutcome.match("Spring Data REST HAL browser not found");
}
}
/**
* Configuration for Endpoint links.
*/
@ConditionalOnProperty(value = "endpoints.links.enabled", matchIfMissing = true)
public static class LinksConfiguration {
@Bean
public LinksMvcEndpoint linksMvcEndpoint(ResourceProperties resources) {
return new LinksMvcEndpoint(resources.getWelcomePage() != null ? "/links"
: "");
String defaultPath = (resources.getWelcomePage() != null ? "/links" : "");
return new LinksMvcEndpoint(defaultPath);
}
/**
* Controller advice that adds links to the home page and/or the management
* context path. The home page is enhanced if it is composed already of a
* {@link ResourceSupport} (e.g. when using Spring Data REST).
*
* @author Dave Syer
*
*/
@ControllerAdvice
public static class HomePageLinksAdvice implements ResponseBodyAdvice<Object> {
@Autowired
MvcEndpoints endpoints;
private MvcEndpoints endpoints;
@Autowired
LinksMvcEndpoint linksEndpoint;
private LinksMvcEndpoint linksEndpoint;
@Autowired
ManagementServerProperties management;
private ManagementServerProperties management;
private LinksEnhancer linksEnhancer;
@PostConstruct
public void init() {
this.linksEnhancer = new LinksEnhancer(this.endpoints,
this.management.getContextPath());
this.linksEnhancer = new LinksEnhancer(this.management.getContextPath(),
this.endpoints);
}
@Override
......@@ -196,33 +200,31 @@ public class EndpointWebMvcHypermediaConfiguration {
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
HttpServletRequest servletRequest = null;
ServerHttpRequest request, ServerHttpResponse response) {
if (request instanceof ServletServerHttpRequest) {
servletRequest = ((ServletServerHttpRequest) request)
.getServletRequest();
Object pattern = servletRequest
.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (pattern != null) {
String path = pattern.toString();
if (isHomePage(path) || isManagementPath(path)
|| isLinksPath(path)) {
ResourceSupport resource = (ResourceSupport) body;
if (isHomePage(path) && hasManagementPath()) {
String rel = this.management.getContextPath()
.substring(1);
resource.add(linkTo(
EndpointWebMvcHypermediaConfiguration.class)
.slash(this.management.getContextPath()).withRel(
rel));
}
else {
this.linksEnhancer.addEndpointLinks(resource, "");
}
beforeBodyWrite(body, (ServletServerHttpRequest) request);
}
return body;
}
private void beforeBodyWrite(Object body, ServletServerHttpRequest request) {
Object pattern = request.getServletRequest().getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (pattern != null) {
String path = pattern.toString();
if (isHomePage(path) || isManagementPath(path) || isLinksPath(path)) {
ResourceSupport resource = (ResourceSupport) body;
if (isHomePage(path) && hasManagementPath()) {
String rel = this.management.getContextPath().substring(1);
resource.add(linkTo(
EndpointWebMvcHypermediaConfiguration.class).slash(
this.management.getContextPath()).withRel(rel));
}
else {
this.linksEnhancer.addEndpointLinks(resource, "");
}
}
}
return body;
}
private boolean hasManagementPath() {
......@@ -245,29 +247,26 @@ public class EndpointWebMvcHypermediaConfiguration {
}
/**
* Controller advice that adds links to the existing Actuator endpoints. By default
* all the top-level resources are enhanced with a "self" link. Those resources that
* could not be enhanced (e.g. "/env/{name}") because their values are "primitive" are
* ignored. Those that have values of type Collection (e.g. /trace) are transformed in
* to maps, and the original collection value is added with a key equal to the
* endpoint name.
*
* @author Dave Syer
*
* Controller advice that adds links to the existing Actuator endpoints. By
* default all the top-level resources are enhanced with a "self" link. Those
* resources that could not be enhanced (e.g. "/env/{name}") because their values
* are "primitive" are ignored. Those that have values of type Collection (e.g.
* /trace) are transformed in to maps, and the original collection value is added
* with a key equal to the endpoint name.
*/
@ControllerAdvice(assignableTypes = MvcEndpoint.class)
public static class MvcEndpointAdvice implements ResponseBodyAdvice<Object> {
@Autowired
ManagementServerProperties management;
private ManagementServerProperties management;
@Autowired
HttpMessageConverters converters;
private HttpMessageConverters converters;
private Map<MediaType, HttpMessageConverter<?>> converterCache = new ConcurrentHashMap<MediaType, HttpMessageConverter<?>>();
@Autowired
ObjectMapper mapper;
private ObjectMapper mapper;
@Override
public boolean supports(MethodParameter returnType,
......@@ -281,71 +280,71 @@ public class EndpointWebMvcHypermediaConfiguration {
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body == null) {
// Assume it already was handled
return body;
ServerHttpRequest request, ServerHttpResponse response) {
if (request instanceof ServletServerHttpRequest) {
return beforeBodyWrite(body, returnType, selectedContentType,
selectedConverterType, (ServletServerHttpRequest) request,
response);
}
return body;
}
if (body instanceof Resource) {
// Assume it already has its links
private Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServletServerHttpRequest request, ServerHttpResponse response) {
if (body == null || body instanceof Resource) {
// Assume it already was handled or it already has its links
return body;
}
@SuppressWarnings("unchecked")
HttpMessageConverter<Object> converter = (HttpMessageConverter<Object>) findConverter(
HttpMessageConverter<Object> converter = findConverter(
selectedConverterType, selectedContentType);
if (converter == null) {
if (converter == null || isHypermediaDisabled(returnType)) {
// Not a resource that can be enhanced with a link
return body;
}
if (AnnotationUtils.findAnnotation(returnType.getMethod(),
HypermediaDisabled.class) != null
|| AnnotationUtils.findAnnotation(returnType.getMethod()
.getDeclaringClass(), HypermediaDisabled.class) != null) {
return body;
}
HttpServletRequest servletRequest = null;
if (request instanceof ServletServerHttpRequest) {
servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
String path = (String) servletRequest
.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (path == null) {
path = "";
}
try {
converter.write(new EndpointResource(body, path),
selectedContentType, response);
}
catch (IOException e) {
throw new HttpMessageNotWritableException("Cannot write response", e);
}
return null;
String path = getPath(request);
try {
converter.write(new EndpointResource(body, path),
selectedContentType, response);
}
else {
return body;
catch (IOException ex) {
throw new HttpMessageNotWritableException("Cannot write response", ex);
}
return null;
}
private HttpMessageConverter<?> findConverter(
@SuppressWarnings("unchecked")
private HttpMessageConverter<Object> findConverter(
Class<? extends HttpMessageConverter<?>> selectedConverterType,
MediaType mediaType) {
MediaType mediaType) {
if (this.converterCache.containsKey(mediaType)) {
return this.converterCache.get(mediaType);
return (HttpMessageConverter<Object>) this.converterCache
.get(mediaType);
}
for (HttpMessageConverter<?> converter : this.converters) {
if (selectedConverterType.isAssignableFrom(converter.getClass())
&& converter.canWrite(EndpointResource.class, mediaType)) {
this.converterCache.put(mediaType, converter);
return converter;
return (HttpMessageConverter<Object>) converter;
}
}
return null;
}
private boolean isHypermediaDisabled(MethodParameter returnType) {
return AnnotationUtils.findAnnotation(returnType.getMethod(),
HypermediaDisabled.class) != null
|| AnnotationUtils.findAnnotation(returnType.getMethod()
.getDeclaringClass(), HypermediaDisabled.class) != null;
}
private String getPath(ServletServerHttpRequest request) {
String path = (String) request.getServletRequest().getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
return (path == null ? "" : path);
}
}
}
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -14,48 +14,51 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
package org.springframework.boot.actuate.autoconfigure;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.util.StringUtils;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* @author Dave Syer
* Adds enpoints links to {@link ResourceSupport}.
*
* @author Dave Syer
*/
public class LinksEnhancer {
class LinksEnhancer {
private MvcEndpoints endpoints;
private final String rootPath;
private String rootPath;
private final MvcEndpoints endpoints;
public LinksEnhancer(MvcEndpoints endpoints, String rootPath) {
this.endpoints = endpoints;
public LinksEnhancer(String rootPath, MvcEndpoints endpoints) {
this.rootPath = rootPath;
this.endpoints = endpoints;
}
public void addEndpointLinks(ResourceSupport resource, String self) {
if (!resource.hasLink("self")) {
resource.add(linkTo(LinksEnhancer.class).slash(
this.rootPath + self).withSelfRel());
resource.add(linkTo(LinksEnhancer.class).slash(this.rootPath + self)
.withSelfRel());
}
for (MvcEndpoint endpoint : this.endpoints.getEndpoints()) {
if (endpoint.getPath().equals(self)) {
continue;
}
Class<?> type = endpoint.getEndpointType();
if (type == null) {
type = Object.class;
}
String path = endpoint.getPath();
String rel = path.startsWith("/") ? path.substring(1) : path;
if (StringUtils.hasText(rel)) {
resource.add(linkTo(type).slash(this.rootPath + endpoint.getPath())
.withRel(rel));
if (!endpoint.getPath().equals(self)) {
addEndpointLink(resource, endpoint);
}
}
}
private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint) {
Class<?> type = endpoint.getEndpointType();
type = (type == null ? Object.class : type);
String path = endpoint.getPath();
String rel = (path.startsWith("/") ? path.substring(1) : path);
if (StringUtils.hasText(rel)) {
String fullPath = this.rootPath + endpoint.getPath();
resource.add(linkTo(type).slash(fullPath).withRel(rel));
}
}
}
......@@ -103,7 +103,7 @@ public class ManagementSecurityAutoConfiguration {
@Configuration
protected static class ManagementSecurityPropertiesConfiguration implements
SecurityPrerequisite {
SecurityPrerequisite {
@Autowired(required = false)
private SecurityProperties security;
......@@ -115,7 +115,7 @@ public class ManagementSecurityAutoConfiguration {
public void init() {
if (this.management != null && this.security != null) {
this.security.getUser().getRole()
.add(this.management.getSecurity().getRole());
.add(this.management.getSecurity().getRole());
}
}
......@@ -124,7 +124,7 @@ public class ManagementSecurityAutoConfiguration {
// Get the ignored paths in early
@Order(SecurityProperties.IGNORED_ORDER + 1)
private static class IgnoredPathsWebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
WebSecurityConfigurer<WebSecurity> {
@Autowired(required = false)
private ErrorController errorController;
......@@ -209,7 +209,7 @@ public class ManagementSecurityAutoConfiguration {
@ConditionalOnProperty(prefix = "management.security", name = "enabled", matchIfMissing = true)
@Order(ManagementServerProperties.BASIC_AUTH_ORDER)
protected static class ManagementWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties security;
......@@ -314,17 +314,15 @@ public class ManagementSecurityAutoConfiguration {
}
if (this.delegate == null) {
List<RequestMatcher> pathMatchers = new ArrayList<RequestMatcher>();
String[] paths = !this.sensitive ? getEndpointPaths(
endpointMapping,
false)
: getEndpointPaths(endpointMapping);
for (String path : paths) {
pathMatchers.add(new AntPathRequestMatcher(
ManagementWebSecurityConfigurerAdapter.this.server
String[] paths = !this.sensitive ? getEndpointPaths(endpointMapping,
false) : getEndpointPaths(endpointMapping);
for (String path : paths) {
pathMatchers.add(new AntPathRequestMatcher(
ManagementWebSecurityConfigurerAdapter.this.server
.getPath(path)));
}
this.delegate = pathMatchers.isEmpty() ? AnyRequestMatcher.INSTANCE
: new OrRequestMatcher(pathMatchers);
}
this.delegate = pathMatchers.isEmpty() ? AnyRequestMatcher.INSTANCE
: new OrRequestMatcher(pathMatchers);
}
return this.delegate.matches(request);
}
......@@ -354,7 +352,8 @@ public class ManagementSecurityAutoConfiguration {
paths.add(path + "/**");
// Add Spring MVC-generated additional paths
paths.add(path + ".*");
} else {
}
else {
paths.add("/");
}
}
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -18,7 +18,6 @@ package org.springframework.boot.actuate.endpoint.mvc;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -26,45 +25,28 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author Dave Syer
* {@link MvcEndpoint} to expose actuator documentation.
*
* @author Dave Syer
* @since 1.3.0
*/
@ConfigurationProperties("endpoints.docs")
public class ActuatorDocsEndpoint extends WebMvcConfigurerAdapter implements MvcEndpoint {
private static final String DOCS_LOCATION = "classpath:/META-INF/resources/spring-boot-actuator/docs/";
private String path = "/docs";
private boolean sensitive;
private ManagementServerProperties management;
private Curies curies = new Curies();
public Curies getCuries() {
return this.curies;
}
/**
* Properties of the default CurieProvider (used for adding docs links). If enabled, all
* unqualified rels will pick up a prefix and a curie template pointing to the docs endpoint.
*
*/
public static class Curies {
/**
* Enable the curie generation (off by default).
*/
private boolean enabled = false;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
public ActuatorDocsEndpoint(ManagementServerProperties management) {
this.management = management;
}
......@@ -82,8 +64,7 @@ public class ActuatorDocsEndpoint extends WebMvcConfigurerAdapter implements Mvc
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(this.management.getContextPath() + this.path + "/**")
.addResourceLocations(
"classpath:/META-INF/resources/spring-boot-actuator/docs/");
.addResourceLocations(DOCS_LOCATION);
}
public void setPath(String path) {
......@@ -109,4 +90,26 @@ public class ActuatorDocsEndpoint extends WebMvcConfigurerAdapter implements Mvc
return null;
}
/**
* Properties of the default CurieProvider (used for adding docs links). If enabled,
* all unqualified rels will pick up a prefix and a curie template pointing to the
* docs endpoint.
*/
public static class Curies {
/**
* Enable the curie generation (off by default).
*/
private boolean enabled = false;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
......@@ -38,7 +38,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author Andy Wilkinson
*/
public class EnvironmentMvcEndpoint extends EndpointMvcAdapter implements
EnvironmentAware {
EnvironmentAware {
private Environment environment;
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -25,14 +25,19 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author Dave Syer
* {@link MvcEndpoint} to support the Spring Data HAL browser.
*
* @author Dave Syer
* @since 1.3.0
*/
@ConfigurationProperties("endpoints.hal")
public class HalBrowserEndpoint extends WebMvcConfigurerAdapter implements MvcEndpoint {
private static final String HAL_BROWSER_VERSION = "b7669f1-1";
private static final String HAL_BROWSER_LOCATION = "classpath:/META-INF/resources/webjars/hal-browser/"
+ HAL_BROWSER_VERSION + "/";
private String path = "";
private ManagementServerProperties management;
......@@ -58,16 +63,11 @@ public class HalBrowserEndpoint extends WebMvcConfigurerAdapter implements MvcEn
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Make sure the root path is not cached otherwise the browser won't come back for
// the JSON
// Make sure the root path is not cached so the browser comes back for the JSON
registry.addResourceHandler(this.management.getContextPath() + this.path + "/")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/hal-browser/"
+ HAL_BROWSER_VERSION + "/").setCachePeriod(0);
.addResourceLocations(HAL_BROWSER_LOCATION).setCachePeriod(0);
registry.addResourceHandler(this.management.getContextPath() + this.path + "/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/hal-browser/"
+ HAL_BROWSER_VERSION + "/");
.addResourceLocations(HAL_BROWSER_LOCATION);
}
public void setPath(String path) {
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -22,11 +22,16 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Dave Syer
* Annotation to that {@link MvcEndpoint} class or {@link RequestMapping} method should't
* generate a hypermedia response.
*
* @author Dave Syer
* @since 1.3.0
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HypermediaDisabled {
......
......@@ -47,7 +47,7 @@ import org.springframework.web.util.UrlPathHelper;
@ConfigurationProperties(prefix = "endpoints.jolokia", ignoreUnknownFields = false)
@HypermediaDisabled
public class JolokiaMvcEndpoint implements MvcEndpoint, InitializingBean,
ApplicationContextAware, ServletContextAware {
ApplicationContextAware, ServletContextAware {
/**
* Endpoint URL path.
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2013-2015 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.
......@@ -24,8 +24,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
* {@link MvcEndpoint} to add hypermedia links.
*
* @author Dave Syer
* @since 1.3.0
*/
@ConfigurationProperties("endpoints.links")
public class LinksMvcEndpoint implements MvcEndpoint {
......@@ -37,7 +39,7 @@ public class LinksMvcEndpoint implements MvcEndpoint {
this.path = defaultPath;
}
@RequestMapping(value = { "/", "" }, produces=MediaType.APPLICATION_JSON_VALUE)
@RequestMapping(value = { "/", "" }, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResourceSupport links() {
ResourceSupport resource = new ResourceSupport();
......
package org.springframework.boot.actuate.autoconfigure;
/*
* Copyright 2012-2015 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.
*/
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
package org.springframework.boot.actuate.autoconfigure;
import org.junit.Before;
import org.junit.Test;
......@@ -24,6 +35,11 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -55,8 +71,8 @@ public class BrowserPathHypermediaIntegrationTests {
@Test
public void redirect() throws Exception {
this.mockMvc.perform(get("/hal").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound())
.andExpect(header().string("location", "/hal/#"));
.andExpect(status().isFound())
.andExpect(header().string("location", "/hal/#"));
}
@MinimalActuatorHypermediaApplication
......@@ -66,6 +82,7 @@ public class BrowserPathHypermediaIntegrationTests {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
}
package org.springframework.boot.actuate.autoconfigure;
/*
* Copyright 2012-2015 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.
*/
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
package org.springframework.boot.actuate.autoconfigure;
import org.junit.Before;
import org.junit.Test;
......@@ -26,6 +37,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -49,24 +65,24 @@ public class ContextPathHypermediaIntegrationTests {
@Test
public void home() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void links() throws Exception {
this.mockMvc.perform(get("/admin").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void trace() throws Exception {
this.mockMvc
.perform(get("/admin/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(
jsonPath("$._links.self.href").value(
"http://localhost/admin/trace"))
.andExpect(jsonPath("$.content").isArray());
.perform(get("/admin/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(
jsonPath("$._links.self.href").value(
"http://localhost/admin/trace"))
.andExpect(jsonPath("$.content").isArray());
}
@Test
......@@ -76,11 +92,11 @@ public class ContextPathHypermediaIntegrationTests {
path = path.startsWith("/") ? path.substring(1) : path;
path = path.length() > 0 ? path : "self";
this.mockMvc
.perform(get("/admin").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(
jsonPath("$._links.%s.href", path).value(
"http://localhost/admin" + endpoint.getPath()));
.perform(get("/admin").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(
jsonPath("$._links.%s.href", path).value(
"http://localhost/admin" + endpoint.getPath()));
}
}
......@@ -98,8 +114,9 @@ public class ContextPathHypermediaIntegrationTests {
public static void main(String[] args) {
new SpringApplicationBuilder(SpringBootHypermediaApplication.class)
.properties("management.contextPath:/admin").run(args);
.properties("management.contextPath:/admin").run(args);
}
}
}
package org.springframework.boot.actuate.autoconfigure;
/*
* Copyright 2012-2015 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.
*/
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
package org.springframework.boot.actuate.autoconfigure;
import org.junit.Before;
import org.junit.Test;
......@@ -25,6 +36,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -47,7 +63,7 @@ public class CustomHomepageHypermediaIntegrationTests {
@Test
public void links() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
......@@ -56,8 +72,8 @@ public class CustomHomepageHypermediaIntegrationTests {
String path = endpoint.getPath();
path = path.startsWith("/") ? path.substring(1) : path;
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.%s.href", path).exists());
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.%s.href", path).exists());
}
}
......@@ -68,13 +84,15 @@ public class CustomHomepageHypermediaIntegrationTests {
@RequestMapping("")
public ResourceSupport home() {
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(SpringBootHypermediaApplication.class).slash("/").withSelfRel());
resource.add(linkTo(SpringBootHypermediaApplication.class).slash("/")
.withSelfRel());
return resource;
}
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
}
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 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.
......@@ -16,9 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
......@@ -63,6 +60,9 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Integration tests for MVC {@link Endpoint}s.
*
......@@ -94,19 +94,19 @@ public class EndpointMvcIntegrationTests {
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
protected static @interface MinimalWebConfiguration {
}
@Configuration
@MinimalWebConfiguration
@Import({ ManagementServerPropertiesAutoConfiguration.class, JacksonAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class })
@Import({ ManagementServerPropertiesAutoConfiguration.class,
JacksonAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class })
@RestController
protected static class Application {
......
......@@ -16,18 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import java.io.FileNotFoundException;
import java.net.SocketException;
import java.net.URI;
......@@ -84,6 +72,18 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link EndpointWebMvcAutoConfiguration}.
*
......@@ -228,8 +228,7 @@ public class EndpointWebMvcAutoConfigurationTests {
this.applicationContext.register(RootConfig.class, EndpointConfig.class,
ServerPortConfig.class, PropertyPlaceholderAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
JacksonAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, JacksonAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
......@@ -246,7 +245,7 @@ public class EndpointWebMvcAutoConfigurationTests {
this.applicationContext.register(RootConfig.class, BaseConfiguration.class,
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class);
new ServerPortInfoApplicationContextInitializer()
.initialize(this.applicationContext);
.initialize(this.applicationContext);
this.applicationContext.refresh();
Integer localServerPort = this.applicationContext.getEnvironment().getProperty(
"local.server.port", Integer.class);
......@@ -262,7 +261,7 @@ public class EndpointWebMvcAutoConfigurationTests {
@Test
public void portPropertiesOnDifferentPort() throws Exception {
new ServerPortInfoApplicationContextInitializer()
.initialize(this.applicationContext);
.initialize(this.applicationContext);
this.applicationContext.register(RootConfig.class, DifferentPortConfig.class,
BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class);
......@@ -423,13 +422,12 @@ public class EndpointWebMvcAutoConfigurationTests {
@Configuration
@Import({ PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
JacksonAutoConfiguration.class,
EndpointAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, WebMvcAutoConfiguration.class })
EmbeddedServletContainerAutoConfiguration.class,
JacksonAutoConfiguration.class, EndpointAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, WebMvcAutoConfiguration.class })
protected static class BaseConfiguration {
}
......@@ -587,7 +585,7 @@ public class EndpointWebMvcAutoConfigurationTests {
}
private static class GrabManagementPort implements
ApplicationListener<EmbeddedServletContainerInitializedEvent> {
ApplicationListener<EmbeddedServletContainerInitializedEvent> {
private ApplicationContext rootContext;
......
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 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.
......@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
......@@ -33,6 +31,8 @@ import org.springframework.mock.web.MockServletContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link EndpointWebMvcAutoConfiguration} of the {@link HealthMvcEndpoint}.
*
......
......@@ -16,13 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import javax.servlet.Filter;
import org.hamcrest.Matchers;
......@@ -60,6 +53,13 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link ManagementSecurityAutoConfiguration}.
*
......@@ -111,8 +111,7 @@ public class ManagementSecurityAutoConfigurationTests {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
SecurityAutoConfiguration.class,
......@@ -122,7 +121,7 @@ public class ManagementSecurityAutoConfigurationTests {
UserDetails user = getUser();
assertTrue(user.getAuthorities().containsAll(
AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN")));
.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN")));
}
private UserDetails getUser() {
......@@ -157,8 +156,8 @@ public class ManagementSecurityAutoConfigurationTests {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(HttpMessageConvertersAutoConfiguration.class,
JacksonAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class,
......@@ -205,15 +204,16 @@ public class ManagementSecurityAutoConfigurationTests {
public void realmSameForManagement() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(AuthenticationConfig.class,
SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class,
JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
WebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context
.register(AuthenticationConfig.class, SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class,
JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
WebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
Filter filter = this.context.getBean("springSecurityFilterChain", Filter.class);
......@@ -222,8 +222,8 @@ public class ManagementSecurityAutoConfigurationTests {
// no user (Main)
mockMvc.perform(MockMvcRequestBuilders.get("/home"))
.andExpect(MockMvcResultMatchers.status().isUnauthorized())
.andExpect(springAuthenticateRealmHeader());
.andExpect(MockMvcResultMatchers.status().isUnauthorized())
.andExpect(springAuthenticateRealmHeader());
// invalid user (Main)
mockMvc.perform(
......@@ -233,8 +233,8 @@ public class ManagementSecurityAutoConfigurationTests {
// no user (Management)
mockMvc.perform(MockMvcRequestBuilders.get("/beans"))
.andExpect(MockMvcResultMatchers.status().isUnauthorized())
.andExpect(springAuthenticateRealmHeader());
.andExpect(MockMvcResultMatchers.status().isUnauthorized())
.andExpect(springAuthenticateRealmHeader());
// invalid user (Management)
mockMvc.perform(
......@@ -254,7 +254,7 @@ public class ManagementSecurityAutoConfigurationTests {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password")
.roles("USER");
.roles("USER");
}
}
......
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -36,20 +36,19 @@ import org.springframework.context.annotation.Import;
/**
* @author Dave Syer
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Import({ ServerPropertiesAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class,
HypermediaAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
ManagementServerPropertiesAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class,
HypermediaAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
public @interface MinimalActuatorHypermediaApplication {
}
package org.springframework.boot.actuate.autoconfigure;
/*
* Copyright 2012-2015 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.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
package org.springframework.boot.actuate.autoconfigure;
import java.util.Arrays;
......@@ -27,6 +39,10 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -57,8 +73,7 @@ public class ServerContextPathHypermediaIntegrationTests {
"http://localhost:" + this.port + "/spring/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("<title"));
assertTrue("Wrong body: " + entity.getBody(), entity.getBody().contains("<title"));
}
@MinimalActuatorHypermediaApplication
......@@ -75,8 +90,9 @@ public class ServerContextPathHypermediaIntegrationTests {
public static void main(String[] args) {
new SpringApplicationBuilder(SpringBootHypermediaApplication.class)
.properties("server.contextPath=/spring").run(args);
.properties("server.contextPath=/spring").run(args);
}
}
}
package org.springframework.boot.actuate.autoconfigure;
/*
* Copyright 2012-2015 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.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
package org.springframework.boot.actuate.autoconfigure;
import java.util.Arrays;
......@@ -27,6 +39,10 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -74,8 +90,9 @@ public class ServerPortHypermediaIntegrationTests {
public static void main(String[] args) {
new SpringApplicationBuilder(SpringBootHypermediaApplication.class)
.properties("management.port:9000").run(args);
.properties("management.port:9000").run(args);
}
}
}
package org.springframework.boot.actuate.autoconfigure;
/*
* Copyright 2012-2015 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.
*/
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
package org.springframework.boot.actuate.autoconfigure;
import org.junit.Before;
import org.junit.Test;
......@@ -25,6 +35,12 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
......@@ -47,8 +63,8 @@ public class VanillaHypermediaIntegrationTests {
@Test
public void links() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists())
.andExpect(header().doesNotExist("cache-control"));
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists())
.andExpect(header().doesNotExist("cache-control"));
}
@Test
......@@ -61,17 +77,17 @@ public class VanillaHypermediaIntegrationTests {
@Test
public void trace() throws Exception {
this.mockMvc
.perform(get("/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.self.href").value("http://localhost/trace"))
.andExpect(jsonPath("$.content").isArray());
.perform(get("/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.self.href").value("http://localhost/trace"))
.andExpect(jsonPath("$.content").isArray());
}
@Test
public void envValue() throws Exception {
this.mockMvc.perform(get("/env/user.home").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
}
@Test
......@@ -80,8 +96,8 @@ public class VanillaHypermediaIntegrationTests {
String path = endpoint.getPath();
path = path.startsWith("/") ? path.substring(1) : path;
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.%s.href", path).exists());
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.%s.href", path).exists());
}
}
......@@ -95,11 +111,11 @@ public class VanillaHypermediaIntegrationTests {
}
path = path.length() > 0 ? path : "/";
this.mockMvc
.perform(get(path).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(
jsonPath("$._links.self.href").value(
"http://localhost" + endpoint.getPath()));
.perform(get(path).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(
jsonPath("$._links.self.href").value(
"http://localhost" + endpoint.getPath()));
}
}
......@@ -110,6 +126,7 @@ public class VanillaHypermediaIntegrationTests {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
}
......@@ -16,12 +16,6 @@
package org.springframework.boot.actuate.endpoint.mvc;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -45,6 +39,12 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link EnvironmentMvcEndpoint}
*
......@@ -72,13 +72,13 @@ public class EnvironmentMvcEndpointTests {
@Test
public void home() throws Exception {
this.mvc.perform(get("/env")).andExpect(status().isOk())
.andExpect(content().string(containsString("systemProperties")));
.andExpect(content().string(containsString("systemProperties")));
}
@Test
public void sub() throws Exception {
this.mvc.perform(get("/env/foo")).andExpect(status().isOk())
.andExpect(content().string(equalToIgnoringCase("bar")));
.andExpect(content().string(equalToIgnoringCase("bar")));
}
@Test
......@@ -90,15 +90,14 @@ public class EnvironmentMvcEndpointTests {
@Test
public void regex() throws Exception {
this.mvc.perform(get("/env/foo.*")).andExpect(status().isOk())
.andExpect(content().string(containsString("\"foo\":\"bar\"")))
.andExpect(content().string(containsString("\"fool\":\"baz\"")));
.andExpect(content().string(containsString("\"foo\":\"bar\"")))
.andExpect(content().string(containsString("\"fool\":\"baz\"")));
}
@Import({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
@Configuration
public static class TestConfiguration {
......
......@@ -16,11 +16,6 @@
package org.springframework.boot.actuate.endpoint.mvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -46,6 +41,11 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author Christian Dupuis
* @author Dave Syer
......@@ -73,22 +73,22 @@ public class JolokiaMvcEndpointContextPathTests {
@Test
public void read() throws Exception {
this.mvc.perform(get("/admin/jolokia/read/java.lang:type=Memory"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
}
@Configuration
@EnableConfigurationProperties
@EnableWebMvc
@Import({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
public static class Config {
}
public static class ContextPathListener implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
EnvironmentTestUtils.addEnvironment(context, "management.contextPath:/admin");
......
......@@ -16,14 +16,6 @@
package org.springframework.boot.actuate.endpoint.mvc;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Set;
import org.hamcrest.Matcher;
......@@ -50,6 +42,14 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link JolokiaMvcEndpoint}
*
......@@ -86,30 +86,30 @@ public class JolokiaMvcEndpointTests {
@Test
public void search() throws Exception {
this.mvc.perform(get("/jolokia/search/java.lang:*")).andExpect(status().isOk())
.andExpect(content().string(containsString("GarbageCollector")));
.andExpect(content().string(containsString("GarbageCollector")));
}
@Test
public void read() throws Exception {
this.mvc.perform(get("/jolokia/read/java.lang:type=Memory"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
}
@Test
public void list() throws Exception {
this.mvc.perform(get("/jolokia/list/java.lang/type=Memory/attr"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
}
@Configuration
@EnableConfigurationProperties
@EnableWebMvc
@Import({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
public static class Config {
}
......
......@@ -16,12 +16,6 @@
package org.springframework.boot.actuate.endpoint.mvc;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
......@@ -49,6 +43,12 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link MetricsMvcEndpoint}
*
......@@ -74,7 +74,7 @@ public class MetricsMvcEndpointTests {
@Test
public void home() throws Exception {
this.mvc.perform(get("/metrics")).andExpect(status().isOk())
.andExpect(content().string(containsString("\"foo\":1")));
.andExpect(content().string(containsString("\"foo\":1")));
}
@Test
......@@ -86,7 +86,7 @@ public class MetricsMvcEndpointTests {
@Test
public void specificMetric() throws Exception {
this.mvc.perform(get("/metrics/foo")).andExpect(status().isOk())
.andExpect(content().string(equalTo("1")));
.andExpect(content().string(equalTo("1")));
}
@Test
......@@ -104,35 +104,34 @@ public class MetricsMvcEndpointTests {
public void regexAll() throws Exception {
String expected = "\"foo\":1,\"group1.a\":1,\"group1.b\":1,\"group2.a\":1,\"group2_a\":1";
this.mvc.perform(get("/metrics/.*")).andExpect(status().isOk())
.andExpect(content().string(containsString(expected)));
.andExpect(content().string(containsString(expected)));
}
@Test
public void regexGroupDot() throws Exception {
String expected = "\"group1.a\":1,\"group1.b\":1,\"group2.a\":1";
this.mvc.perform(get("/metrics/group[0-9]+\\..*")).andExpect(status().isOk())
.andExpect(content().string(containsString(expected)));
.andExpect(content().string(containsString(expected)));
}
@Test
public void regexGroup1() throws Exception {
String expected = "\"group1.a\":1,\"group1.b\":1";
this.mvc.perform(get("/metrics/group1\\..*")).andExpect(status().isOk())
.andExpect(content().string(containsString(expected)));
.andExpect(content().string(containsString(expected)));
}
@Test
public void specificMetricWithDot() throws Exception {
this.mvc.perform(get("/metrics/group2.a")).andExpect(status().isOk())
.andExpect(content().string(containsString("1")));
.andExpect(content().string(containsString("1")));
}
@Import({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
@Configuration
public static class TestConfiguration {
......
......@@ -16,10 +16,6 @@
package org.springframework.boot.actuate.endpoint.mvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
......@@ -38,6 +34,10 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for the actuator endpoints' CORS support
*
......@@ -51,7 +51,8 @@ public class MvcEndpointCorsIntegrationTests {
public void createContext() {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
this.context.register(JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
......@@ -63,7 +64,7 @@ public class MvcEndpointCorsIntegrationTests {
createMockMvc().perform(
options("/beans").header("Origin", "foo.example.com").header(
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")).andExpect(
header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
}
@Test
......@@ -73,7 +74,7 @@ public class MvcEndpointCorsIntegrationTests {
createMockMvc().perform(
options("/beans").header("Origin", "bar.example.com").header(
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")).andExpect(
status().isForbidden());
status().isForbidden());
performAcceptedCorsRequest();
}
......@@ -100,8 +101,8 @@ public class MvcEndpointCorsIntegrationTests {
"endpoints.cors.allowed-origins:foo.example.com");
createMockMvc().perform(
options("/beans").header("Origin", "foo.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Alpha"))
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Alpha"))
.andExpect(status().isForbidden());
}
......@@ -111,15 +112,15 @@ public class MvcEndpointCorsIntegrationTests {
"endpoints.cors.allowed-origins:foo.example.com",
"endpoints.cors.allowed-headers:Alpha,Bravo");
createMockMvc()
.perform(
options("/beans")
.header("Origin", "foo.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS,
"Alpha"))
.andExpect(status().isOk())
.andExpect(
header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Alpha"));
.perform(
options("/beans")
.header("Origin", "foo.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS,
"Alpha"))
.andExpect(status().isOk())
.andExpect(
header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Alpha"));
}
@Test
......@@ -129,7 +130,7 @@ public class MvcEndpointCorsIntegrationTests {
createMockMvc().perform(
options("/health").header(HttpHeaders.ORIGIN, "foo.example.com").header(
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "HEAD")).andExpect(
status().isForbidden());
status().isForbidden());
}
@Test
......@@ -138,10 +139,10 @@ public class MvcEndpointCorsIntegrationTests {
"endpoints.cors.allowed-origins:foo.example.com",
"endpoints.cors.allowed-methods:GET,HEAD");
createMockMvc()
.perform(
options("/health")
.header(HttpHeaders.ORIGIN, "foo.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "HEAD"))
.perform(
options("/health")
.header(HttpHeaders.ORIGIN, "foo.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "HEAD"))
.andExpect(status().isOk())
.andExpect(
header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,
......@@ -173,7 +174,7 @@ public class MvcEndpointCorsIntegrationTests {
createMockMvc().perform(
options("/jolokia").header("Origin", "bar.example.com").header(
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")).andExpect(
status().isForbidden());
status().isForbidden());
performAcceptedCorsRequest("/jolokia");
}
......@@ -190,10 +191,10 @@ public class MvcEndpointCorsIntegrationTests {
return createMockMvc()
.perform(
options(url).header(HttpHeaders.ORIGIN, "foo.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"))
.andExpect(
header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,
"foo.example.com")).andExpect(status().isOk());
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"))
.andExpect(
header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,
"foo.example.com")).andExpect(status().isOk());
}
}
......@@ -150,30 +150,33 @@ For example, the following will disable _all_ endpoints except for `info`:
endpoints.info.enabled=true
----
[[production-ready-endpoint-hypermedia]]
=== Hypermedia for MVC Endpoints
If http://projects.spring.io/spring-hateoas[Spring HATEOAS] is on the classpath (e.g.
through the `spring-boot-starter-hateoas` or if you are using
through the `spring-boot-starter-hateoas` or if you are using
http://projects.spring.io/spring-data-rest[Spring Data REST]) then the HTTP endpoints
from the Actuator are enhanced with hypermedia links, and a "discovery page" is added
with links to all the endpoints. The "discovery page" is actually an endpoint itself,
so it can be disabled along with the rest of the hypermedia by setting
`endpoints.links.enabled=false`. If it is not explicitly disabled the links
so it can be disabled along with the rest of the hypermedia by setting
`endpoints.links.enabled=false`. If it is not explicitly disabled the links
endpoint renders a JSON object with a link for each other endpoint, and the default
path is the same as the `management.contentPath` (so "/" by default).
path is the same as the `management.contentPath` (so "`/`" by default).
NOTE: if there is a static home page ("index.html") in your application and the links
endpoint is registered with its default path ("/") then content negotiation will kick in
NOTE: if there is a static home page ("`index.html`") in your application and the links
endpoint is registered with its default path ("`/`") then content negotiation will kick in
to determine which content is shown to a client that requests the home page (the
links will show only if the client accepts `application/json`).
If the https://github.com/mikekelly/hal-browser[HAL Browser] is on the classpath
via its webjar (`org.webjars:hal-browser`), or via the `spring-data-hal-browser` then
the default home page for HTML clients will be the HAL Browser. This is also exposed via an
endpoint ("hal") so it can be disabled and have its path explicitly configured like
the default home page for HTML clients will be the HAL Browser. This is also exposed via
an endpoint ("`hal`") so it can be disabled and have its path explicitly configured like
the other endpoints.
[[production-ready-customizing-endpoints-programmatically]]
=== Adding Custom Endpoints
If you add a `@Bean` of type `Endpoint` then it will automatically be exposed over JMX and
......
......@@ -2,24 +2,19 @@
<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>
<artifactId>spring-boot-sample-hypermedia-gson</artifactId>
<packaging>jar</packaging>
<name>spring-boot-sample-hypermedia-gson</name>
<description>Demo project for Spring Boot</description>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.3.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia-gson</artifactId>
<name>Spring Boot Hypermedia GSON Sample</name>
<description>Spring Boot Hypermedia GSON Sample</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.basedir>${basedir}/../..</main.basedir>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -37,11 +32,6 @@
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......@@ -53,7 +43,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
......@@ -62,5 +51,4 @@
</plugin>
</plugins>
</build>
</project>
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.hypermedia.gson;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaGsonApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaGsonApplication.class, args);
}
}
package demo;
/*
* Copyright 2012-2015 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.
*/
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
package sample.hypermedia.gson;
import org.junit.Before;
import org.junit.Test;
......@@ -17,11 +29,16 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import sample.hypermedia.gson.SampleHypermediaGsonApplication;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@SpringApplicationConfiguration(classes = SampleHypermediaGsonApplication.class)
@WebAppConfiguration
@TestPropertySource(properties="endpoints.health.sensitive: false")
public class SpringBootHypermediaApplicationTests {
@TestPropertySource(properties = "endpoints.health.sensitive: false")
public class SampleHypermediaGsonApplicationTests {
@Autowired
private WebApplicationContext context;
......@@ -35,27 +52,25 @@ public class SpringBootHypermediaApplicationTests {
@Test
public void health() throws Exception {
this.mockMvc
.perform(get("/health").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.links[0].href").value("http://localhost/health"))
.andExpect(jsonPath("$.content.status").exists());
this.mockMvc.perform(get("/health").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.links[0].href").value("http://localhost/health"))
.andExpect(jsonPath("$.content.status").exists());
}
@Test
public void trace() throws Exception {
this.mockMvc
.perform(get("/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.links[0].href").value("http://localhost/trace"))
.andExpect(jsonPath("$.content").isArray());
this.mockMvc.perform(get("/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.links[0].href").value("http://localhost/trace"))
.andExpect(jsonPath("$.content").isArray());
}
@Test
public void envValue() throws Exception {
this.mockMvc.perform(get("/env/user.home").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
}
}
......@@ -2,24 +2,19 @@
<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>
<artifactId>spring-boot-sample-hypermedia-jpa</artifactId>
<packaging>jar</packaging>
<name>spring-boot-sample-hypermedia-jpa</name>
<description>Demo project for Spring Boot</description>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.3.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia-jpa</artifactId>
<name>Spring Boot Hypermedia JPA Sample</name>
<description>Spring Boot Hypermedia JPA Sample</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.basedir>${basedir}/../..</main.basedir>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -53,11 +48,6 @@
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......@@ -69,7 +59,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
......@@ -78,5 +67,4 @@
</plugin>
</plugins>
</build>
</project>
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JpaHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(JpaHypermediaApplication.class, args);
}
}
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package demo;
package sample.hypermedia.jpa;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
......@@ -22,14 +22,19 @@ import javax.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String title;
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
}
\ No newline at end of file
}
/*
* Copyright 2015 the original author or authors.
* Copyright 2012-2015 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.
......@@ -14,9 +14,10 @@
* limitations under the License.
*/
package demo;
package sample.hypermedia.jpa;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}
\ No newline at end of file
}
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.hypermedia.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaJpaApplication.class, args);
}
}
package demo;
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.hypermedia.jpa;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import sample.hypermedia.jpa.SampleHypermediaJpaApplication;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = JpaHypermediaApplication.class)
@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class)
@WebAppConfiguration
public class JpaHypermediaApplicationTests {
public class SampleHypermediaJpaApplicationTests {
@Test
public void contextLoads() {
......
package demo;
/*
* Copyright 2012-2015 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.
*/
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
package sample.hypermedia.jpa;
import org.junit.Before;
import org.junit.Test;
......@@ -20,11 +31,17 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import sample.hypermedia.jpa.SampleHypermediaJpaApplication;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = JpaHypermediaApplication.class)
@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class)
@WebAppConfiguration
@DirtiesContext
public class VanillaHypermediaIntegrationTests {
public class SampleHypermediaJpaApplicationVanillaIntegrationTests {
@Autowired
private WebApplicationContext context;
......@@ -59,7 +76,8 @@ public class VanillaHypermediaIntegrationTests {
@Test
public void docs() throws Exception {
MvcResult response = this.mockMvc.perform(get("/admin/docs/").accept(MediaType.TEXT_HTML))
MvcResult response = this.mockMvc
.perform(get("/admin/docs/").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn();
System.err.println(response.getResponse().getContentAsString());
}
......@@ -68,7 +86,8 @@ public class VanillaHypermediaIntegrationTests {
public void browser() throws Exception {
MvcResult response = this.mockMvc.perform(get("/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound()).andReturn();
assertEquals("/browser/index.html#", response.getResponse().getHeaders("location").get(0));
assertEquals("/browser/index.html#", response.getResponse()
.getHeaders("location").get(0));
}
}
......@@ -2,24 +2,19 @@
<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>
<artifactId>spring-boot-sample-hypermedia-ui</artifactId>
<packaging>jar</packaging>
<name>spring-boot-sample-hypermedia-ui</name>
<description>Demo project for Spring Boot</description>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.3.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia-ui</artifactId>
<name>Spring Boot Hypermedia UI Sample</name>
<description>Spring Boot Hypermedia UI Sample</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.basedir>${basedir}/../..</main.basedir>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -37,18 +32,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
......@@ -57,5 +46,4 @@
</plugin>
</plugins>
</build>
</project>
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.hypermedia.ui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaUiApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaUiApplication.class, args);
}
}
package demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
public class SpringBootHypermediaApplicationTests {
@Test
public void contextLoads() {
}
}
package demo;
/*
* Copyright 2012-2015 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.
*/
import static org.junit.Assert.assertTrue;
package sample.hypermedia.ui;
import java.net.URI;
import java.util.Arrays;
......@@ -19,26 +33,29 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import sample.hypermedia.ui.SampleHypermediaUiApplication;
import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@SpringApplicationConfiguration(classes = SampleHypermediaUiApplication.class)
@WebAppConfiguration
@IntegrationTest({ "management.contextPath=", "server.port=0" })
public class HomePageHypermediaApplicationTests {
public class SampleHypermediaUiApplicationTests {
@Value("${local.server.port}")
private int port;
@Test
public void home() {
String response = new TestRestTemplate().getForObject("http://localhost:" + port,
String.class);
String response = new TestRestTemplate().getForObject("http://localhost:"
+ this.port, String.class);
assertTrue("Wrong body: " + response, response.contains("Hello World"));
}
@Test
public void links() {
String response = new TestRestTemplate().getForObject("http://localhost:" + port + "/links",
String.class);
String response = new TestRestTemplate().getForObject("http://localhost:"
+ this.port + "/links", String.class);
assertTrue("Wrong body: " + response, response.contains("\"_links\":"));
}
......@@ -47,8 +64,8 @@ public class HomePageHypermediaApplicationTests {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> response = new TestRestTemplate().exchange(
new RequestEntity<Void>(headers , HttpMethod.GET, new URI("http://localhost:"
+ port + "/links")), String.class);
new RequestEntity<Void>(headers, HttpMethod.GET, new URI(
"http://localhost:" + this.port + "/links")), String.class);
assertTrue("Wrong body: " + response, response.getBody().contains("\"_links\":"));
}
......@@ -57,8 +74,8 @@ public class HomePageHypermediaApplicationTests {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> response = new TestRestTemplate().exchange(
new RequestEntity<Void>(headers , HttpMethod.GET, new URI("http://localhost:"
+ port)), String.class);
new RequestEntity<Void>(headers, HttpMethod.GET, new URI(
"http://localhost:" + this.port)), String.class);
assertTrue("Wrong body: " + response, response.getBody().contains("Hello World"));
}
......
......@@ -2,24 +2,20 @@
<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>
<artifactId>spring-boot-sample-hypermedia</artifactId>
<packaging>jar</packaging>
<name>spring-boot-sample-hypermedia</name>
<description>Demo project for Spring Boot</description>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.3.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia</artifactId>
<name>Spring Boot Hypermedia Sample</name>
<description>Spring Boot Hypermedia Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -41,18 +37,12 @@
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
......@@ -61,5 +51,4 @@
</plugin>
</plugins>
</build>
</project>
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
}
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.hypermedia;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaApplication.class, args);
}
}
package demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@WebAppConfiguration
public class SpringBootHypermediaApplicationTests {
@Test
public void contextLoads() {
}
}
package demo;
/*
* Copyright 2012-2015 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.
*/
import static org.junit.Assert.assertTrue;
package sample.hypermedia;
import java.net.URI;
import java.util.Arrays;
......@@ -19,19 +33,21 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootHypermediaApplication.class)
@SpringApplicationConfiguration(classes = SampleHypermediaApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port=0")
public class HomePageHypermediaApplicationTests {
public class SampleHypermediaApplicationHomePageTests {
@Value("${local.server.port}")
private int port;
@Test
public void home() {
String response = new TestRestTemplate().getForObject("http://localhost:" + port,
String.class);
String response = new TestRestTemplate().getForObject("http://localhost:"
+ this.port, String.class);
assertTrue("Wrong body: " + response, response.contains("\"_links\":"));
assertTrue("Wrong body: " + response, response.contains("\"curies\":"));
}
......@@ -41,8 +57,8 @@ public class HomePageHypermediaApplicationTests {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> response = new TestRestTemplate().exchange(
new RequestEntity<Void>(headers , HttpMethod.GET, new URI("http://localhost:"
+ port + "/")), String.class);
new RequestEntity<Void>(headers, HttpMethod.GET, new URI(
"http://localhost:" + this.port + "/")), String.class);
assertTrue("Wrong body: " + response, response.getBody().contains("\"_links\":"));
}
......@@ -51,8 +67,8 @@ public class HomePageHypermediaApplicationTests {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> response = new TestRestTemplate().exchange(
new RequestEntity<Void>(headers , HttpMethod.GET, new URI("http://localhost:"
+ port)), String.class);
new RequestEntity<Void>(headers, HttpMethod.GET, new URI(
"http://localhost:" + this.port)), String.class);
assertTrue("Wrong body: " + response, response.getBody().contains("HAL Browser"));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment