diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml index ab8b9bb38f..b54f924ec2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml @@ -419,6 +419,11 @@ spring-integration-jmx test + + org.springframework.restdocs + spring-restdocs-mockmvc + test + org.springframework.security spring-security-test @@ -435,4 +440,157 @@ true + + + full + + + full + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + org.apache.ant + ant-nodeps + 1.8.1 + + + org.tigris.antelope + antelopetasks + 3.2.10 + + + + + set-up-maven-properties + prepare-package + + run + + + true + + + + + + + + + + + + + + + package-docs-zip + package + + run + + + + + + + + + + + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + + + generate-html-documentation + prepare-package + + process-asciidoc + + + html + + + + generate-pdf-documentation + prepare-package + + process-asciidoc + + + pdf + + + + + index.adoc + + ${version-type} + ${project.version} + ${project.build.directory}/generated-snippets/ + + + + + org.asciidoctor + asciidoctorj-pdf + 1.5.0-alpha.11 + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-zip + + attach-artifact + + + + + ${project.build.directory}/${project.artifactId}-${project.version}-docs.zip + zip + docs + + + + + + + + + + diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/auditevents.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/auditevents.adoc new file mode 100644 index 0000000000..60eb448c9e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/auditevents.adoc @@ -0,0 +1,45 @@ +[[audit-events]] += Audit Events (`auditevents`) + +The `auditevents` endpoint provides information about the application's audit events. + + + +[[audit-events-retrieving]] +== Retrieving Audit Events + +To retrieve the audit events, make a `GET` request to `/application/auditevents`, as shown +in the following curl-based example: + +include::{snippets}auditevents/filtered/curl-request.adoc[] + +The preceding example retrieves `logout` events for the principal, `alice`, that occurred +after 09:37 on 7 November 2017 in the UTC timezone. The resulting response is similar to +the following: + +include::{snippets}auditevents/filtered/http-response.adoc[] + + + +[[audit-events-retrieving-query-parameters]] +=== Query Parameters + +The endpoint uses query parameters to limit the events that it returns. The following + table shows the supported query parameters: + +[cols="2,4"] +include::{snippets}auditevents/filtered/request-parameters.adoc[] + +The `after` parameter is required. You can also use one or both of the `principal` and +`type` parameters to further limit the results. + + + +[[audit-events-retrieving-response-structure]] +=== Response Structure + +The response contains details of all of the audit events that matched the query. The +following table describes the structure of the response: + +[cols="2,1,3"] +include::{snippets}auditevents/after/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/beans.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/beans.adoc new file mode 100644 index 0000000000..1371ef5d17 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/beans.adoc @@ -0,0 +1,29 @@ +[[beans]] += Beans (`beans`) + +The `beans` endpoint provides information about the application's beans. + + + +[[beans-retrieving]] +== Retrieving the Beans + +To retrieve the beans, make a `GET` request to `/application/beans`, as shown in the +following curl-based example: + +include::{snippets}beans/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}beans/http-response.adoc[] + + + +[[beans-retrieving-response-structure]] +=== Response Structure + +The response contains details of the application's beans. The following table describes +the structure of the response: + +[cols="2,1,3"] +include::{snippets}beans/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/conditions.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/conditions.adoc new file mode 100644 index 0000000000..cf492bece3 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/conditions.adoc @@ -0,0 +1,30 @@ +[[conditions]] += Conditions Evaluation Report (`conditions`) + +The `conditions` endpoint provides information about the evaluation of conditions on +configuration and auto-configuration classes. + + + +[[conditions-retrieving]] +== Retrieving the Report + +To retrieve the report, make a `GET` request to `/application/conditions`, as shown in +the following curl-based example: + +include::{snippets}conditions/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}conditions/http-response.adoc[] + + + +[[conditions-retrieving-response-structure]] +=== Response Structure + +The response contains details of the application's condition evaluation. The following +table describes the structure of the response: + +[cols="3,1,3"] +include::{snippets}conditions/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/configprops.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/configprops.adoc new file mode 100644 index 0000000000..1b3dac2005 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/configprops.adoc @@ -0,0 +1,30 @@ +[[configprops]] += Configuration Properties (`configprops`) + +The `configprops` endpoint provides information about the application's +`@ConfigurationProperties` beans. + + + +[[configprops-retrieving]] +== Retrieving the `@ConfigurationProperties` Bean + +To retrieve the `@ConfigurationProperties` beans, make a `GET` request to +`/application/configprops`, as shown in the following curl-based example: + +include::{snippets}configprops/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}configprops/http-response.adoc[] + + + +[[configprops-retrieving-response-structure]] +=== Response Structure + +The response contains details of the application's `@ConfigurationProperties` beans. The +following table describes the structure of the response: + +[cols="2,1,3"] +include::{snippets}configprops/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/env.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/env.adoc new file mode 100644 index 0000000000..782588d6a4 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/env.adoc @@ -0,0 +1,55 @@ +[[env]] += Environment (`env`) + +The `env` endpoint provides information about the application's `Environment`. + + + +[[env-entire]] +== Retrieving the Entire Environment + +To retrieve the entire environment, make a `GET` request to `/application/env`, as shown in +the following curl-based example: + +include::{snippets}env/all/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}env/all/http-response.adoc[] + + + +[[env-entire-response-structure]] +=== Response Structure + +The response contains details of the application's `Environment`. The following table +describes the structure of the response: + +[cols="3,1,3"] +include::{snippets}env/all/response-fields.adoc[] + + + +[[env-single-property]] +== Retrieving a Single Property + +To retrieve a single property, make a `GET` request to `/application/env/{property.name}`, +as shown in the following curl-based example: + +include::{snippets}env/single/curl-request.adoc[] + +The preceding example retrieves information about the property named +`com.example.cache.max-size`. The resulting response is similar to the following: + +include::{snippets}env/single/http-response.adoc[] + + + +[[env-single-response-structure]] +=== Response Structure + +The response contains details of the requested property. The following table describes the +structure of the response: + +[cols="3,1,3"] +include::{snippets}env/single/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/flyway.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/flyway.adoc new file mode 100644 index 0000000000..7a8752da8a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/flyway.adoc @@ -0,0 +1,29 @@ +[[flyway]] += Flyway (`flyway`) + +The `flyway` endpoint provides information about database migrations performed by Flyway. + + + +[[flyway-retrieving]] +== Retrieving the Migrations + +To retrieve the migrations, make a `GET` request to `/application/flyway`, as shown in the +following curl-based example: + +include::{snippets}flyway/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}flyway/http-response.adoc[] + + + +[[flyway-retrieving-response-structure]] +=== Response Structure + +The response contains details of the application's Flyway migrations. The following table +describes the structure of the response: + +[cols="2,1,3"] +include::{snippets}flyway/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/health.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/health.adoc new file mode 100644 index 0000000000..e03baa1281 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/health.adoc @@ -0,0 +1,29 @@ +[[health]] += Health (`health`) + +The `health` endpoint provides detailed information about the health of the application. + + + +[[health-retrieving]] +== Retrieving the Health + +To retrieve the health of the application, make a `GET` request to `/application/health`, +as shown in the following curl-based example: + +include::{snippets}health/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}health/http-response.adoc[] + + + +[[health-retrieving-response-structure]] +=== Response Structure + +The response contains details of the health of the application. The following table +describes the structure of the response: + +[cols="2,1,3"] +include::{snippets}health/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/heapdump.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/heapdump.adoc new file mode 100644 index 0000000000..f689532017 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/heapdump.adoc @@ -0,0 +1,20 @@ +[[heapdump]] += Heap Dump (`heapdump`) + +The `heapdump` endpoint provides a heap dump from the application's JVM. + + + +[[heapdump-retrieving]] +== Retrieving the Heap Dump + +To retrieve the heap dump, make a `GET` request to `/application/heapdump`. The response +is binary data in https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html[ +HPROF] format and can be large. Typically, you should save the response to disk for +subsequent analysis. When using curl, this can be achieved by using the `-O` option, +as shown in the following example: + +include::{snippets}heapdump/curl-request.adoc[] + +The preceding example results in a file named `heapdump` being written to the current +working directory. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/info.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/info.adoc new file mode 100644 index 0000000000..0efaba32a0 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/info.adoc @@ -0,0 +1,47 @@ +[[info]] += Info (`info`) + +The `info` endpoint provides general information about the application. + + + +[[info-retrieving]] +== Retrieving the Info + +To retrieve the information about the application, make a `GET` request to +`/application/info`, as shown in the following curl-based example: + +include::{snippets}info/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}info/http-response.adoc[] + + + +[[info-retrieving-response-structure]] +=== Response Structure + +The response contains general information about the application. Each section of the +response is contributed by an `InfoContributor`. Spring Boot provides `build` and `git` +contributions. + + + +[[info-retrieving-response-structure-build]] +==== `build` Response Structure + +The following table describe the structure of the `build` section of the response: + +[cols="2,1,3"] +include::{snippets}info/response-fields-beneath-build.adoc[] + + + +[[info-retrieving-response-structure-git]] +==== `git` Response Structure + +The following table describes the structure of the `git` section of the response: + +[cols="2,1,3"] +include::{snippets}info/response-fields-beneath-git.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/liquibase.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/liquibase.adoc new file mode 100644 index 0000000000..bede34f295 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/liquibase.adoc @@ -0,0 +1,30 @@ +[[liquibase]] += Liquibase (`liquibase`) + +The `liquibase` endpoint provides information about database change sets applied by +Liquibase. + + + +[[liquibase-retrieving]] +== Retrieving the Changes + +To retrieve the changes, make a `GET` request to `/application/liquibase`, as shown in the +following curl-based example: + +include::{snippets}liquibase/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}liquibase/http-response.adoc[] + + + +[[liquibase-retrieving-response-structure]] +=== Response Structure + +The response contains details of the application's Liquibase change sets. The following +table describes the structure of the response: + +[cols="2,1,3"] +include::{snippets}liquibase/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/logfile.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/logfile.adoc new file mode 100644 index 0000000000..ed2d919a6e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/logfile.adoc @@ -0,0 +1,35 @@ +[[log-file]] += Log File (`logfile`) + +The `logfile` endpoint provides access to the contents of the application's log file. + + + +[[logfile-retrieving]] +== Retrieving the Log File + +To retrieve the log file, make a `GET` request to `/application/logfile`, as shown in the +following curl-based example: + +include::{snippets}logfile/entire/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}logfile/entire/http-response.adoc[] + + + +[[logfile-retrieving-part]] +== Retrieving Part of the Log File + +NOTE: Retrieving part of the log file is not supported when using Jersey. + +To retrieve part of the log file, make a `GET` request to `/application/logfile` by using +the `Range` header, as shown in the following curl-based example: + +include::{snippets}logfile/range/curl-request.adoc[] + +The preceding example retrieves the first 1024 bytes of the log file. The resulting +response is similar to the following: + +include::{snippets}logfile/range/http-response.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/loggers.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/loggers.adoc new file mode 100644 index 0000000000..edc07c648a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/loggers.adoc @@ -0,0 +1,93 @@ +[[loggers]] += Loggers (`loggers`) + +The `loggers` endpoint provides access to the application's loggers and the configuration +of their levels. + + + +[[loggers-all]] +== Retrieving All Loggers + +To retrieve the application's loggers, make a `GET` request to `/application/loggers`, as +shown in the following curl-based example: + +include::{snippets}loggers/all/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}loggers/all/http-response.adoc[] + + + +[[loggers-all-response-structure]] +=== Response Structure + +The response contains details of the application's loggers. The following table describes +the structure of the response: + +[cols="3,1,3"] +include::{snippets}loggers/all/response-fields.adoc[] + + + +[[loggers-single]] +== Retrieving a Single Logger + +To retrieve a single logger, make a `GET` request to `/application/loggers/{logger.name}`, +as shown in the following curl-based example: + +include::{snippets}loggers/single/curl-request.adoc[] + +The preceding example retrieves information about the logger named `com.example`. The +resulting response is similar to the following: + +include::{snippets}loggers/single/http-response.adoc[] + + + +[[loggerse-single-response-structure]] +=== Response Structure + +The response contains details of the requested logger. The following table describes the +structure of the response: + +[cols="3,1,3"] +include::{snippets}loggers/single/response-fields.adoc[] + + + +[[loggers-setting-level]] +== Setting a Log Level + +To set the level of a logger, make a `POST` request to +`/application/loggers/{logger.name}` with a JSON body that specifies the configured level +for the logger, as shown in the following curl-based example: + +include::{snippets}loggers/set/curl-request.adoc[] + +The preceding example sets the `configuredLevel` of the `com.example` logger to `DEBUG`. + + + +[[loggers-setting-level-request-structure]] +=== Request Structure + +The request specifies the desired level of the logger. The following table describes the +structure of the request: + +[cols="3,1,3"] +include::{snippets}loggers/set/request-fields.adoc[] + + + +[[loggers-clearing-level]] +== Clearing a Log Level + +To clear the level of a logger, make a `POST` request to +`/application/loggers/{logger.name}` with a JSON body containing an empty object, as shown +in the following curl-based example: + +include::{snippets}loggers/clear/curl-request.adoc[] + +The preceding example clears the configured level of the `com.example` logger. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/metrics.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/metrics.adoc new file mode 100644 index 0000000000..c9571e8057 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/metrics.adoc @@ -0,0 +1,80 @@ +[[metrics]] += Metrics (`metrics`) + +The `metrics` endpoint provides access to application metrics. + + + +[[metrics-retrieving-names]] +== Retrieving Metric Names + +To retrieve the names of the available metrics, make a `GET` request to +`/application/metrics`, as shown in the following curl-based example: + +include::{snippets}metrics/names/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}metrics/names/http-response.adoc[] + + + +[[metrics-retrieving-names-response-structure]] +=== Response Structure + +The response contains details of the metric names. The following table describes the +structure of the response: + +[cols="3,1,2"] +include::{snippets}metrics/names/response-fields.adoc[] + + + +[[metrics-retrieving-metric]] +== Retrieving a Metric + +To retrieve a metric, make a `GET` request to `/application/metrics/{metric.name}`, as +shown in the following curl-based example: + +include::{snippets}metrics/metric/curl-request.adoc[] + +The preceding example retrieves information about the metric named `jvm.memory.max`. The +resulting response is similar to the following: + +include::{snippets}metrics/metric/http-response.adoc[] + + + +[[metrics-retrieving-metric-query-parameters]] +=== Query Parameters + +The endpoint uses query parameters to <> into a metric +by using its tags. The following table shows the single supported query parameter: + +[cols="2,4"] +include::{snippets}metrics/metric-with-tags/request-parameters.adoc[] + + + +[[metrics-retrieving-metric-response-structure]] +=== Response structure + +The response contains details of the metric. The following table describes the structure +of the response: + +include::{snippets}metrics/metric/response-fields.adoc[] + + +[[metrics-drilling-down]] +== Drilling Down + +To drill down into a metric, make a `GET` request to `/application/metrics/{metric.name}` +using the `tag` query parameter, as shown in the following curl-based example: + +include::{snippets}metrics/metric-with-tags/curl-request.adoc[] + +The preceding example retrieves the `jvm.memory.max` metric, where the `area` tag has a +value of `nonheap` and the `id` attribute has a value of `Code Cache`. The resulting +response is similar to the following: + +include::{snippets}metrics/metric-with-tags/http-response.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/prometheus.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/prometheus.adoc new file mode 100644 index 0000000000..65bbdf17dc --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/prometheus.adoc @@ -0,0 +1,19 @@ +[[prometheus]] += Prometheus (`prometheus`) + +The `prometheus` endpoint provides Spring Boot application's metrics in the format +required for scraping by a Prometheus server. + + + +[[prometheus-retrieving]] +== Retrieving the Metrics + +To retrieve the metrics, make a `GET` request to `/application/prometheus`, as shown in +the following curl-based example: + +include::{snippets}prometheus/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}prometheus/http-response.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/scheduledtasks.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/scheduledtasks.adoc new file mode 100644 index 0000000000..2b38868023 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/scheduledtasks.adoc @@ -0,0 +1,30 @@ +[[scheduled-tasks]] += Scheduled Tasks (`scheduledtasks`) + +The `scheduledtasks` endpoint provides information about the application's scheduled +tasks. + + + +[[scheduled-tasks-retrieving]] +== Retrieving the Scheduled Tasks + +To retrieve the scheduled tasks, make a `GET` request to `/application/scheduledtasks`, +as shown in the following curl-based example: + +include::{snippets}scheduled-tasks/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}scheduled-tasks/http-response.adoc[] + + + +[[scheduled-tasks-retrieving-response-structure]] +=== Response Structure + +The response contains details of the application's scheduled tasks. The following table +describes the structure of the response: + +[cols="2,1,3"] +include::{snippets}scheduled-tasks/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/sessions.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/sessions.adoc new file mode 100644 index 0000000000..8a0a4b5015 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/sessions.adoc @@ -0,0 +1,84 @@ +[[sessions]] += Sessions (`sessions`) + +The `sessions` endpoint provides information about the application's HTTP sessions that +are managed by Spring Session. + + + +[[sessions-retrieving]] +== Retrieving Sessions + +To retrieve the sessions, make a `GET` request to `/application/sessions`, as shown in the +following curl-based example: + +include::{snippets}sessions/username/curl-request.adoc[] + +The preceding examples retrieves all of the sessions for the user whose username is +`alice`. + +The resulting response is similar to the following: + +include::{snippets}sessions/username/http-response.adoc[] + + + +[[sessions-retrieving-query-parameters]] +=== Query Parameters + +The endpoint uses query parameters to limit the sessions that it returns. The following +table shows the single required query parameter: + +[cols="2,4"] +include::{snippets}sessions/username/request-parameters.adoc[] + + + +[[sessions-retrieving-response-structure]] +=== Response Structure + +The response contains details of the matching sessions. The following table describes the +structure of the response: + +[cols="3,1,3"] +include::{snippets}sessions/username/response-fields.adoc[] + + + +[[sessions-retrieving-id]] +== Retrieving a Single Session + +To retrieve a single session, make a `GET` request to `/application/sessions/{id}`, as +shown in the following curl-based example: + +include::{snippets}sessions/id/curl-request.adoc[] + +The preceding example retrieves the session with the `id` of +`4db5efcc-99cb-4d05-a52c-b49acfbb7ea9`. The resulting response is similar to the +following: + +include::{snippets}sessions/id/http-response.adoc[] + + + +[[sessions-retrieving-id-response-structure]] +=== Response Structure + +The response contains details of the requested session. The following table describes the +structure of the response: + +[cols="3,1,3"] +include::{snippets}sessions/id/response-fields.adoc[] + + + +[[sessions-deleting]] +== Deleting a Session + +To delete a session, make a `DELETE` request to `/application/sessions/{id}`, as shown in +the following curl-based example: + +include::{snippets}sessions/delete/curl-request.adoc[] + +The preceding example deletes the session with the `id` of +`4db5efcc-99cb-4d05-a52c-b49acfbb7ea9`. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/shutdown.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/shutdown.adoc new file mode 100644 index 0000000000..b2c8f689e4 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/shutdown.adoc @@ -0,0 +1,29 @@ +[[shutdown]] += Shutdown (`shutdown`) + +The `shutdown` endpoint is used to shut down the application. + + + +[[shutdown-shutting-down]] +== Shutting Down the Application + +To shut down the application, make a `POST` request to `/application/shutdown`, as shown +in the following curl-based example: + +include::{snippets}shutdown/curl-request.adoc[] + +A response similar to the following is produced: + +include::{snippets}shutdown/http-response.adoc[] + + + +[[shutdowm-shutting-down-response-structure]] +=== Response Structure + +The response contains details of the result of the shutdown request. The following table +describes the structure of the response: + +[cols="3,1,3"] +include::{snippets}shutdown/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/status.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/status.adoc new file mode 100644 index 0000000000..fb16a60b56 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/status.adoc @@ -0,0 +1,29 @@ +[[status]] += Status (`status`) + +The `status` endpoint provides an overview of the status of the application. + + + +[[status-retrieving]] +== Retrieving the Status + +To retrieve the status of the application, make a `GET` request to `/application/status`, +as shown in the following curl-based example: + +include::{snippets}status/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}status/http-response.adoc[] + + + +[[status-retrieving-response-structure]] +=== Response Structure + +The response contains the status of the application. The following table describes the +structure of the response: + +[cols="2,1,3"] +include::{snippets}status/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/threaddump.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/threaddump.adoc new file mode 100644 index 0000000000..7f72308aaa --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/threaddump.adoc @@ -0,0 +1,29 @@ +[[threaddump]] += Thread Dump (`threaddump`) + +The `threaddump` endpoint provides a thread dump from the application's JVM. + + + +[[threaddump-retrieving]] +== Retrieving the Thread Dump + +To retrieve the thread dump, make a `GET` request to `/application/threaddump`, as shown +in the following curl-based example: + +include::{snippets}threaddump/curl-request.adoc[] + +The resulting response is similar to the following: + +include::{snippets}threaddump/http-response.adoc[] + + + +[[threaddump-retrieving-response-structure]] +=== Response Structure + +The response contains details of the JVM's threads. The following table describes the +structure of the response: + +[cols="3,1,2"] +include::{snippets}threaddump/response-fields.adoc[] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc new file mode 100644 index 0000000000..e30d7e6616 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc @@ -0,0 +1,70 @@ += Spring Boot Actuator Web API Documentation +Andy Wilkinson +:doctype: book +:toc: left +:toclevels: 4 +:source-highlighter: prettify +:numbered: +:icons: font +:hide-uri-scheme: + +This API documentation describes Spring Boot Actuators web endpoints. + + + +[[overview]] +== Overview + +Before you proceed, you should read the following topics: + +* <> +* <> + + + +[[overview-endpoint-urls]] +=== URLs + +By default, all web endpoints are available beneath the path `/application` with URLs of +the form `/application/{id}`. The `/application` base path can be configured by using the +`management.endpoints.web.base-path` property, as shown in the following example: + +[source,properties,indent=0] +---- + management.endpoints.web.base-path=/manage +---- + +The preceding `application.properties` example changes the form of the endpoint URLs from +`/application/{id}` to `/manage/{id}`. For example, the URL `info` endpoint would become +`/manage/info`. + + + +[[overview-timestamps]] +=== Timestamps + +All timestamps that are consumed by the endpoints, either as query parameters or in the +request body, must be formatted as an offset date and time as specified in +https://en.wikipedia.org/wiki/ISO_8601[ISO 8601]. + + + +include::endpoints/auditevents.adoc[leveloffset=+1] +include::endpoints/beans.adoc[leveloffset=+1] +include::endpoints/conditions.adoc[leveloffset=+1] +include::endpoints/configprops.adoc[leveloffset=+1] +include::endpoints/env.adoc[leveloffset=+1] +include::endpoints/flyway.adoc[leveloffset=+1] +include::endpoints/health.adoc[leveloffset=+1] +include::endpoints/heapdump.adoc[leveloffset=+1] +include::endpoints/info.adoc[leveloffset=+1] +include::endpoints/liquibase.adoc[leveloffset=+1] +include::endpoints/logfile.adoc[leveloffset=+1] +include::endpoints/loggers.adoc[leveloffset=+1] +include::endpoints/metrics.adoc[leveloffset=+1] +include::endpoints/prometheus.adoc[leveloffset=+1] +include::endpoints/scheduledtasks.adoc[leveloffset=+1] +include::endpoints/sessions.adoc[leveloffset=+1] +include::endpoints/shutdown.adoc[leveloffset=+1] +include::endpoints/status.adoc[leveloffset=+1] +include::endpoints/threaddump.adoc[leveloffset=+1] diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java new file mode 100644 index 0000000000..88bc790ff4 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java @@ -0,0 +1,142 @@ +/* + * 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.endpoint.web.documentation; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.flywaydb.core.internal.util.StringUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor; +import org.springframework.restdocs.operation.preprocess.OperationPreprocessor; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + * Abstract base class for tests that generate endpoint documentation using Spring REST + * Docs. + * + * @author Andy Wilkinson + */ +@RunWith(SpringRunner.class) +@SpringBootTest(properties = { "spring.jackson.serialization.indent_output=true", + "management.endpoints.web.expose=*" }) +public abstract class AbstractEndpointDocumentationTests { + + @Rule + public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + protected MockMvc mockMvc; + + @Autowired + private WebApplicationContext applicationContext; + + @Before + public void before() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext) + .apply(MockMvcRestDocumentation + .documentationConfiguration(this.restDocumentation).uris()) + .build(); + } + + protected String describeEnumValues(Class> enumType) { + return StringUtils + .collectionToCommaDelimitedString(Stream.of(enumType.getEnumConstants()) + .map((constant) -> "`" + constant.name() + "`") + .collect(Collectors.toList())); + } + + protected OperationPreprocessor limit(String key) { + return limit(key, (candidate) -> true); + } + + @SuppressWarnings("unchecked") + protected OperationPreprocessor limit(String key, Predicate filter) { + return new ContentModifyingOperationPreprocessor((content, mediaType) -> { + ObjectMapper objectMapper = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT); + try { + Map payload = objectMapper.readValue(content, Map.class); + Object entry = payload.get(key); + if (entry instanceof Map) { + payload.put(key, select((Map) entry, filter)); + } + else { + payload.put(key, select((List) entry, filter)); + } + return objectMapper.writeValueAsBytes(payload); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + }); + } + + @SuppressWarnings("unchecked") + private Map select(Map candidates, + Predicate filter) { + Map selected = new HashMap(); + candidates.entrySet().stream().filter((candidate) -> filter.test((T) candidate)) + .limit(3) + .forEach((entry) -> selected.put(entry.getKey(), entry.getValue())); + return selected; + } + + @SuppressWarnings("unchecked") + private List select(List candidates, Predicate filter) { + return candidates.stream().filter((candidate) -> filter.test((T) candidate)) + .limit(3).collect(Collectors.toList()); + } + + @Configuration + @Import({ JacksonAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class, + DispatcherServletAutoConfiguration.class, EndpointAutoConfiguration.class, + WebEndpointAutoConfiguration.class, + WebMvcEndpointManagementContextConfiguration.class, + PropertyPlaceholderAutoConfiguration.class }) + static class BaseDocumentationConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java new file mode 100644 index 0000000000..e969eef757 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java @@ -0,0 +1,118 @@ +/* + * 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.endpoint.web.documentation; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; + +import org.junit.Test; + +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.boot.actuate.audit.AuditEventRepository; +import org.springframework.boot.actuate.audit.AuditEventsEndpoint; +import org.springframework.boot.actuate.audit.AuditEventsEndpointWebExtension; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing {@link AuditEventsEndpoint}. + * + * @author Andy Wilkinson + */ +public class AuditEventsEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @MockBean + private AuditEventRepository repository; + + @Test + public void allAuditEventsAfter() throws Exception { + String queryTimestamp = "2017-11-07T09:37Z"; + given(this.repository.find(any(), any(), any())).willReturn( + Arrays.asList(new AuditEvent("alice", "logout", Collections.emptyMap()))); + this.mockMvc + .perform(get("/application/auditevents").param("after", queryTimestamp)) + .andExpect(status().isOk()) + .andDo(document("auditevents/after", responseFields( + fieldWithPath("events").description("An array of audit events."), + fieldWithPath("events.[].timestamp") + .description("The timestamp of when the event occurred."), + fieldWithPath("events.[].principal") + .description("The principal that triggered the event."), + fieldWithPath("events.[].type") + .description("The type of the event.")))); + } + + @Test + public void filteredAuditEvents() throws Exception { + ZonedDateTime now = ZonedDateTime.now(); + String queryTimestamp = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(now); + Date date = new Date(now.toEpochSecond() * 1000); + given(this.repository.find("alice", date, "logout")).willReturn( + Arrays.asList(new AuditEvent("alice", "logout", Collections.emptyMap()))); + this.mockMvc + .perform(get("/application/auditevents").param("principal", "alice") + .param("after", queryTimestamp).param("type", "logout")) + .andExpect(status().isOk()) + .andDo(document("auditevents/filtered", + requestParameters( + parameterWithName("after").description( + "Restricts the events to those that occurred " + + "after the given time. Required."), + parameterWithName("principal").description( + "Restricts the events to those with the given " + + "principal. Optional."), + parameterWithName("type").description( + "Restricts the events to those with the given " + + "type. Optional.")))); + verify(this.repository).find("alice", date, "logout"); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public AuditEventsEndpoint auditEventsEndpoint(AuditEventRepository repository) { + return new AuditEventsEndpoint(repository); + } + + @Bean + public AuditEventsEndpointWebExtension adAuditEventsWebEndpointExtension( + AuditEventsEndpoint delegate) { + return new AuditEventsEndpointWebExtension(delegate); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java new file mode 100644 index 0000000000..ebad7a9811 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java @@ -0,0 +1,95 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Test; + +import org.springframework.boot.actuate.beans.BeansEndpoint; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.restdocs.payload.ResponseFieldsSnippet; +import org.springframework.util.CollectionUtils; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing {@link BeansEndpoint}. + * + * @author Andy Wilkinson + */ +public class BeansEndpointDocumentationTests extends AbstractEndpointDocumentationTests { + + @Test + public void beans() throws Exception { + List beanFields = Arrays + .asList(fieldWithPath("aliases").description("Names of any aliases."), + fieldWithPath("scope") + .description("Scope of the bean."), + fieldWithPath("type").description("Fully qualified type of the bean."), + fieldWithPath("resource") + .description("Resource in which the bean was defined, if any.") + .optional(), + fieldWithPath("dependencies").description("Names of any dependencies.")); + ResponseFieldsSnippet responseFields = responseFields( + fieldWithPath("contextId").description("ID of the application context."), + fieldWithPath("beans.*") + .description("Beans in the application context keyed by name.")) + .andWithPrefix("beans.*.", beanFields) + .and(subsectionWithPath("parent") + .description("Beans in the parent application " + + "context, if any.") + .type(JsonFieldType.OBJECT).optional()); + this.mockMvc.perform(get("/application/beans")).andExpect(status().isOk()) + .andDo(document("beans", + preprocessResponse(limit("beans", this::isIndependentBean)), + responseFields)); + } + + private boolean isIndependentBean(Entry> bean) { + return CollectionUtils.isEmpty((Collection) bean.getValue().get("aliases")) + && CollectionUtils + .isEmpty((Collection) bean.getValue().get("dependencies")); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public BeansEndpoint beansEndpoint(ConfigurableApplicationContext context) { + return new BeansEndpoint(context); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java new file mode 100644 index 0000000000..38159bc38e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java @@ -0,0 +1,122 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint; +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.JsonFieldType; +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.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing {@link ConditionsReportEndpoint}. + * + * @author Andy Wilkinson + */ +public class ConditionsReportEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Rule + public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext applicationContext; + + @Override + @Before + public void before() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext) + .apply(MockMvcRestDocumentation + .documentationConfiguration(this.restDocumentation).uris()) + .build(); + } + + @Test + public void conditions() throws Exception { + this.mockMvc.perform(get("/application/conditions")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("conditions", + preprocessResponse(limit("positiveMatches"), + limit("negativeMatches")), + responseFields( + fieldWithPath("positiveMatches").description( + "Classes and methods with conditions that were matched."), + fieldWithPath("positiveMatches.*.[].condition") + .description("Name of the condition."), + fieldWithPath("positiveMatches.*.[].message").description( + "Details of why the condition was matched."), + fieldWithPath("negativeMatches").description( + "Classes and methods with conditions that were not matched."), + fieldWithPath("negativeMatches.*.notMatched") + .description("Conditions that were matched."), + fieldWithPath("negativeMatches.*.notMatched.[].condition") + .description("Name of the condition."), + fieldWithPath("negativeMatches.*.notMatched.[].message") + .description( + "Details of why the condition was not matched."), + fieldWithPath("negativeMatches.*.matched") + .description("Conditions that were matched."), + fieldWithPath("negativeMatches.*.matched.[].condition") + .description("Name of the condition.") + .type(JsonFieldType.STRING).optional(), + fieldWithPath("negativeMatches.*.matched.[].message") + .description( + "Details of why the condition was matched.") + .type(JsonFieldType.STRING).optional(), + fieldWithPath("unconditionalClasses").description( + "Names of unconditional auto-configuration classes, if any.")))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public ConditionsReportEndpoint autoConfigurationReportEndpoint( + ConfigurableListableBeanFactory beanFactory) { + ConditionEvaluationReport conditionEvaluationReport = ConditionEvaluationReport + .get(beanFactory); + conditionEvaluationReport.recordEvaluationCandidates( + Arrays.asList(PropertyPlaceholderAutoConfiguration.class.getName())); + return new ConditionsReportEndpoint(conditionEvaluationReport); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java new file mode 100644 index 0000000000..f71cf51cdc --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java @@ -0,0 +1,76 @@ +/* + * 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.endpoint.web.documentation; + +import org.junit.Test; + +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.JsonFieldType; + +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing + * {@link ConfigurationPropertiesReportEndpoint}. + * + * @author Andy Wilkinson + */ +public class ConfigurationPropertiesReportEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void configProps() throws Exception { + this.mockMvc.perform(get("/application/configprops")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("configprops", + preprocessResponse(limit("beans")), + responseFields( + fieldWithPath("contextId") + .description("ID of the application context."), + fieldWithPath("beans.*").description( + "`@ConfigurationProperties` beans keyed by bean name."), + fieldWithPath("beans.*.prefix").description( + "Prefix applied to the names of the bean's properties."), + subsectionWithPath("beans.*.properties").description( + "Properties of the bean as name-value pairs."), + subsectionWithPath("parent") + .description( + "`@ConfigurationProperties` beans in the parent " + + "context, if any.") + .optional().type(JsonFieldType.OBJECT)))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public ConfigurationPropertiesReportEndpoint endpoint() { + return new ConfigurationPropertiesReportEndpoint(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/EnvironmentEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/EnvironmentEndpointDocumentationTests.java new file mode 100644 index 0000000000..37a7e4b296 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/EnvironmentEndpointDocumentationTests.java @@ -0,0 +1,179 @@ +/* + * 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.endpoint.web.documentation; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.junit.Test; + +import org.springframework.boot.actuate.env.EnvironmentEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.http.MediaType; +import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor; +import org.springframework.restdocs.operation.preprocess.OperationPreprocessor; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link EnvironmentEndpoint}. + * + * @author Andy Wilkinson + */ +@TestPropertySource(properties = "spring.config.location=classpath:/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/") +public class EnvironmentEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + private static final FieldDescriptor activeProfiles = fieldWithPath("activeProfiles") + .description("Names of the active profiles, if any."); + + private static final FieldDescriptor propertySources = fieldWithPath( + "propertySources").description("Property sources in order of precedence."); + + private static final FieldDescriptor propertySourceName = fieldWithPath( + "propertySources.[].name").description("Name of the property source."); + + @Test + public void env() throws Exception { + this.mockMvc.perform(get("/application/env")).andExpect(status().isOk()) + .andDo(document("env/all", + preprocessResponse(replacePattern( + Pattern.compile( + "org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/"), + ""), filterProperties()), + responseFields(activeProfiles, propertySources, + propertySourceName, + fieldWithPath("propertySources.[].properties") + .description( + "Properties in the property source keyed by property name."), + fieldWithPath("propertySources.[].properties.*.value") + .description("Value of the property."), + fieldWithPath("propertySources.[].properties.*.origin") + .description("Origin of the property, if any.") + .optional()))); + } + + @Test + public void singlePropertyFromEnv() throws Exception { + this.mockMvc.perform(get("/application/env/com.example.cache.max-size")) + .andExpect(status().isOk()) + .andDo(document("env/single", + preprocessResponse(replacePattern( + Pattern.compile( + "org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/"), + "")), + responseFields( + fieldWithPath("property").description( + "Property from the environment, if found.") + .optional(), + fieldWithPath("property.source").description( + "Name of the source of the property."), + fieldWithPath("property.value") + .description("Value of the property."), + activeProfiles, propertySources, propertySourceName, + fieldWithPath("propertySources.[].property").description( + "Property in the property source, if any.") + .optional(), + fieldWithPath("propertySources.[].property.value") + .description("Value of the property."), + fieldWithPath("propertySources.[].property.origin") + .description("Origin of the property, if any.") + .optional()))); + } + + private OperationPreprocessor filterProperties() { + return new ContentModifyingOperationPreprocessor(this::filterProperties); + } + + @SuppressWarnings("unchecked") + private byte[] filterProperties(byte[] content, MediaType mediaType) { + ObjectMapper objectMapper = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT); + try { + Map payload = objectMapper.readValue(content, Map.class); + List> propertySources = (List>) payload + .get("propertySources"); + for (Map propertySource : propertySources) { + Map properties = (Map) propertySource + .get("properties"); + Set filteredKeys = properties.keySet().stream() + .filter(this::retainKey).limit(3).collect(Collectors.toSet()); + properties.keySet().retainAll(filteredKeys); + } + return objectMapper.writeValueAsBytes(payload); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + private boolean retainKey(String key) { + return key.startsWith("java.") || key.equals("JAVA_HOME") + || key.startsWith("com.example"); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public EnvironmentEndpoint endpoint(ConfigurableEnvironment environment) { + return new EnvironmentEndpoint(new AbstractEnvironment() { + + @Override + protected void customizePropertySources( + MutablePropertySources propertySources) { + StreamSupport + .stream(environment.getPropertySources().spliterator(), false) + .filter(this::includedPropertySource) + .forEach(propertySources::addLast); + } + + private boolean includedPropertySource(PropertySource propertySource) { + return propertySource instanceof EnumerablePropertySource + && !"Inlined Test Properties" + .equals(propertySource.getName()); + } + + }); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java new file mode 100644 index 0000000000..d4ca7d25d6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java @@ -0,0 +1,109 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationState; +import org.flywaydb.core.api.MigrationType; +import org.junit.Test; + +import org.springframework.boot.actuate.flyway.FlywayEndpoint; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link FlywayEndpoint}. + * + * @author Andy Wilkinson + */ +@AutoConfigureTestDatabase +public class FlywayEndpointDocumentationTests extends AbstractEndpointDocumentationTests { + + @Test + public void flyway() throws Exception { + this.mockMvc.perform(get("/application/flyway")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("flyway", + responseFields(fieldWithPath("*.migrations").description( + "Migrations performed by the Flyway instance, keyed by" + + " bean name.")).andWithPrefix( + "*.migrations.[].", + migrationFieldDescriptors()))); + } + + private List migrationFieldDescriptors() { + return Arrays.asList( + fieldWithPath("checksum") + .description("Checksum of the migration, if any.").optional(), + fieldWithPath("description") + .description("Description of the migration, if any.").optional(), + fieldWithPath("executionTime") + .description( + "Execution time in milliseconds of an applied migration.") + .optional(), + fieldWithPath("installedBy") + .description("User that installed the applied migration, if any.") + .optional(), + fieldWithPath("installedOn").description( + "Timestamp of when the applied migration was installed, " + + "if any.") + .optional(), + fieldWithPath("installedRank").description( + "Rank of the applied migration, if any. Later migrations have " + + "higher ranks.") + .optional(), + fieldWithPath("script") + .description( + "Name of the script used to execute the migration, if any.") + .optional(), + fieldWithPath("state").description("State of the migration. (" + + describeEnumValues(MigrationState.class) + ")"), + fieldWithPath("type").description("Type of the migration. (" + + describeEnumValues(MigrationType.class) + ")"), + fieldWithPath("version").description( + "Version of the database after applying the migration, " + + "if any.") + .optional()); + } + + @Configuration + @Import({ BaseDocumentationConfiguration.class, EmbeddedDataSourceConfiguration.class, + FlywayAutoConfiguration.class }) + static class TestConfiguration { + + @Bean + public FlywayEndpoint endpoint(Map flyways) { + return new FlywayEndpoint(flyways); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java new file mode 100644 index 0000000000..3beaa5993c --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java @@ -0,0 +1,92 @@ +/* + * 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.endpoint.web.documentation; + +import java.io.File; +import java.util.Map; + +import javax.sql.DataSource; + +import org.junit.Test; + +import org.springframework.boot.actuate.health.CompositeHealthIndicator; +import org.springframework.boot.actuate.health.HealthEndpoint; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator; +import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link HealthEndpoint}. + * + * @author Andy Wilkinson + */ +public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentationTests { + + @Test + public void health() throws Exception { + this.mockMvc.perform(get("/application/health")).andExpect(status().isOk()) + .andDo(document("health", + responseFields( + fieldWithPath("status").description( + "Overall status of the application."), + fieldWithPath("details") + .description("Details of the health of the application."), + fieldWithPath("details.*.status").description( + "Status of a specific part of the application."), + subsectionWithPath("details.*.details").description( + "Details of the health of a specific part of the" + + " application.")))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class TestConfiguration { + + @Bean + public HealthEndpoint endpoint(Map healthIndicators) { + return new HealthEndpoint(new CompositeHealthIndicator( + new OrderedHealthAggregator(), healthIndicators)); + } + + @Bean + public DiskSpaceHealthIndicator diskSpaceHealthIndicator() { + return new DiskSpaceHealthIndicator(new File("."), 1024 * 1024 * 10); + } + + @Bean + public DataSourceHealthIndicator dataSourceHealthIndicator( + DataSource dataSource) { + return new DataSourceHealthIndicator(dataSource); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HeapDumpWebEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HeapDumpWebEndpointDocumentationTests.java new file mode 100644 index 0000000000..7f4fe292ca --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HeapDumpWebEndpointDocumentationTests.java @@ -0,0 +1,82 @@ +/* + * 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.endpoint.web.documentation; + +import java.io.FileWriter; +import java.util.Map; + +import org.junit.Test; + +import org.springframework.boot.actuate.management.HeapDumpWebEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.cli.CliDocumentation; +import org.springframework.restdocs.cli.CurlRequestSnippet; +import org.springframework.restdocs.operation.Operation; +import org.springframework.util.FileCopyUtils; + +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; + +/** + * Tests for generating documentation describing the {@link HeapDumpWebEndpoint}. + * + * @author Andy Wilkinson + */ +public class HeapDumpWebEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void heapDump() throws Exception { + this.mockMvc.perform(get("/application/heapdump")).andExpect(status().isOk()) + .andDo(document("heapdump", + new CurlRequestSnippet(CliDocumentation.multiLineFormat()) { + + @Override + protected Map createModel( + Operation operation) { + Map model = super.createModel(operation); + model.put("options", "-O"); + return model; + } + + })); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public HeapDumpWebEndpoint endpoint() { + return new HeapDumpWebEndpoint() { + + @Override + protected HeapDumper createHeapDumper() + throws HeapDumperUnavailableException { + return (file, live) -> FileCopyUtils.copy("<>", + new FileWriter(file)); + } + + }; + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/InfoEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/InfoEndpointDocumentationTests.java new file mode 100644 index 0000000000..5429a0236d --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/InfoEndpointDocumentationTests.java @@ -0,0 +1,117 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.List; +import java.util.Properties; + +import org.junit.Test; + +import org.springframework.boot.actuate.info.BuildInfoContributor; +import org.springframework.boot.actuate.info.GitInfoContributor; +import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.boot.actuate.info.InfoEndpoint; +import org.springframework.boot.info.BuildProperties; +import org.springframework.boot.info.GitProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.JsonFieldType; + +import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link InfoEndpoint}. + * + * @author Andy Wilkinson + */ +public class InfoEndpointDocumentationTests extends AbstractEndpointDocumentationTests { + + @Test + public void info() throws Exception { + this.mockMvc.perform(get("/application/info")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("info", + responseFields(beneathPath("git"), + fieldWithPath("branch") + .description("Name of the Git branch, if any."), + fieldWithPath("commit") + .description("Details of the Git commit, if any."), + fieldWithPath("commit.time") + .description("Timestamp of the commit, if any.") + .type(JsonFieldType.VARIES), + fieldWithPath("commit.id") + .description("ID of the commit, if any.")), + responseFields(beneathPath("build"), + fieldWithPath("artifact") + .description( + "Artifact ID of the application, if any.") + .optional(), + fieldWithPath("group") + .description( + "Group ID of the application, if any.") + .optional(), + fieldWithPath("name") + .description("Name of the application, if any.") + .type(JsonFieldType.STRING).optional(), + fieldWithPath("version") + .description( + "Version of the application, if any.") + .optional(), + fieldWithPath("time") + .description( + "Timestamp of when the application was built, if any.") + .type(JsonFieldType.VARIES).optional()))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public InfoEndpoint endpoint(List infoContributors) { + return new InfoEndpoint(infoContributors); + } + + @Bean + public GitInfoContributor gitInfoContributor() { + Properties properties = new Properties(); + properties.put("branch", "master"); + properties.put("commit.id", "df027cf1ec5aeba2d4fedd7b8c42b88dc5ce38e5"); + properties.put("commit.id.abbrev", "df027cf"); + properties.put("commit.time", Long.toString(System.currentTimeMillis())); + GitProperties gitProperties = new GitProperties(properties); + return new GitInfoContributor(gitProperties); + } + + @Bean + public BuildInfoContributor buildInfoContributor() { + Properties properties = new Properties(); + properties.put("group", "com.example"); + properties.put("artifact", "application"); + properties.put("version", "1.0.3"); + BuildProperties buildProperties = new BuildProperties(properties); + return new BuildInfoContributor(buildProperties); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java new file mode 100644 index 0000000000..a609643433 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java @@ -0,0 +1,95 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import liquibase.changelog.ChangeSet.ExecType; +import liquibase.integration.spring.SpringLiquibase; +import org.junit.Test; + +import org.springframework.boot.actuate.liquibase.LiquibaseEndpoint; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link LiquibaseEndpoint}. + * + * @author Andy Wilkinson + */ +public class LiquibaseEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void liquibase() throws Exception { + this.mockMvc.perform(get("/application/liquibase")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("liquibase", + responseFields(fieldWithPath("*.changeSets").description( + "Change sets made by the Liquibase beans, keyed by " + + "bean name.")).andWithPrefix("*.changeSets[].", + getChangeSetFieldDescriptors()))); + } + + private List getChangeSetFieldDescriptors() { + return Arrays.asList( + fieldWithPath("author").description("Author of the change set."), + fieldWithPath("changeLog") + .description("Change log that contains the change set."), + fieldWithPath("comments").description("Comments on the change set."), + fieldWithPath("contexts").description("Contexts of the change set."), + fieldWithPath("dateExecuted") + .description("Timestamp of when the change set was executed."), + fieldWithPath("deploymentId") + .description("ID of the deployment that ran the change set."), + fieldWithPath("description") + .description("Description of the change set."), + fieldWithPath("execType").description("Execution type of the change set (" + + describeEnumValues(ExecType.class) + ")."), + fieldWithPath("id").description("ID of the change set."), + fieldWithPath("labels") + .description("Labels associated with the change set."), + fieldWithPath("checksum").description("Checksum of the change set."), + fieldWithPath("orderExecuted") + .description("Order of the execution of the change set."), + fieldWithPath("tag").description("Tag associated with the change set.")); + } + + @Configuration + @Import({ BaseDocumentationConfiguration.class, EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class }) + static class TestConfiguration { + + @Bean + public LiquibaseEndpoint endpoint(Map liquibases) { + return new LiquibaseEndpoint(liquibases); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java new file mode 100644 index 0000000000..91a89bca4a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java @@ -0,0 +1,65 @@ +/* + * 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.endpoint.web.documentation; + +import org.junit.Test; + +import org.springframework.boot.actuate.logging.LogFileWebEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link LogFileWebEndpoint}. + * + * @author Andy Wilkinson + */ +@TestPropertySource(properties = "logging.file=src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log") +public class LogFileWebEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void logFile() throws Exception { + this.mockMvc.perform(get("/application/logfile")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("logfile/entire")); + } + + @Test + public void logFileRange() throws Exception { + this.mockMvc.perform(get("/application/logfile").header("Range", "bytes=0-1023")) + .andExpect(status().isPartialContent()) + .andDo(MockMvcRestDocumentation.document("logfile/range")); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public LogFileWebEndpoint endpoint(Environment environment) { + return new LogFileWebEndpoint(environment); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java new file mode 100644 index 0000000000..c3705190ac --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java @@ -0,0 +1,125 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; + +import org.junit.Test; + +import org.springframework.boot.actuate.logging.LoggersEndpoint; +import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.logging.LoggerConfiguration; +import org.springframework.boot.logging.LoggingSystem; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +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; + +/** + * Tests for generating documentation describing the {@link LoggersEndpoint}. + * + * @author Andy Wilkinson + */ +public class LoggersEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + private static final List levelFields = Arrays.asList( + fieldWithPath("configuredLevel") + .description("Configured level of the logger, if any.").optional(), + fieldWithPath("effectiveLevel") + .description("Effective level of the logger.")); + + @MockBean + private LoggingSystem loggingSystem; + + @Test + public void allLoggers() throws Exception { + given(this.loggingSystem.getSupportedLogLevels()) + .willReturn(EnumSet.allOf(LogLevel.class)); + given(this.loggingSystem.getLoggerConfigurations()).willReturn(Arrays.asList( + new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO), + new LoggerConfiguration("com.example", LogLevel.DEBUG, LogLevel.DEBUG))); + this.mockMvc.perform(get("/application/loggers")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("loggers/all", + responseFields( + fieldWithPath("levels").description( + "Levels support by the logging system."), + fieldWithPath("loggers").description("Loggers keyed by name.")) + .andWithPrefix("loggers.*.", levelFields))); + } + + @Test + public void logger() throws Exception { + given(this.loggingSystem.getLoggerConfiguration("com.example")).willReturn( + new LoggerConfiguration("com.example", LogLevel.INFO, LogLevel.INFO)); + this.mockMvc.perform(get("/application/loggers/com.example")) + .andExpect(status().isOk()).andDo(MockMvcRestDocumentation + .document("loggers/single", responseFields(levelFields))); + } + + @Test + public void setLogLevel() throws Exception { + this.mockMvc + .perform(post("/application/loggers/com.example") + .content("{\"configuredLevel\":\"debug\"}") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()) + .andDo(MockMvcRestDocumentation + .document("loggers/set", + requestFields(fieldWithPath("configuredLevel") + .description("Level for the logger. May be" + + " omitted to clear the level.") + .optional()))); + verify(this.loggingSystem).setLogLevel("com.example", LogLevel.DEBUG); + } + + @Test + public void clearLogLevel() throws Exception { + this.mockMvc + .perform(post("/application/loggers/com.example").content("{}") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()) + .andDo(MockMvcRestDocumentation.document("loggers/clear")); + verify(this.loggingSystem).setLogLevel("com.example", null); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public LoggersEndpoint endpoint(LoggingSystem loggingSystem) { + return new LoggersEndpoint(loggingSystem); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MetricsEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MetricsEndpointDocumentationTests.java new file mode 100644 index 0000000000..ce22ae8532 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MetricsEndpointDocumentationTests.java @@ -0,0 +1,98 @@ +/* + * 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.endpoint.web.documentation; + +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; + +import org.springframework.boot.actuate.metrics.MetricsEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link MetricsEndpoint}. + * + * @author Andy Wilkinson + */ +public class MetricsEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void metricNames() throws Exception { + this.mockMvc.perform(get("/application/metrics")).andExpect(status().isOk()) + .andDo(document("metrics/names", responseFields(fieldWithPath("names") + .description("Names of the known metrics.")))); + } + + @Test + public void metric() throws Exception { + this.mockMvc.perform(get("/application/metrics/jvm.memory.max")) + .andExpect(status().isOk()) + .andDo(document("metrics/metric", + responseFields( + fieldWithPath("name").description("Name of the metric"), + fieldWithPath("measurements") + .description("Measurements of the metric"), + fieldWithPath("measurements[].statistic") + .description("Statistic of the measurement. (" + + describeEnumValues(Statistic.class) + ")."), + fieldWithPath("measurements[].value") + .description("Value of the measurement."), + fieldWithPath("availableTags") + .description("Tags that are available for drill-down."), + fieldWithPath("availableTags[].tag") + .description("Name of the tag."), + fieldWithPath("availableTags[].values") + .description("Possible values of the tag.")))); + } + + @Test + public void metricWithTags() throws Exception { + this.mockMvc + .perform(get("/application/metrics/jvm.memory.max") + .param("tag", "area:nonheap").param("tag", "id:Code Cache")) + .andExpect(status().isOk()) + .andDo(document("metrics/metric-with-tags", + requestParameters(parameterWithName("tag").description( + "A tag to use for drill-down in the form `name:value`.")))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public MetricsEndpoint endpoint() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + new JvmMemoryMetrics().bindTo(registry); + return new MetricsEndpoint(registry); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java new file mode 100644 index 0000000000..c39dabf585 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/PrometheusScrapeEndpointDocumentationTests.java @@ -0,0 +1,71 @@ +/* + * 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.endpoint.web.documentation; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.prometheus.client.CollectorRegistry; +import org.junit.Test; + +import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +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; + +/** + * Tests for generating documentation describing the {@link PrometheusScrapeEndpoint}. + * + * @author Andy Wilkinson + */ +public class PrometheusScrapeEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void prometheus() throws Exception { + this.mockMvc.perform(get("/application/prometheus")).andExpect(status().isOk()) + .andDo(document("prometheus")); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public PrometheusScrapeEndpoint endpoint() { + CollectorRegistry collectorRegistry = new CollectorRegistry(true); + PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry( + new PrometheusConfig() { + + @Override + public String get(String key) { + return null; + } + + }, collectorRegistry, Clock.SYSTEM); + new JvmMemoryMetrics().bindTo(meterRegistry); + return new PrometheusScrapeEndpoint(collectorRegistry); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java new file mode 100644 index 0000000000..337441b769 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java @@ -0,0 +1,116 @@ +/* + * 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.endpoint.web.documentation; + +import java.util.Collection; +import java.util.regex.Pattern; + +import org.junit.Test; + +import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.config.ScheduledTaskHolder; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link ScheduledTasksEndpoint}. + * + * @author Andy Wilkinson + */ +public class ScheduledTasksEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void scheduledTasks() throws Exception { + this.mockMvc.perform(get("/application/scheduledtasks")) + .andExpect(status().isOk()) + .andDo(document("scheduled-tasks", + preprocessResponse(replacePattern( + Pattern.compile( + "org.*\\.ScheduledTasksEndpointDocumentationTests\\$" + + "TestConfiguration"), + "com.example.Processor")), + responseFields( + fieldWithPath("cron").description("Cron tasks, if any."), + targetFieldWithPrefix("cron.[]"), + fieldWithPath("cron.[].expression") + .description("Cron expression."), + fieldWithPath("fixedDelay") + .description("Fixed delay tasks, if any."), + targetFieldWithPrefix("fixedDelay.[]"), + initialDelayWithPrefix("fixedDelay.[]."), + fieldWithPath("fixedDelay.[].interval").description( + "Interval, in milliseconds, between the end of the last" + + " execution and the start of the next."), + fieldWithPath("fixedRate") + .description("Fixed rate tasks, if any."), + targetFieldWithPrefix("fixedRate.[]."), + fieldWithPath("fixedRate.[].interval").description( + "Interval, in milliseconds, between the start of each execution."), + initialDelayWithPrefix("fixedRate.[].")))); + } + + private FieldDescriptor targetFieldWithPrefix(String prefix) { + return fieldWithPath(prefix + "runnable.target") + .description("Target that will be executed."); + } + + private FieldDescriptor initialDelayWithPrefix(String prefix) { + return fieldWithPath(prefix + "initialDelay") + .description("Delay, in milliseconds, before first execution."); + } + + @Configuration + @EnableScheduling + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public ScheduledTasksEndpoint endpoint(Collection holders) { + return new ScheduledTasksEndpoint(holders); + } + + @Scheduled(cron = "0 0 0/3 1/1 * ?") + public void processOrders() { + + } + + @Scheduled(fixedDelay = 5000, initialDelay = 5000) + public void purge() { + + } + + @Scheduled(fixedRate = 3000, initialDelay = 10000) + public void retrieveIssues() { + + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java new file mode 100644 index 0000000000..2276c59952 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java @@ -0,0 +1,150 @@ +/* + * 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.endpoint.web.documentation; + +import java.time.Instant; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.junit.Test; + +import org.springframework.boot.actuate.context.ShutdownEndpoint; +import org.springframework.boot.actuate.session.SessionsEndpoint; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.MapSession; +import org.springframework.session.Session; +import org.springframework.test.context.TestPropertySource; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link ShutdownEndpoint}. + * + * @author Andy Wilkinson + */ +@TestPropertySource(properties = "spring.jackson.serialization.write-dates-as-timestamps=false") +public class SessionsEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + private static final Session sessionOne = createSession( + Instant.now().minusSeconds(60 * 60 * 12), Instant.now().minusSeconds(45)); + + private static final Session sessionTwo = createSession( + "4db5efcc-99cb-4d05-a52c-b49acfbb7ea9", + Instant.now().minusSeconds(60 * 60 * 5), Instant.now().minusSeconds(37)); + + private static final Session sessionThree = createSession( + Instant.now().minusSeconds(60 * 60 * 2), Instant.now().minusSeconds(12)); + + private static final List sessionFields = Arrays + .asList(fieldWithPath("id").description("ID of the session."), + fieldWithPath("attributeNames").description( + "Names of the attributes stored in the session."), + fieldWithPath("creationTime") + .description("Timestamp of when the session was created."), + fieldWithPath("lastAccessedTime") + .description("Timestamp of when the session was last accessed."), + fieldWithPath("maxInactiveInterval") + .description("Maximum permitted period of inactivity, in seconds, " + + "before the session will expire."), + fieldWithPath("expired").description("Whether the session has expired.")); + + @MockBean + private FindByIndexNameSessionRepository sessionRepository; + + @Test + public void sessionsForUsername() throws Exception { + Map sessions = new HashMap<>(); + sessions.put(sessionOne.getId(), sessionOne); + sessions.put(sessionTwo.getId(), sessionTwo); + sessions.put(sessionThree.getId(), sessionThree); + given(this.sessionRepository.findByIndexNameAndIndexValue( + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "alice")) + .willReturn(sessions); + this.mockMvc.perform(get("/application/sessions").param("username", "alice")) + .andExpect(status().isOk()) + .andDo(document("sessions/username", + responseFields(fieldWithPath("sessions") + .description("Sessions for the given username.")) + .andWithPrefix("sessions.[].", sessionFields), + requestParameters(parameterWithName("username") + .description("Name of the user.")))); + } + + @Test + public void sessionWithId() throws Exception { + Map sessions = new HashMap<>(); + sessions.put(sessionOne.getId(), sessionOne); + sessions.put(sessionTwo.getId(), sessionTwo); + sessions.put(sessionThree.getId(), sessionThree); + given(this.sessionRepository.findById(sessionTwo.getId())).willReturn(sessionTwo); + this.mockMvc.perform(get("/application/sessions/{id}", sessionTwo.getId())) + .andExpect(status().isOk()) + .andDo(document("sessions/id", responseFields(sessionFields))); + } + + @Test + public void deleteASession() throws Exception { + this.mockMvc.perform(delete("/application/sessions/{id}", sessionTwo.getId())) + .andExpect(status().isNoContent()).andDo(document("sessions/delete")); + verify(this.sessionRepository).deleteById(sessionTwo.getId()); + } + + private static MapSession createSession(Instant creationTime, + Instant lastAccessedTime) { + return createSession(UUID.randomUUID().toString(), creationTime, + lastAccessedTime); + } + + private static MapSession createSession(String id, Instant creationTime, + Instant lastAccessedTime) { + MapSession session = new MapSession(id); + session.setCreationTime(creationTime); + session.setLastAccessedTime(lastAccessedTime); + return session; + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public SessionsEndpoint endpoint( + FindByIndexNameSessionRepository sessionRepository) { + return new SessionsEndpoint(sessionRepository); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java new file mode 100644 index 0000000000..c17ab80828 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java @@ -0,0 +1,65 @@ +/* + * 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.endpoint.web.documentation; + +import org.junit.Test; + +import org.springframework.boot.actuate.context.ShutdownEndpoint; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link ShutdownEndpoint}. + * + * @author Andy Wilkinson + */ +@TestPropertySource(properties = "endpoints.shutdown.enabled=true") +public class ShutdownEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void shutdown() throws Exception { + this.mockMvc.perform(post("/application/shutdown")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("shutdown", + responseFields(fieldWithPath("message").description( + "Message describing the result of the request.")))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public ShutdownEndpoint endpoint(Environment environment) { + ShutdownEndpoint endpoint = new ShutdownEndpoint(); + endpoint.setApplicationContext(new AnnotationConfigApplicationContext()); + return endpoint; + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/StatusEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/StatusEndpointDocumentationTests.java new file mode 100644 index 0000000000..3ce0fdf278 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/StatusEndpointDocumentationTests.java @@ -0,0 +1,82 @@ +/* + * 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.endpoint.web.documentation; + +import java.io.File; +import java.util.Map; + +import javax.sql.DataSource; + +import org.junit.Test; + +import org.springframework.boot.actuate.health.CompositeHealthIndicator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.health.StatusEndpoint; +import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator; +import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing the {@link StatusEndpoint}. + * + * @author Andy Wilkinson + */ +public class StatusEndpointDocumentationTests extends AbstractEndpointDocumentationTests { + + @Test + public void health() throws Exception { + this.mockMvc.perform(get("/application/status")).andExpect(status().isOk()) + .andDo(document("status", responseFields(fieldWithPath("status") + .description("Overall status of the application.")))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class TestConfiguration { + + @Bean + public StatusEndpoint endpoint(Map healthIndicators) { + return new StatusEndpoint(new CompositeHealthIndicator( + new OrderedHealthAggregator(), healthIndicators)); + } + + @Bean + public DiskSpaceHealthIndicator diskSpaceHealthIndicator() { + return new DiskSpaceHealthIndicator(new File("."), 1024 * 1024 * 10); + } + + @Bean + public DataSourceHealthIndicator dataSourceHealthIndicator( + DataSource dataSource) { + return new DataSourceHealthIndicator(dataSource); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ThreadDumpEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ThreadDumpEndpointDocumentationTests.java new file mode 100644 index 0000000000..7b2933a67a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ThreadDumpEndpointDocumentationTests.java @@ -0,0 +1,176 @@ +/* + * 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.endpoint.web.documentation; + +import org.junit.Test; + +import org.springframework.boot.actuate.management.ThreadDumpEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.JsonFieldType; + +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for generating documentation describing {@link ThreadDumpEndpoint}. + * + * @author Andy Wilkinson + */ +public class ThreadDumpEndpointDocumentationTests + extends AbstractEndpointDocumentationTests { + + @Test + public void threadDump() throws Exception { + this.mockMvc.perform(get("/application/threaddump")).andExpect(status().isOk()) + .andDo(MockMvcRestDocumentation.document("threaddump", + preprocessResponse(limit("threads")), + responseFields( + fieldWithPath("threads").description("JVM's threads."), + fieldWithPath("threads.[].threadName") + .description("Name of the thread."), + fieldWithPath("threads.[].threadId") + .description("ID of the thread."), + fieldWithPath("threads.[].blockedTime").description( + "Time in milliseconds that the thread has spent " + + "blocked. -1 if thread contention " + + "monitoring is disabled."), + fieldWithPath("threads.[].blockedCount").description( + "Total number of times that the thread has been " + + "blocked."), + fieldWithPath("threads.[].waitedTime").description( + "Time in milliseconds that the thread has spent " + + "waiting. -1 if thread contention " + + "monitoring is disabled"), + fieldWithPath("threads.[].waitedCount").description( + "Total number of times that the thread has waited" + + " for notification."), + fieldWithPath("threads.[].lockName") + .description( + "Description of the object on which the " + + "thread is blocked, if any.") + .optional(), + fieldWithPath("threads.[].lockOwnerId").description( + "ID of the thread that owns the object on which " + + "the thread is blocked. `-1` if the " + + "thread is not blocked."), + fieldWithPath("threads.[].lockOwnerName") + .description( + "Name of the thread that owns the object " + + "on which the thread is blocked.") + .optional(), + fieldWithPath("threads.[].inNative").description( + "Whether the thread is executing native code."), + fieldWithPath("threads.[].suspended") + .description("Whether the thread is suspended."), + fieldWithPath("threads.[].threadState") + .description("State of the thread (" + + describeEnumValues(Thread.State.class) + + ")."), + fieldWithPath("threads.[].stackTrace") + .description("Stack trace of the thread."), + fieldWithPath("threads.[].stackTrace.[].methodName") + .description("Name of the method."), + fieldWithPath("threads.[].stackTrace.[].fileName") + .description( + "Name of the source file that contains " + + "the execution point identified " + + "by this entry, if any.") + .optional().type(JsonFieldType.STRING), + fieldWithPath("threads.[].stackTrace.[].lineNumber") + .description("Line number of the execution point " + + "identified by this entry. Negative" + + " if unknown."), + fieldWithPath("threads.[].stackTrace.[].className") + .description( + "Name of the class that contains the " + + "execution point identified " + + "by this entry."), + fieldWithPath("threads.[].stackTrace.[].nativeMethod") + .description( + "Whether the execution point is a native " + + "method."), + fieldWithPath("threads.[].lockedMonitors").description( + "Monitors locked by this thread, if any"), + fieldWithPath("threads.[].lockedMonitors.[].className") + .description("Class name of the lock object.") + .optional().type(JsonFieldType.STRING), + fieldWithPath( + "threads.[].lockedMonitors.[].identityHashCode") + .description( + "Identity hash code of the lock " + + "object.") + .optional().type(JsonFieldType.NUMBER), + fieldWithPath( + "threads.[].lockedMonitors.[].lockedStackDepth") + .description( + "Stack depth where the monitor " + + "was locked.") + .optional().type(JsonFieldType.NUMBER), + subsectionWithPath( + "threads.[].lockedMonitors.[].lockedStackFrame") + .description( + "Stack frame that locked the " + + "monitor.") + .optional().type(JsonFieldType.OBJECT), + fieldWithPath("threads.[].lockedSynchronizers") + .description( + "Synchronizers locked by this thread."), + fieldWithPath( + "threads.[].lockedSynchronizers.[].className") + .description("Class name of the locked " + + "synchronizer.") + .optional().type(JsonFieldType.STRING), + fieldWithPath( + "threads.[].lockedSynchronizers.[].identifyHashCode") + .description( + "Identity hash code of the locked " + + "synchronizer.") + .optional().type(JsonFieldType.NUMBER), + fieldWithPath("threads.[].lockInfo").description( + "Object for which the thread is blocked " + + "waiting.") + .optional(), + fieldWithPath("threads.[].lockInfo.className") + .description( + "Fully qualified class name of the lock" + + " object.") + .optional(), + fieldWithPath("threads.[].lockInfo.identityHashCode") + .description( + "Identity hash code of the lock object.") + .optional()))); + } + + @Configuration + @Import(BaseDocumentationConfiguration.class) + static class TestConfiguration { + + @Bean + public ThreadDumpEndpoint endpoint() { + return new ThreadDumpEndpoint(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/application.properties b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/application.properties new file mode 100644 index 0000000000..2574e2ee85 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/application.properties @@ -0,0 +1 @@ +com.example.cache.max-size: 1000 diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log new file mode 100644 index 0000000000..d8c7e36812 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log @@ -0,0 +1,31 @@ + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: + +2017-08-08 17:12:30.910 INFO 19866 --- [ main] s.f.SampleWebFreeMarkerApplication : Starting SampleWebFreeMarkerApplication on host.local with PID 19866 +2017-08-08 17:12:30.913 INFO 19866 --- [ main] s.f.SampleWebFreeMarkerApplication : No active profile set, falling back to default profiles: default +2017-08-08 17:12:30.952 INFO 19866 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@76b10754: startup date [Tue Aug 08 17:12:30 BST 2017]; root of context hierarchy +2017-08-08 17:12:31.878 INFO 19866 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2017-08-08 17:12:31.889 INFO 19866 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2017-08-08 17:12:31.890 INFO 19866 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.16 +2017-08-08 17:12:31.978 INFO 19866 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2017-08-08 17:12:31.978 INFO 19866 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1028 ms +2017-08-08 17:12:32.080 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/] +2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] +2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] +2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*] +2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*] +2017-08-08 17:12:32.349 INFO 19866 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@76b10754: startup date [Tue Aug 08 17:12:30 BST 2017]; root of context hierarchy +2017-08-08 17:12:32.420 INFO 19866 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest) +2017-08-08 17:12:32.421 INFO 19866 --- [ 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.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) +2017-08-08 17:12:32.444 INFO 19866 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] +2017-08-08 17:12:32.444 INFO 19866 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] +2017-08-08 17:12:32.471 INFO 19866 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] +2017-08-08 17:12:32.600 INFO 19866 --- [ main] o.s.w.s.v.f.FreeMarkerConfigurer : ClassTemplateLoader for Spring macros added to FreeMarker configuration +2017-08-08 17:12:32.681 INFO 19866 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup +2017-08-08 17:12:32.744 INFO 19866 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) +2017-08-08 17:12:32.750 INFO 19866 --- [ main] s.f.SampleWebFreeMarkerApplication : Started SampleWebFreeMarkerApplication in 2.172 seconds (JVM running for 2.479) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java index 830099e5be..1f3d644012 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java @@ -117,7 +117,7 @@ public class LiquibaseEndpoint { private final String comments; - private final ContextExpression contextExpression; + private final Set contexts; private final Date dateExecuted; @@ -141,8 +141,7 @@ public class LiquibaseEndpoint { this.author = ranChangeSet.getAuthor(); this.changeLog = ranChangeSet.getChangeLog(); this.comments = ranChangeSet.getComments(); - this.contextExpression = new ContextExpression( - ranChangeSet.getContextExpression().getContexts()); + this.contexts = ranChangeSet.getContextExpression().getContexts(); this.dateExecuted = ranChangeSet.getDateExecuted(); this.deploymentId = ranChangeSet.getDeploymentId(); this.description = ranChangeSet.getDescription(); @@ -167,8 +166,8 @@ public class LiquibaseEndpoint { return this.comments; } - public ContextExpression getContextExpression() { - return this.contextExpression; + public Set getContexts() { + return this.contexts; } public Date getDateExecuted() { diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml index 4e9688d82c..f2bc79dd26 100644 --- a/spring-boot-project/spring-boot-dependencies/pom.xml +++ b/spring-boot-project/spring-boot-dependencies/pom.xml @@ -151,7 +151,7 @@ 2.1.0.RC1 2.3.2.RELEASE 1.2.0.RELEASE - 2.0.0.RC1 + 2.0.0.BUILD-SNAPSHOT 1.2.1.RELEASE 5.0.0.RC1 2.0.0.RC1 diff --git a/spring-boot-project/spring-boot-docs/pom.xml b/spring-boot-project/spring-boot-docs/pom.xml index 820e4f60f4..b081f2d8f2 100644 --- a/spring-boot-project/spring-boot-docs/pom.xml +++ b/spring-boot-project/spring-boot-docs/pom.xml @@ -980,6 +980,15 @@ ${project.build.directory}/contents/gradle-plugin META-INF/** + + org.springframework.boot + spring-boot-actuator-autoconfigure + ${revision} + docs + zip + ${project.build.directory}/contents/actuator-api + META-INF/** + diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc index 9ac2992fa7..35c176f218 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc @@ -35,6 +35,7 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; :dc-spring-boot-test-autoconfigure: {dc-root}/org/springframework/boot/test/autoconfigure :dependency-management-plugin: https://github.com/spring-gradle-plugins/dependency-management-plugin :dependency-management-plugin-documentation: {dependency-management-plugin}/blob/master/README.md +:spring-boot-actuator-api: http://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/actuator-api/ :spring-boot-maven-plugin-site: http://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/maven-plugin/ :spring-boot-gradle-plugin: http://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/gradle-plugin/ :spring-reference: http://docs.spring.io/spring/docs/{spring-docs-version}/spring-framework-reference/ diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 6d8722f71b..0ad0e503d9 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -142,6 +142,12 @@ content. |=== +To learn more about the Actuator's endpoints and their request and response formats, +please refer to the separate API documentation that is available in the following formats: + +* {spring-boot-actuator-api}/html[HTML] +* {spring-boot-actuator-api}/pdf/spring-boot-actuator-web-api.pdf[PDF] + [[production-ready-endpoints-exposing-endpoints]] diff --git a/spring-boot-project/spring-boot-parent/src/checkstyle/checkstyle.xml b/spring-boot-project/spring-boot-parent/src/checkstyle/checkstyle.xml index fc343f4c16..b38767c901 100644 --- a/spring-boot-project/spring-boot-parent/src/checkstyle/checkstyle.xml +++ b/spring-boot-project/spring-boot-parent/src/checkstyle/checkstyle.xml @@ -70,7 +70,7 @@ + value="io.restassured.RestAssured.*, org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.ArgumentMatchers.*, org.mockito.Matchers.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.payload.PayloadDocumentation.*, org.springframework.restdocs.request.RequestDocumentation.*, org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.*, org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.ExpectedCount.*, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*" />