Drop support for self-hosted Actuator docs

Closes gh-9899
This commit is contained in:
Andy Wilkinson 2017-07-28 19:50:23 +01:00
parent e64090838c
commit fb3d79c750
41 changed files with 1 additions and 1697 deletions

View File

@ -86,7 +86,6 @@
<module>spring-boot-devtools</module>
<module>spring-boot-docs</module>
<module>spring-boot-starters</module>
<module>spring-boot-actuator-docs</module>
<module>spring-boot-cli</module>
</modules>
</profile>

View File

@ -1,252 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.0.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>
<main.basedir>${basedir}/..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<!-- Provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-templates</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-restdoc-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/restdoc/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-restdoc-resource</id>
<phase>generate-sources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/restdoc/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testClassesDirectory>${project.build.outputDirectory}</testClassesDirectory>
<includes>
<include>**/*Documentation.java</include>
</includes>
<skipTests>false</skipTests>
</configuration>
<executions>
<execution>
<id>generate-rest-documentation</id>
<phase>process-classes</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.2</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<sourceDocumentName>index.adoc</sourceDocumentName>
<attributes>
<generated>${project.build.directory}/generated-snippets</generated>
<docs>${project.build.directory}/../src/main/asciidoc</docs>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/META-INF/resources/spring-boot-actuator/docs</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}/generated-docs</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<includes>
<include>**/*.html</include>
<include>**/*.png</include>
</includes>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-surefire-plugin
</artifactId>
<versionRange>
[2.18.1,)
</versionRange>
<goals>
<goal>test</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -1,23 +0,0 @@
=== /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 bean'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).
NOTE: The report is actually about `@Conditional` evaluation not auto-configuration
per se, but most auto-configuration features use `@Conditional` heavily, so there is a lot
of overlap.
Example curl request:
include::{generated}/autoconfig/curl-request.adoc[]
Example HTTP request: [small]##link:../autoconfig[icon:external-link[role="silver"]]##
include::{generated}/autoconfig/http-request.adoc[]
Example HTTP response:
include::{generated}/autoconfig/http-response.adoc[]

View File

@ -1,14 +0,0 @@
=== /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.
NOTE: Some beans are pure configuration (any class that is annotated `@Configuration`).
Example curl request:
include::{generated}/beans/curl-request.adoc[]
Example HTTP request: [small]##link:../beans[icon:external-link[role="silver"]]##
include::{generated}/beans/http-request.adoc[]
Example HTTP response:
include::{generated}/beans/http-response.adoc[]

View File

@ -1,16 +0,0 @@
=== /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>-<fqn>`, where `<prefix>` is the environment key prefix specified in the
`@ConfigurationProperties` annotation and <fqn> the fully qualified name of the bean.
Example curl request:
include::{generated}/configprops/curl-request.adoc[]
Example HTTP request: [small]##link:../configprops[icon:external-link[role="silver"]]##
include::{generated}/configprops/http-request.adoc[]
Example HTTP response:
include::{generated}/configprops/http-response.adoc[]

View File

@ -1,17 +0,0 @@
=== /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. It 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.
Example curl request:
include::{generated}/dump/curl-request.adoc[]
Example HTTP request: [small]##link:../dump[icon:external-link[role="silver"]]##
include::{generated}/dump/http-request.adoc[]
Example HTTP response:
include::{generated}/dump/http-response.adoc[]

View File

@ -1,15 +0,0 @@
=== /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.
Example curl request:
include::{generated}/env/curl-request.adoc[]
Example HTTP request: [small]##link:../env[icon:external-link[role="silver"]]##
include::{generated}/env/http-request.adoc[]
Example HTTP response:
include::{generated}/env/http-response.adoc[]

View File

@ -1,12 +0,0 @@
=== /flyway
This endpoint provides information about database migrations that have been performed
by Flyway.
Example curl request:
include::{generated}/flyway/curl-request.adoc[]
Example HTTP request:
include::{generated}/flyway/http-request.adoc[]
Example HTTP response:
include::{generated}/flyway/http-response.adoc[]

View File

@ -1,20 +0,0 @@
=== /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`).
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[]
Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]##
include::{generated}/health/http-request.adoc[]
Example HTTP response:
include::{generated}/health/http-response.adoc[]
Example HTTP response with `endpoints.health.sensitive=false`:
include::{generated}/health/insensitive/http-response.adoc[]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 KiB

View File

@ -1,203 +0,0 @@
= Spring Boot Actuator Endpoints
:toc: left
:idprefix: spring_boot_actuator_
:sectanchors:
:icons: font
:last-update-label!:
Actuator endpoints allow you to monitor and interact with your application. Spring Boot
includes a number of built-in endpoints and you can also add your own. For example the
`health` endpoint provides basic application health information.
The way that endpoints are exposed will depend on the type of technology that you choose.
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
include::{generated}/endpoints.adoc[]
=== /auditevents
This endpoint provides information about audit events registered by the application.
Audit events can be filtered using the `after`, `principal` and `type` parameters as
defined by `AuditEventRepository`.
Example cURL request with `after` parameter:
include::{generated}/auditevents/curl-request.adoc[]
Example HTTP request with `after` parameter:
include::{generated}/auditevents/http-request.adoc[]
Example HTTP response:
include::{generated}/auditevents/http-response.adoc[]
Example cURL request with `principal` and `after` parameters:
include::{generated}/auditevents/filter-by-principal/curl-request.adoc[]
Example HTTP request with `principal` and `after` parameters:
include::{generated}/auditevents/filter-by-principal/http-request.adoc[]
Example cURL request with `principal`, `after` and `type` parameters:
include::{generated}/auditevents/filter-by-principal-and-type/curl-request.adoc[]
Example HTTP request with `principal`, `after` and `type` parameters:
include::{generated}/auditevents/filter-by-principal-and-type/http-request.adoc[]
=== /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).
Example curl request:
include::{generated}/logfile/curl-request.adoc[]
Example HTTP request: [small]##link:../logfile[icon:external-link[role="silver"]]##
include::{generated}/logfile/http-request.adoc[]
Example HTTP response:
include::{generated}/logfile/http-response.adoc[]
==== Partial content
You can use the `Range` header to retrieve part of the log file's content.
Example curl request:
include::{generated}/partial-logfile/curl-request.adoc[]
Example HTTP request:
include::{generated}/partial-logfile/http-request.adoc[]
Example HTTP response:
include::{generated}/partial-logfile/http-response.adoc[]
=== /docs
This endpoint (if available) contains HTML documentation for the other endpoints. Its path
can be "/docs" (if there is an existing home page) or "/" (otherwise, including if the
HAL browser is not active).
== Hypermedia Support
If `endpoints.hypermedia.enabled` is set to `true` and
https://projects.spring.io/spring-hateoas[Spring HATEOAS] is on the classpath (e.g.
through the `spring-boot-starter-hateoas` or if you are using
http://projects.spring.io/spring-data-rest[Spring Data REST]) 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":
include::{generated}/metrics/hypermedia/http-response.adoc[]
WARNING: Beware of Actuator endpoint paths clashing with application endpoints.
The easiest way to avoid that is to use a `management.context-path`, e.g. "/admin".
TIP: You can disable the hypermedia support in Actuator endpoints by setting
`endpoints.actuator.enabled=false`.
=== Main entry point
When the hypermedia support is enabled, the Actuator provides a main entry point that
provides links to all of its endpoints. If `management.context-path` is empty, this entry
point is available at `/actuator`. If a management context path has been configured then
the entry point is available at the root of that context. For example, if
`management.context-path` has been set to `/admin` then the main entry point will be
available at `/admin`.
TIP: The endpoint path can always, as with all MVC endpoints, be overridden using
`endpoints.actuator.path=/yourpath` (note the leading slash).
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`:
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.
For example in Maven:
[source,xml,indent=0]
----
<dependency>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
</dependency>
----
or in Gradle:
[source,groovy,indent=0]
----
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.
If you do that then the main entry point will server a static HTML page to browser clients
with some JavaScript that lets you browse the available resources.
Example:
image::hal-browser.png[HAL Browser]
== 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.
For example in Maven:
[source,xml,indent=0]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
----
or in Gradle:
[source,groovy,indent=0]
----
dependencies {
...
compile('org.springframework.boot:spring-boot-actuator-docs')
...
}
----
If you do that then a new endpoint at `/` or `/docs` (relative to the
`management.context-path`) 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.context-path` is
empty, then the docs browser shows up on the home page.

View File

@ -1,14 +0,0 @@
=== /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`
if such a file exists in the root of the classpath.
Example curl request:
include::{generated}/info/curl-request.adoc[]
Example HTTP request: [small]##link:../info[icon:external-link[role="silver"]]##
include::{generated}/info/http-request.adoc[]
Example HTTP response:
include::{generated}/info/http-response.adoc[]

View File

@ -1,12 +0,0 @@
=== /liquibase
This endpoint provides information about database migrations that have been performed
by Liquibase.
Example curl request:
include::{generated}/liquibase/curl-request.adoc[]
Example HTTP request:
include::{generated}/liquibase/http-request.adoc[]
Example HTTP response:
include::{generated}/liquibase/http-response.adoc[]

View File

@ -1,61 +0,0 @@
=== /loggers
This endpoint allows you to view and modify the log levels for the loggers in your
application. It builds on top of the `LoggingSystem` abstraction and supports the same
logging frameworks. The logging levels are defined by the `LogLevel` enumeration and
consist of the following values (although not all logging systems support the full set):
* `TRACE`
* `DEBUG`
* `INFO`
* `WARN`
* `ERROR`
* `FATAL`
* `OFF`
* `null`
The `configuredLevel` property reflects an explicitly configured logger level, while the
`effectiveLevel` property reflects the logger level inherited from parent loggers. The
`effectiveLevel` is managed by each logging framework and reflects the propagation rules
inherent to and configured in that framework. `null` indicates that there is no explicit
configuration defined.
==== Listing All Loggers
Example curl request:
include::{generated}/loggers/curl-request.adoc[]
Example HTTP request: [small]##link:../loggers[icon:external-link[role="silver"]]##
include::{generated}/loggers/http-request.adoc[]
Example HTTP response:
include::{generated}/loggers/http-response.adoc[]
==== Getting a Single Logger
Example curl request:
include::{generated}/single-logger/curl-request.adoc[]
Example HTTP request: [small]##link:../loggers[icon:external-link[role="silver"]]##
include::{generated}/single-logger/http-request.adoc[]
Example HTTP response:
include::{generated}/single-logger/http-response.adoc[]
==== Configuring a Logger
Setting the `configuredLevel` of a logger requires `POSTing` a partial payload to the
resource. The `configuredLevel` property must contain a string representation of the
enumeration described above. `null` indicates that the log level should be unset,
allowing it to inherit configuration from its parent.
Example curl request:
include::{generated}/set-logger/curl-request.adoc[]
Example HTTP request: [small]##link:../loggers[icon:external-link[role="silver"]]##
include::{generated}/set-logger/http-request.adoc[]
Example HTTP response:
include::{generated}/set-logger/http-response.adoc[]

View File

@ -1,12 +0,0 @@
=== /mappings
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[]
Example HTTP request: [small]##link:../mappings[icon:external-link[role="silver"]]##
include::{generated}/mappings/http-request.adoc[]
Example HTTP response:
include::{generated}/mappings/http-response.adoc[]

View File

@ -1,15 +0,0 @@
=== /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.
Example curl request:
include::{generated}/metrics/curl-request.adoc[]
Example HTTP request: [small]##link:../metrics[icon:external-link[role="silver"]]##
include::{generated}/metrics/http-request.adoc[]
Example HTTP response:
include::{generated}/metrics/http-response.adoc[]

View File

@ -1,14 +0,0 @@
=== /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 stores 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[]
Example HTTP request: [small]##link:../trace[icon:external-link[role="silver"]]##
include::{generated}/trace/http-request.adoc[]
Example HTTP response:
include::{generated}/trace/http-response.adoc[]

View File

@ -1,235 +0,0 @@
/*
* Copyright 2012-2017 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;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import groovy.text.Template;
import groovy.text.TemplateEngine;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorMediaTypes;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrint;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class)
@WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"endpoints.health.sensitive=true", "endpoints.actuator.enabled=false",
"management.security.enabled=false" })
@DirtiesContext
@AutoConfigureRestDocs(EndpointDocumentation.RESTDOCS_OUTPUT_DIR)
@AutoConfigureMockMvc(print = MockMvcPrint.NONE)
public class EndpointDocumentation {
static final String RESTDOCS_OUTPUT_DIR = "target/generated-snippets";
static final File LOG_FILE = new File("target/logs/spring.log");
private static final Set<String> SKIPPED = Collections
.<String>unmodifiableSet(new HashSet<>(
Arrays.asList("/docs", "/logfile", "/heapdump", "/auditevents")));
@Autowired
private MvcEndpoints mvcEndpoints;
@Autowired
private TemplateEngine templates;
@Autowired
private MockMvc mockMvc;
@BeforeClass
public static void clearLog() {
LOG_FILE.delete();
}
@Test
public void logfile() throws Exception {
this.mockMvc.perform(get("/application/logfile").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andDo(document("logfile"));
}
@Test
public void partialLogfile() throws Exception {
FileCopyUtils.copy(getClass().getResourceAsStream("log.txt"),
new FileOutputStream(LOG_FILE));
this.mockMvc
.perform(get("/application/logfile").accept(MediaType.TEXT_PLAIN)
.header(HttpHeaders.RANGE, "bytes=0-1024"))
.andExpect(status().isPartialContent())
.andDo(document("partial-logfile"));
}
@Test
public void singleLogger() throws Exception {
this.mockMvc
.perform(get("/application/loggers/org.springframework.boot")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("single-logger"));
}
@Test
public void setLogger() throws Exception {
this.mockMvc
.perform(post("/application/loggers/org.springframework.boot")
.contentType(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON)
.content("{\"configuredLevel\": \"DEBUG\"}"))
.andExpect(status().isNoContent()).andDo(document("set-logger"));
}
@Test
public void auditEvents() throws Exception {
this.mockMvc
.perform(get("/application/auditevents")
.param("after", "2016-11-01T10:00:00+0000")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk()).andDo(document("auditevents"));
}
@Test
public void auditEventsByPrincipal() throws Exception {
this.mockMvc
.perform(get("/application/auditevents").param("principal", "admin")
.param("after", "2016-11-01T10:00:00+0000")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk())
.andDo(document("auditevents/filter-by-principal"));
}
@Test
public void auditEventsByPrincipalAndType() throws Exception {
this.mockMvc
.perform(get("/application/auditevents").param("principal", "admin")
.param("after", "2016-11-01T10:00:00+0000")
.param("type", "AUTHENTICATION_SUCCESS")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk())
.andDo(document("auditevents/filter-by-principal-and-type"));
}
@Test
public void endpoints() throws Exception {
File docs = new File("src/main/asciidoc");
Map<String, Object> model = new LinkedHashMap<>();
model.put("endpoints", getEndpointDocs(docs));
File file = new File(RESTDOCS_OUTPUT_DIR + "/endpoints.adoc");
file.getParentFile().mkdirs();
try (PrintWriter writer = new PrintWriter(file, "UTF-8")) {
Template template = this.templates.createTemplate(
new File("src/restdoc/resources/templates/endpoints.adoc.tpl"));
template.make(model).writeTo(writer);
}
}
private List<EndpointDoc> getEndpointDocs(File docs) throws Exception {
final List<EndpointDoc> endpoints = new ArrayList<>();
for (MvcEndpoint endpoint : getEndpoints()) {
String path = endpoint.getPath();
path = (StringUtils.hasText(path) ? path : "/");
if (!SKIPPED.contains(path)) {
documentEndpoint(path);
endpoints.add(new EndpointDoc(docs, path));
}
}
return endpoints;
}
private Collection<? extends MvcEndpoint> getEndpoints() {
List<? extends MvcEndpoint> endpoints = new ArrayList<>(
this.mvcEndpoints.getEndpoints());
endpoints.sort(Comparator.comparing(MvcEndpoint::getPath));
return endpoints;
}
private String documentEndpoint(final String endpointPath) throws Exception {
String output = endpointPath.substring(1);
output = (output.length() > 0 ? output : "./");
this.mockMvc
.perform(get("/application" + endpointPath)
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk()).andDo(document(output));
return output;
}
public static class EndpointDoc {
private String path;
private String custom;
private String title;
public EndpointDoc(File rootDir, String path) {
this.title = path;
this.path = path.equals("/") ? "" : path;
String custom = path.substring(1) + ".adoc";
if (new File(rootDir, custom).exists()) {
this.custom = custom;
}
}
public String getTitle() {
return this.title;
}
public String getPath() {
return this.path;
}
public String getCustom() {
return this.custom;
}
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2012-2017 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;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorMediaTypes;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class)
@WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"endpoints.health.sensitive=false", "management.security.enabled=false" })
@DirtiesContext
@AutoConfigureMockMvc
@AutoConfigureRestDocs("target/generated-snippets")
public class HealthEndpointDocumentation {
@Autowired
private MockMvc mockMvc;
@Test
public void health() throws Exception {
this.mockMvc
.perform(get("/application/health")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk()).andDo(document("health/insensitive"));
}
}

View File

@ -1,79 +0,0 @@
/*
* Copyright 2012-2017 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;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorMediaTypes;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
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(SpringRunner.class)
@ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class)
@WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"endpoints.hypermedia.enabled=true", "management.security.enabled=false" })
@DirtiesContext
@AutoConfigureMockMvc
@AutoConfigureRestDocs("target/generated-snippets")
public class HypermediaEndpointDocumentation {
@Autowired
private MockMvc mockMvc;
@Test
public void beans() throws Exception {
this.mockMvc
.perform(get("/application/beans")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk()).andDo(document("beans/hypermedia"));
}
@Test
public void metrics() throws Exception {
this.mockMvc
.perform(get("/application/metrics")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.self.href")
.value("http://localhost:8080/application/metrics"))
.andDo(document("metrics/hypermedia"));
}
@Test
public void home() throws Exception {
this.mockMvc
.perform(get("/application")
.accept(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON))
.andExpect(status().isOk()).andDo(document("admin"));
}
}

View File

@ -1,98 +0,0 @@
/*
* Copyright 2012-2017 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* {@link EnvironmentEndpoint} with reduced output to look better in the documentation.
*
* @author Phillip Webb
*/
public class LimitedEnvironmentEndpoint extends EnvironmentEndpoint {
private static final MultiValueMap<String, String> INCLUDES;
static {
Map<String, List<String>> includes = new LinkedHashMap<>();
List<String> systemProperties = new ArrayList<>();
systemProperties.add("java.runtime.name");
systemProperties.add("sun.boot.library.path");
systemProperties.add("java.vendor.url");
systemProperties.add("path.separator");
systemProperties.add("sun.java.launcher");
systemProperties.add("java.runtime.version");
systemProperties.add("os.arch");
systemProperties.add("line.separator");
systemProperties.add("os.name");
systemProperties.add("user.timezone");
systemProperties.add("file.encoding");
systemProperties.add("java.vm.specification.version");
systemProperties.add("sun.java.command");
systemProperties.add("sun.arch.data.model");
systemProperties.add("user.language");
systemProperties.add("awt.toolkit");
systemProperties.add("java.awt.headless");
systemProperties.add("java.vendor");
systemProperties.add("file.separator");
includes.put("systemProperties", systemProperties);
List<String> systemEnvironment = new ArrayList<>();
systemEnvironment.add("SHELL");
systemEnvironment.add("TMPDIR");
systemEnvironment.add("DISPLAY");
systemEnvironment.add("LOGNAME");
includes.put("systemEnvironment", systemEnvironment);
INCLUDES = new LinkedMultiValueMap<>(Collections.unmodifiableMap(includes));
}
@Override
public Object sanitize(String name, Object object) {
if (name.equals("gopherProxySet")) {
return object;
}
return null;
}
@Override
protected Map<String, Object> postProcessSourceProperties(String sourceName,
Map<String, Object> properties) {
List<String> sourceIncludes = INCLUDES.get(sourceName);
if (sourceIncludes != null) {
Set<Entry<String, Object>> entries = properties.entrySet();
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
while (iterator.hasNext()) {
if (!sourceIncludes.contains(iterator.next().getKey())) {
iterator.remove();
}
}
}
return properties;
}
}

View File

@ -1,73 +0,0 @@
/*
* Copyright 2012-2017 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;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import groovy.text.GStringTemplateEngine;
import groovy.text.TemplateEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
// Flyway must go first
@SpringBootApplication
@Import({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
public class SpringBootHypermediaApplication implements CommandLineRunner {
@Autowired
private AuditEventRepository auditEventRepository;
@Bean
public TemplateEngine groovyTemplateEngine() {
return new GStringTemplateEngine();
}
@Bean
public EnvironmentEndpoint environmentEndpoint() {
return new LimitedEnvironmentEndpoint();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
this.auditEventRepository.add(
createEvent("2016-11-01T11:00:00Z", "user", "AUTHENTICATION_FAILURE"));
this.auditEventRepository.add(
createEvent("2016-11-01T12:00:00Z", "admin", "AUTHENTICATION_SUCCESS"));
}
private AuditEvent createEvent(String instant, String principal, String type) {
return new AuditEvent(Date.from(Instant.parse(instant)), principal, type,
Collections.<String, Object>emptyMap());
}
}

View File

@ -1,2 +0,0 @@
# management.context-path=/admin
logging.path: target/logs

View File

@ -1,5 +0,0 @@
databaseChangeLog:
- changeSet:
id: 1
author: awilkinson
changes:

View File

@ -1,24 +0,0 @@
2016-05-10 10:55:45.405 INFO 40532 --- [ main] sanity.SanityCheckApplication : Starting SanityCheckApplication on pwmbp with PID 40532 (/Users/pwebb/projects/spring-boot/samples/spring-boot-sanity-check/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/samples/spring-boot-sanity-check)
2016-05-10 10:55:45.408 INFO 40532 --- [ main] sanity.SanityCheckApplication : No active profile set, falling back to default profiles: default
2016-05-10 10:55:45.473 INFO 40532 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@59e5ddf: startup date [Tue May 10 10:55:45 PDT 2016]; root of context hierarchy
2016-05-10 10:55:47.076 INFO 40532 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2016-05-10 10:55:47.088 INFO 40532 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2016-05-10 10:55:47.090 INFO 40532 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.0.33
2016-05-10 10:55:47.173 INFO 40532 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2016-05-10 10:55:47.173 INFO 40532 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1705 ms
2016-05-10 10:55:47.486 INFO 40532 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2016-05-10 10:55:47.489 INFO 40532 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-05-10 10:55:47.490 INFO 40532 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2016-05-10 10:55:47.490 INFO 40532 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2016-05-10 10:55:47.490 INFO 40532 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2016-05-10 10:55:47.763 INFO 40532 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@59e5ddf: startup date [Tue May 10 10:55:45 PDT 2016]; root of context hierarchy
2016-05-10 10:55:47.826 INFO 40532 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public org.springframework.http.ResponseEntity<java.lang.String> sanity.MyController.hello()
2016-05-10 10:55:47.829 INFO 40532 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-05-10 10:55:47.830 INFO 40532 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2016-05-10 10:55:47.864 INFO 40532 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-05-10 10:55:47.864 INFO 40532 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-05-10 10:55:47.908 INFO 40532 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-05-10 10:55:48.208 WARN 40532 --- [ main] arterDepricationWarningAutoConfiguration : spring-boot-starter-redis is deprecated as of Spring Boot 1.4, please migrate to spring-boot-starter-data-redis
2016-05-10 10:55:48.268 INFO 40532 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2016-05-10 10:55:48.320 ERROR 40532 --- [ main] o.a.coyote.http11.Http11NioProtocol : Failed to start end point associated with ProtocolHandler ["http-nio-8080"]

View File

@ -1,8 +0,0 @@
[source,http,options="nowrap"]
----
HTTP/1.1 {{statusCode}} {{statusReason}}
{{#headers}}
{{name}}: {{value}}
{{/headers}}
{{responseBody}}
----

View File

@ -1,16 +0,0 @@
<% endpoints.each { endpoint ->
if (endpoint.custom) { %>
include::{docs}/${endpoint.custom}[]
<% } else { %>
=== Link: ${endpoint.title}
Example curl request:
include::{generated}${endpoint.path}/curl-request.adoc[]
Example HTTP request:
include::{generated}${endpoint.path}/http-request.adoc[]
Example HTTP response:
include::{generated}${endpoint.path}/http-response.adoc[]
<% }
} %>

View File

@ -33,38 +33,28 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration.EndpointHypermediaEnabledCondition;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorMediaTypes;
import org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalBrowserMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HypermediaDisabled;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.UriTemplate;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.hateoas.hal.DefaultCurieProvider;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
@ -93,7 +83,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
@ConditionalOnClass(Link.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnBean(HttpMessageConverters.class)
@Conditional(EndpointHypermediaEnabledCondition.class)
@ConditionalOnEnabledEndpoint("actuator")
@EnableConfigurationProperties({ ResourceProperties.class,
ManagementServerProperties.class })
public class EndpointWebMvcHypermediaManagementContextConfiguration {
@ -104,7 +94,6 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
return properties::getContextPath;
}
@ConditionalOnEnabledEndpoint("actuator")
@Bean
public HalJsonMvcEndpoint halJsonMvcEndpoint(
ManagementServletContext managementServletContext,
@ -115,30 +104,6 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
return new HalJsonMvcEndpoint(managementServletContext);
}
@Bean
@ConditionalOnBean(DocsMvcEndpoint.class)
@ConditionalOnMissingBean(CurieProvider.class)
@ConditionalOnProperty(prefix = "endpoints.docs.curies", name = "enabled", matchIfMissing = false)
public DefaultCurieProvider curieProvider(ManagementServerProperties management,
DocsMvcEndpoint endpoint) {
String path = management.getContextPath() + endpoint.getPath()
+ "/#spring_boot_actuator__{rel}";
return new DefaultCurieProvider("boot", new UriTemplate(path));
}
@Configuration
static class DocsMvcEndpointConfiguration {
@Bean
@ConditionalOnEnabledEndpoint("docs")
@ConditionalOnResource(resources = "classpath:/META-INF/resources/spring-boot-actuator/docs/index.html")
public DocsMvcEndpoint docsMvcEndpoint(
ManagementServletContext managementServletContext) {
return new DocsMvcEndpoint(managementServletContext);
}
}
/**
* Controller advice that adds links to the actuator endpoint's path.
*/
@ -363,22 +328,4 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
}
static class EndpointHypermediaEnabledCondition extends AnyNestedCondition {
EndpointHypermediaEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnEnabledEndpoint("actuator")
static class ActuatorEndpointEnabled {
}
@ConditionalOnEnabledEndpoint("docs")
static class DocsEndpointEnabled {
}
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright 2012-2017 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.endpoint.mvc;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
/**
* {@link MvcEndpoint} to expose actuator documentation.
*
* @author Dave Syer
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "endpoints.docs")
public class DocsMvcEndpoint extends AbstractNamedMvcEndpoint {
private static final String DOCS_LOCATION = "classpath:/META-INF/resources/spring-boot-actuator/docs/";
private final ManagementServletContext managementServletContext;
private Curies curies = new Curies();
public Curies getCuries() {
return this.curies;
}
public DocsMvcEndpoint(ManagementServletContext managementServletContext) {
super("docs", "/docs", false);
this.managementServletContext = managementServletContext;
}
@RequestMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
public String browse() {
return "forward:" + this.managementServletContext.getContextPath() + getPath()
+ "/index.html";
}
@RequestMapping(value = "", produces = MediaType.TEXT_HTML_VALUE)
public String redirect() {
return "redirect:" + this.managementServletContext.getContextPath() + getPath()
+ "/";
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(
this.managementServletContext.getContextPath() + getPath() + "/**")
.addResourceLocations(DOCS_LOCATION);
}
/**
* 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.
*/
private boolean enabled = false;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@ -1,151 +0,0 @@
/*
* Copyright 2012-2017 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.autoconfigure;
import java.net.URL;
import com.jayway.jsonpath.JsonPath;
import net.minidev.json.JSONArray;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for the href of the actuator's curies link
*
* @author Andy Wilkinson
*/
public class BootCuriesHrefIntegrationTests {
private AnnotationConfigServletWebServerApplicationContext context;
@After
public void closeContext() {
this.context.close();
}
@Test
public void basicCuriesHref() {
int port = load("endpoints.docs.curies.enabled:true", "server.port:0");
assertThat(getCurieHref("http://localhost:" + port + "/application"))
.isEqualTo("http://localhost:" + port
+ "/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesHrefWithCustomContextPath() {
int port = load("endpoints.docs.curies.enabled:true", "server.port:0",
"server.servlet.context-path:/context");
assertThat(getCurieHref("http://localhost:" + port + "/context/application"))
.isEqualTo("http://localhost:" + port
+ "/context/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesHrefWithCustomServletPath() {
int port = load("endpoints.docs.curies.enabled:true", "server.port:0",
"server.servlet.path:/servlet");
assertThat(getCurieHref("http://localhost:" + port + "/servlet/application"))
.isEqualTo("http://localhost:" + port
+ "/servlet/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesHrefWithCustomServletAndContextPaths() {
int port = load("endpoints.docs.curies.enabled:true", "server.port:0",
"server.servlet.context-path:/context", "server.servlet.path:/servlet");
assertThat(getCurieHref("http://localhost:" + port
+ "/context/servlet/application")).isEqualTo("http://localhost:" + port
+ "/context/servlet/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesHrefWithCustomServletContextAndManagementContextPaths() {
int port = load("endpoints.docs.curies.enabled:true", "server.port:0",
"server.servlet.context-path:/context", "server.servlet.path:/servlet",
"management.context-path:/management");
assertThat(getCurieHref("http://localhost:" + port
+ "/context/servlet/management/")).isEqualTo("http://localhost:" + port
+ "/context/servlet/management/docs/#spring_boot_actuator__{rel}");
}
@Test
public void serverPathsAreIgnoredWithSeparateManagementPort() {
int port = load("endpoints.docs.curies.enabled:true", "server.port:0",
"server.servlet.context-path:/context", "server.servlet.path:/servlet",
"management.port:0");
assertThat(getCurieHref("http://localhost:" + port + "/application/"))
.isEqualTo("http://localhost:" + port
+ "/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void managementContextPathWithSeparateManagementPort() {
int port = load("endpoints.docs.curies.enabled:true",
"management.context-path:/management", "server.port:0",
"management.port:0");
assertThat(getCurieHref("http://localhost:" + port + "/management/"))
.isEqualTo("http://localhost:" + port
+ "/management/docs/#spring_boot_actuator__{rel}");
}
private int load(String... properties) {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.setClassLoader(new ClassLoader(getClass().getClassLoader()) {
@Override
public URL getResource(String name) {
if ("META-INF/resources/spring-boot-actuator/docs/index.html"
.equals(name)) {
return super.getResource("actuator-docs-index.html");
}
return super.getResource(name);
}
});
TestPropertyValues.of(properties).applyTo(this.context);
this.context.register(TestConfiguration.class);
new ServerPortInfoApplicationContextInitializer().initialize(this.context);
this.context.refresh();
return Integer.parseInt(
this.context.getEnvironment().getProperty("local.management.port"));
}
private String getCurieHref(String uri) {
ResponseEntity<String> response = new TestRestTemplate().getForEntity(uri,
String.class);
JSONArray bootCuriesHrefs = JsonPath.parse(response.getBody())
.read("_links.curies[?(@.name == 'boot')].href");
assertThat(bootCuriesHrefs).hasSize(1);
return (String) bootCuriesHrefs.get(0);
}
@Configuration
@MinimalActuatorHypermediaApplication
static class TestConfiguration {
}
}

View File

@ -16,13 +16,10 @@
package org.springframework.boot.actuate.autoconfigure;
import java.net.URL;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
@ -32,7 +29,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.hal.DefaultCurieProvider;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
@ -72,60 +68,11 @@ public class EndpointWebMvcHypermediaManagementContextConfigurationTests {
assertThat(this.context.getBeansOfType(ManagementServletContext.class))
.hasSize(1);
assertThat(this.context.getBeansOfType(HalJsonMvcEndpoint.class)).hasSize(1);
assertThat(this.context.getBeansOfType(DocsMvcEndpoint.class)).hasSize(1);
assertThat(this.context.getBeansOfType(DefaultCurieProvider.class)).isEmpty();
}
@Test
public void curiesEnabledWithDefaultPorts() {
load("endpoints.docs.curies.enabled:true");
assertThat(getCurieHref()).isEqualTo(
"http://localhost/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesEnabledWithRandomPorts() {
load("endpoints.docs.curies.enabled:true", "server.port:0", "management.port:0");
assertThat(getCurieHref()).isEqualTo(
"http://localhost/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesEnabledWithSpecificServerPort() {
load("endpoints.docs.curies.enabled:true", "server.port:8080");
assertThat(getCurieHref()).isEqualTo(
"http://localhost/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesEnabledWithSpecificManagementPort() {
load("endpoints.docs.curies.enabled:true", "management.port:8081");
assertThat(getCurieHref()).isEqualTo(
"http://localhost/application/docs/#spring_boot_actuator__{rel}");
}
@Test
public void curiesEnabledWithSpecificManagementAndServerPorts() {
load("endpoints.docs.curies.enabled:true", "server.port:8080",
"management.port:8081");
assertThat(getCurieHref()).isEqualTo(
"http://localhost/application/docs/#spring_boot_actuator__{rel}");
}
private void load(String... properties) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setClassLoader(new ClassLoader(getClass().getClassLoader()) {
@Override
public URL getResource(String name) {
if ("META-INF/resources/spring-boot-actuator/docs/index.html"
.equals(name)) {
return super.getResource("actuator-docs-index.html");
}
return super.getResource(name);
}
});
TestPropertyValues.of(properties).applyTo(this.context);
this.context.register(TestConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
@ -133,13 +80,6 @@ public class EndpointWebMvcHypermediaManagementContextConfigurationTests {
this.context.refresh();
}
private String getCurieHref() {
DefaultCurieProvider curieProvider = this.context
.getBean(DefaultCurieProvider.class);
Link link = (Link) curieProvider.getCurieInformation(null).iterator().next();
return link.getHref();
}
@Configuration
@EnableConfigurationProperties({ ManagementServerProperties.class,
ServerProperties.class })

View File

@ -36,7 +36,6 @@ import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
@ -44,7 +43,6 @@ import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
@ -90,7 +88,6 @@ public class MvcEndpointPathConfigurationTests {
new Object[] { "beans", BeansEndpoint.class },
new Object[] { "configprops",
ConfigurationPropertiesReportEndpoint.class },
new Object[] { "docs", DocsMvcEndpoint.class },
new Object[] { "dump", DumpEndpoint.class },
new Object[] { "env", EnvironmentMvcEndpoint.class },
new Object[] { "flyway", FlywayEndpoint.class },
@ -169,11 +166,6 @@ public class MvcEndpointPathConfigurationTests {
return new LiquibaseEndpoint(new SpringLiquibase());
}
@Bean
public DocsMvcEndpoint docs(ManagementServletContext managementServletContext) {
return new DocsMvcEndpoint(managementServletContext);
}
}
}

View File

@ -240,11 +240,6 @@
<artifactId>spring-boot-actuator</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>

View File

@ -144,11 +144,6 @@ If you are using Spring MVC, the following additional endpoints can also be used
|===
| ID | Description | Sensitive Default
|`docs`
|Displays documentation, including example requests and responses, for the Actuator's
endpoints. Requires `spring-boot-actuator-docs` to be on the classpath.
|false
|`heapdump`
|Returns a GZip compressed `hprof` heap dump file.
|true

View File

@ -53,7 +53,6 @@
<module>../spring-boot-autoconfigure</module>
<module>../spring-boot-test-autoconfigure</module>
<module>../spring-boot-actuator</module>
<module>../spring-boot-actuator-docs</module>
<module>../spring-boot-devtools</module>
<module>../spring-boot-starters</module>
<module>../spring-boot-cli</module>

View File

@ -27,10 +27,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>

View File

@ -59,14 +59,6 @@ public class SampleHypermediaJpaApplicationIntegrationTests {
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void docs() throws Exception {
MvcResult response = this.mockMvc
.perform(get("/admin/docs/").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn();
System.err.println(response.getResponse().getContentAsString());
}
@Test
public void browser() throws Exception {
MvcResult response = this.mockMvc.perform(get("/").accept(MediaType.TEXT_HTML))

View File

@ -31,10 +31,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -27,10 +27,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -28,10 +28,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>