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.autoconfigure.endpoint; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.List; 024import java.util.Locale; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.springframework.boot.actuate.endpoint.EndpointFilter; 029import org.springframework.boot.actuate.endpoint.EndpointId; 030import org.springframework.boot.actuate.endpoint.ExposableEndpoint; 031import org.springframework.boot.context.properties.bind.Bindable; 032import org.springframework.boot.context.properties.bind.Binder; 033import org.springframework.core.env.Environment; 034import org.springframework.util.Assert; 035 036/** 037 * {@link EndpointFilter} that will filter endpoints based on {@code include} and 038 * {@code exclude} properties. 039 * 040 * @param <E> the endpoint type 041 * @author Phillip Webb 042 * @since 2.0.0 043 */ 044public class ExposeExcludePropertyEndpointFilter<E extends ExposableEndpoint<?>> 045 implements EndpointFilter<E> { 046 047 private final Class<E> endpointType; 048 049 private final Set<String> include; 050 051 private final Set<String> exclude; 052 053 private final Set<String> exposeDefaults; 054 055 public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, 056 Environment environment, String prefix, String... exposeDefaults) { 057 Assert.notNull(endpointType, "EndpointType must not be null"); 058 Assert.notNull(environment, "Environment must not be null"); 059 Assert.hasText(prefix, "Prefix must not be empty"); 060 Binder binder = Binder.get(environment); 061 this.endpointType = endpointType; 062 this.include = bind(binder, prefix + ".include"); 063 this.exclude = bind(binder, prefix + ".exclude"); 064 this.exposeDefaults = asSet(Arrays.asList(exposeDefaults)); 065 } 066 067 public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, 068 Collection<String> include, Collection<String> exclude, 069 String... exposeDefaults) { 070 Assert.notNull(endpointType, "EndpointType Type must not be null"); 071 this.endpointType = endpointType; 072 this.include = asSet(include); 073 this.exclude = asSet(exclude); 074 this.exposeDefaults = asSet(Arrays.asList(exposeDefaults)); 075 } 076 077 private Set<String> bind(Binder binder, String name) { 078 return asSet(binder.bind(name, Bindable.listOf(String.class)).map(this::cleanup) 079 .orElseGet(ArrayList::new)); 080 } 081 082 private List<String> cleanup(List<String> values) { 083 return values.stream().map(this::cleanup).collect(Collectors.toList()); 084 } 085 086 private String cleanup(String value) { 087 return "*".equals(value) ? "*" 088 : EndpointId.fromPropertyValue(value).toLowerCaseString(); 089 } 090 091 private Set<String> asSet(Collection<String> items) { 092 if (items == null) { 093 return Collections.emptySet(); 094 } 095 return items.stream().map((item) -> item.toLowerCase(Locale.ENGLISH)) 096 .collect(Collectors.toSet()); 097 } 098 099 @Override 100 public boolean match(E endpoint) { 101 if (this.endpointType.isInstance(endpoint)) { 102 return isExposed(endpoint) && !isExcluded(endpoint); 103 } 104 return true; 105 } 106 107 private boolean isExposed(ExposableEndpoint<?> endpoint) { 108 if (this.include.isEmpty()) { 109 return this.exposeDefaults.contains("*") 110 || contains(this.exposeDefaults, endpoint); 111 } 112 return this.include.contains("*") || contains(this.include, endpoint); 113 } 114 115 private boolean isExcluded(ExposableEndpoint<?> endpoint) { 116 if (this.exclude.isEmpty()) { 117 return false; 118 } 119 return this.exclude.contains("*") || contains(this.exclude, endpoint); 120 } 121 122 private boolean contains(Set<String> items, ExposableEndpoint<?> endpoint) { 123 return items.contains(endpoint.getEndpointId().toLowerCaseString()); 124 } 125 126}