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.web.mappings.servlet; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Map.Entry; 026import java.util.stream.Collectors; 027import java.util.stream.Stream; 028 029import javax.servlet.Servlet; 030 031import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription; 032import org.springframework.boot.actuate.web.mappings.MappingDescriptionProvider; 033import org.springframework.boot.web.servlet.ServletRegistrationBean; 034import org.springframework.context.ApplicationContext; 035import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping; 036import org.springframework.util.ClassUtils; 037import org.springframework.web.context.WebApplicationContext; 038import org.springframework.web.method.HandlerMethod; 039import org.springframework.web.servlet.DispatcherServlet; 040import org.springframework.web.servlet.HandlerMapping; 041import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping; 042import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 043import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; 044 045/** 046 * A {@link MappingDescriptionProvider} that introspects the {@link HandlerMapping 047 * HandlerMappings} that are known to one or more {@link DispatcherServlet 048 * DispatcherServlets}. 049 * 050 * @author Andy Wilkinson 051 * @author Stephane Nicoll 052 * @since 2.0.0 053 */ 054public class DispatcherServletsMappingDescriptionProvider 055 implements MappingDescriptionProvider { 056 057 private static final List<HandlerMappingDescriptionProvider<? extends HandlerMapping>> descriptionProviders; 058 059 static { 060 List<HandlerMappingDescriptionProvider<? extends HandlerMapping>> providers = new ArrayList<>(); 061 providers.add(new RequestMappingInfoHandlerMappingDescriptionProvider()); 062 providers.add(new UrlHandlerMappingDescriptionProvider()); 063 if (ClassUtils.isPresent( 064 "org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping", 065 null)) { 066 providers.add(new DelegatingHandlerMappingDescriptionProvider( 067 new ArrayList<>(providers))); 068 } 069 descriptionProviders = Collections.unmodifiableList(providers); 070 } 071 072 @Override 073 public String getMappingName() { 074 return "dispatcherServlets"; 075 } 076 077 @Override 078 public Map<String, List<DispatcherServletMappingDescription>> describeMappings( 079 ApplicationContext context) { 080 if (context instanceof WebApplicationContext) { 081 return describeMappings((WebApplicationContext) context); 082 } 083 return Collections.emptyMap(); 084 } 085 086 private Map<String, List<DispatcherServletMappingDescription>> describeMappings( 087 WebApplicationContext context) { 088 Map<String, List<DispatcherServletMappingDescription>> mappings = new HashMap<>(); 089 determineDispatcherServlets(context).forEach((name, dispatcherServlet) -> mappings 090 .put(name, describeMappings(new DispatcherServletHandlerMappings(name, 091 dispatcherServlet, context)))); 092 return mappings; 093 } 094 095 private Map<String, DispatcherServlet> determineDispatcherServlets( 096 WebApplicationContext context) { 097 Map<String, DispatcherServlet> dispatcherServlets = new LinkedHashMap<>(); 098 context.getBeansOfType(ServletRegistrationBean.class).values() 099 .forEach((registration) -> { 100 Servlet servlet = registration.getServlet(); 101 if (servlet instanceof DispatcherServlet 102 && !dispatcherServlets.containsValue(servlet)) { 103 dispatcherServlets.put(registration.getServletName(), 104 (DispatcherServlet) servlet); 105 } 106 }); 107 context.getBeansOfType(DispatcherServlet.class) 108 .forEach((name, dispatcherServlet) -> { 109 if (!dispatcherServlets.containsValue(dispatcherServlet)) { 110 dispatcherServlets.put(name, dispatcherServlet); 111 } 112 }); 113 return dispatcherServlets; 114 } 115 116 private List<DispatcherServletMappingDescription> describeMappings( 117 DispatcherServletHandlerMappings mappings) { 118 return mappings.getHandlerMappings().stream().flatMap(this::describe) 119 .collect(Collectors.toList()); 120 } 121 122 private <T extends HandlerMapping> Stream<DispatcherServletMappingDescription> describe( 123 T handlerMapping) { 124 return describe(handlerMapping, descriptionProviders).stream(); 125 } 126 127 @SuppressWarnings("unchecked") 128 private static <T extends HandlerMapping> List<DispatcherServletMappingDescription> describe( 129 T handlerMapping, 130 List<HandlerMappingDescriptionProvider<?>> descriptionProviders) { 131 for (HandlerMappingDescriptionProvider<?> descriptionProvider : descriptionProviders) { 132 if (descriptionProvider.getMappingClass().isInstance(handlerMapping)) { 133 return ((HandlerMappingDescriptionProvider<T>) descriptionProvider) 134 .describe(handlerMapping); 135 } 136 } 137 return Collections.emptyList(); 138 } 139 140 private interface HandlerMappingDescriptionProvider<T extends HandlerMapping> { 141 142 Class<T> getMappingClass(); 143 144 List<DispatcherServletMappingDescription> describe(T handlerMapping); 145 146 } 147 148 private static final class RequestMappingInfoHandlerMappingDescriptionProvider 149 implements 150 HandlerMappingDescriptionProvider<RequestMappingInfoHandlerMapping> { 151 152 @Override 153 public Class<RequestMappingInfoHandlerMapping> getMappingClass() { 154 return RequestMappingInfoHandlerMapping.class; 155 } 156 157 @Override 158 public List<DispatcherServletMappingDescription> describe( 159 RequestMappingInfoHandlerMapping handlerMapping) { 160 Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping 161 .getHandlerMethods(); 162 return handlerMethods.entrySet().stream().map(this::describe) 163 .collect(Collectors.toList()); 164 } 165 166 private DispatcherServletMappingDescription describe( 167 Entry<RequestMappingInfo, HandlerMethod> mapping) { 168 DispatcherServletMappingDetails mappingDetails = new DispatcherServletMappingDetails(); 169 mappingDetails 170 .setHandlerMethod(new HandlerMethodDescription(mapping.getValue())); 171 mappingDetails.setRequestMappingConditions( 172 new RequestMappingConditionsDescription(mapping.getKey())); 173 return new DispatcherServletMappingDescription(mapping.getKey().toString(), 174 mapping.getValue().toString(), mappingDetails); 175 } 176 177 } 178 179 private static final class UrlHandlerMappingDescriptionProvider 180 implements HandlerMappingDescriptionProvider<AbstractUrlHandlerMapping> { 181 182 @Override 183 public Class<AbstractUrlHandlerMapping> getMappingClass() { 184 return AbstractUrlHandlerMapping.class; 185 } 186 187 @Override 188 public List<DispatcherServletMappingDescription> describe( 189 AbstractUrlHandlerMapping handlerMapping) { 190 return handlerMapping.getHandlerMap().entrySet().stream().map(this::describe) 191 .collect(Collectors.toList()); 192 } 193 194 private DispatcherServletMappingDescription describe( 195 Entry<String, Object> mapping) { 196 return new DispatcherServletMappingDescription(mapping.getKey(), 197 mapping.getValue().toString(), null); 198 } 199 200 } 201 202 private static final class DelegatingHandlerMappingDescriptionProvider 203 implements HandlerMappingDescriptionProvider<DelegatingHandlerMapping> { 204 205 private final List<HandlerMappingDescriptionProvider<?>> descriptionProviders; 206 207 private DelegatingHandlerMappingDescriptionProvider( 208 List<HandlerMappingDescriptionProvider<?>> descriptionProviders) { 209 this.descriptionProviders = descriptionProviders; 210 } 211 212 @Override 213 public Class<DelegatingHandlerMapping> getMappingClass() { 214 return DelegatingHandlerMapping.class; 215 } 216 217 @Override 218 public List<DispatcherServletMappingDescription> describe( 219 DelegatingHandlerMapping handlerMapping) { 220 List<DispatcherServletMappingDescription> descriptions = new ArrayList<>(); 221 for (HandlerMapping delegate : handlerMapping.getDelegates()) { 222 descriptions.addAll(DispatcherServletsMappingDescriptionProvider 223 .describe(delegate, this.descriptionProviders)); 224 } 225 return descriptions; 226 } 227 228 } 229 230}