/* * Copyright 2014 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 de.codecentric.boot.admin.zuul; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator; import org.springframework.cloud.netflix.zuul.filters.Route; import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.registry.ApplicationRegistry; /** * RouteLocator to register all applications' routes to zuul * * @author Johannes Edmeier */ public class ApplicationRouteLocator implements RefreshableRouteLocator { private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRouteLocator.class); private ApplicationRegistry registry; private PathMatcher pathMatcher = new AntPathMatcher(); private AtomicReference<List<ApplicationRoute>> routes = new AtomicReference<>(); private String prefix; private String servletPath; private String[] endpoints = {}; public ApplicationRouteLocator(String servletPath, ApplicationRegistry registry, String prefix) { this.servletPath = servletPath; this.registry = registry; this.prefix = prefix; } protected List<ApplicationRoute> locateRoutes() { Collection<Application> applications = registry.getApplications(); List<ApplicationRoute> locateRoutes = new ArrayList<>( applications.size() * (endpoints.length + 1)); for (Application application : applications) { addRoute(locateRoutes, application, "health", application.getHealthUrl()); if (!StringUtils.isEmpty(application.getManagementUrl())) { for (String endpoint : endpoints) { addRoute(locateRoutes, application, endpoint, application.getManagementUrl() + "/" + endpoint); } } } return locateRoutes; } private void addRoute(List<ApplicationRoute> locateRoutes, Application application, String endpoint, String targetUrl) { ApplicationRoute route = new ApplicationRoute(application, application.getId() + "-" + endpoint, "/**", targetUrl, prefix + application.getId() + "/" + endpoint); locateRoutes.add(route); } @Override public ApplicationRoute getMatchingRoute(final String path) { LOGGER.debug("Finding route for path: {}", path); if (this.routes.get() == null) { this.routes.set(locateRoutes()); } LOGGER.debug("servletPath= {}", this.servletPath); String adjustedPath = stripServletPath(path); for (ApplicationRoute route : this.routes.get()) { String pattern = route.getFullPath(); LOGGER.debug("Matching pattern: {}", pattern); if (this.pathMatcher.match(pattern, adjustedPath)) { LOGGER.debug("route matched= {}", route); return adjustPathRoute(route, adjustedPath); } } return null; } private ApplicationRoute adjustPathRoute(ApplicationRoute route, String path) { String adjustedPath; if (path.startsWith(route.getPrefix())) { adjustedPath = path.substring(route.getPrefix().length()); } else { adjustedPath = path; } return new ApplicationRoute(route.getApplication(), route.getId(), adjustedPath, route.getLocation(), route.getPrefix()); } @Override public List<Route> getRoutes() { if (this.routes.get() == null) { this.routes.set(locateRoutes()); } return new ArrayList<Route>(routes.get()); } @Override public void refresh() { routes.set(locateRoutes()); } @Override public Collection<String> getIgnoredPaths() { return Collections.emptyList(); } public void setEndpoints(String[] proxyEndpoints) { for (String endpoint : proxyEndpoints) { Assert.hasText(endpoint, "The proxyEndpoints must not contain null"); Assert.isTrue(!endpoint.startsWith("/"), "All proxyEndpoints must not start with '/'"); } this.endpoints = proxyEndpoints.clone(); } private String stripServletPath(final String path) { String adjustedPath = path; if (StringUtils.hasText(servletPath) && !"/".equals(servletPath)) { adjustedPath = path.substring(this.servletPath.length()); } LOGGER.debug("adjustedPath={}", path); return adjustedPath; } public static class ApplicationRoute extends Route { private final Application application; public ApplicationRoute(Application application, String id, String path, String location, String prefix) { super(id, path, location, prefix, false, null); this.application = application; } public Application getApplication() { return application; } } }