001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.actuate.endpoint.web;
018
019import java.util.Collection;
020import java.util.LinkedHashMap;
021import java.util.Map;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
027
028/**
029 * A resolver for {@link Link links} to web endpoints.
030 *
031 * @author Andy Wilkinson
032 * @since 2.0.0
033 */
034public class EndpointLinksResolver {
035
036        private static final Log logger = LogFactory.getLog(EndpointLinksResolver.class);
037
038        private final Collection<? extends ExposableEndpoint<?>> endpoints;
039
040        /**
041         * Creates a new {@code EndpointLinksResolver} that will resolve links to the given
042         * {@code endpoints}.
043         * @param endpoints the endpoints
044         */
045        public EndpointLinksResolver(Collection<? extends ExposableEndpoint<?>> endpoints) {
046                this.endpoints = endpoints;
047        }
048
049        /**
050         * Creates a new {@code EndpointLinksResolver} that will resolve links to the given
051         * {@code endpoints} that are exposed beneath the given {@code basePath}.
052         * @param endpoints the endpoints
053         * @param basePath the basePath
054         */
055        public EndpointLinksResolver(Collection<? extends ExposableEndpoint<?>> endpoints,
056                        String basePath) {
057                this.endpoints = endpoints;
058                if (logger.isInfoEnabled()) {
059                        logger.info("Exposing " + endpoints.size()
060                                        + " endpoint(s) beneath base path '" + basePath + "'");
061                }
062        }
063
064        /**
065         * Resolves links to the known endpoints based on a request with the given
066         * {@code requestUrl}.
067         * @param requestUrl the url of the request for the endpoint links
068         * @return the links
069         */
070        public Map<String, Link> resolveLinks(String requestUrl) {
071                String normalizedUrl = normalizeRequestUrl(requestUrl);
072                Map<String, Link> links = new LinkedHashMap<>();
073                links.put("self", new Link(normalizedUrl));
074                for (ExposableEndpoint<?> endpoint : this.endpoints) {
075                        if (endpoint instanceof ExposableWebEndpoint) {
076                                collectLinks(links, (ExposableWebEndpoint) endpoint, normalizedUrl);
077                        }
078                        else if (endpoint instanceof PathMappedEndpoint) {
079                                String rootPath = ((PathMappedEndpoint) endpoint).getRootPath();
080                                Link link = createLink(normalizedUrl, rootPath);
081                                links.put(endpoint.getEndpointId().toLowerCaseString(), link);
082                        }
083                }
084                return links;
085        }
086
087        private String normalizeRequestUrl(String requestUrl) {
088                if (requestUrl.endsWith("/")) {
089                        return requestUrl.substring(0, requestUrl.length() - 1);
090                }
091                return requestUrl;
092        }
093
094        private void collectLinks(Map<String, Link> links, ExposableWebEndpoint endpoint,
095                        String normalizedUrl) {
096                for (WebOperation operation : endpoint.getOperations()) {
097                        links.put(operation.getId(), createLink(normalizedUrl, operation));
098                }
099        }
100
101        private Link createLink(String requestUrl, WebOperation operation) {
102                return createLink(requestUrl, operation.getRequestPredicate().getPath());
103        }
104
105        private Link createLink(String requestUrl, String path) {
106                return new Link(requestUrl + (path.startsWith("/") ? path : "/" + path));
107        }
108
109}