001/* 002 * Copyright 2012-2017 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.info; 018 019import java.util.Collections; 020import java.util.LinkedHashMap; 021import java.util.Map; 022import java.util.Properties; 023 024import org.springframework.boot.context.properties.bind.Bindable; 025import org.springframework.boot.context.properties.bind.Binder; 026import org.springframework.boot.context.properties.source.ConfigurationPropertySources; 027import org.springframework.boot.info.InfoProperties; 028import org.springframework.core.env.PropertySource; 029import org.springframework.util.StringUtils; 030 031/** 032 * A base {@link InfoContributor} to expose an {@link InfoProperties}. 033 * 034 * @param <T> the type of the {@link InfoProperties} to expose 035 * @author Stephane Nicoll 036 * @author Madhura Bhave 037 * @since 1.4.0 038 */ 039public abstract class InfoPropertiesInfoContributor<T extends InfoProperties> 040 implements InfoContributor { 041 042 private static final Bindable<Map<String, Object>> STRING_OBJECT_MAP = Bindable 043 .mapOf(String.class, Object.class); 044 045 private final T properties; 046 047 private final Mode mode; 048 049 protected InfoPropertiesInfoContributor(T properties, Mode mode) { 050 this.properties = properties; 051 this.mode = mode; 052 } 053 054 /** 055 * Return the properties that this instance manages. 056 * @return the info properties 057 */ 058 protected final T getProperties() { 059 return this.properties; 060 } 061 062 /** 063 * Return the mode that should be used to expose the content. 064 * @return the mode 065 */ 066 protected final Mode getMode() { 067 return this.mode; 068 } 069 070 /** 071 * Return a {@link PropertySource} for the {@link Mode#SIMPLE SIMPLE} mode. 072 * @return the property source for the simple model 073 * @see #toPropertySource() 074 */ 075 protected abstract PropertySource<?> toSimplePropertySource(); 076 077 /** 078 * Extract the content to contribute to the info endpoint. 079 * @return the content to expose 080 * @see #extractContent(PropertySource) 081 * @see #postProcessContent(Map) 082 */ 083 protected Map<String, Object> generateContent() { 084 Map<String, Object> content = extractContent(toPropertySource()); 085 postProcessContent(content); 086 return content; 087 } 088 089 /** 090 * Extract the raw content based on the specified {@link PropertySource}. 091 * @param propertySource the property source to use 092 * @return the raw content 093 */ 094 protected Map<String, Object> extractContent(PropertySource<?> propertySource) { 095 return new Binder(ConfigurationPropertySources.from(propertySource)) 096 .bind("", STRING_OBJECT_MAP).orElseGet(LinkedHashMap::new); 097 } 098 099 /** 100 * Post-process the content to expose. Elements can be added, changed or removed. 101 * @param content the content to expose 102 */ 103 protected void postProcessContent(Map<String, Object> content) { 104 105 } 106 107 /** 108 * Return the {@link PropertySource} to use based on the chosen {@link Mode}. 109 * @return the property source 110 */ 111 protected PropertySource<?> toPropertySource() { 112 if (this.mode.equals(Mode.FULL)) { 113 return this.properties.toPropertySource(); 114 } 115 return toSimplePropertySource(); 116 } 117 118 /** 119 * Copy the specified key to the target {@link Properties} if it is set. 120 * @param target the target properties to update 121 * @param key the key 122 */ 123 protected void copyIfSet(Properties target, String key) { 124 String value = this.properties.get(key); 125 if (StringUtils.hasText(value)) { 126 target.put(key, value); 127 } 128 } 129 130 /** 131 * Replace the {@code value} for the specified key if the value is not {@code null}. 132 * @param content the content to expose 133 * @param key the property to replace 134 * @param value the new value 135 */ 136 protected void replaceValue(Map<String, Object> content, String key, Object value) { 137 if (content.containsKey(key) && value != null) { 138 content.put(key, value); 139 } 140 } 141 142 /** 143 * Return the nested map with the specified key or empty map if the specified map 144 * contains no mapping for the key. 145 * @param map the content 146 * @param key the key of a nested map 147 * @return the nested map 148 */ 149 @SuppressWarnings("unchecked") 150 protected Map<String, Object> getNestedMap(Map<String, Object> map, String key) { 151 Object value = map.get(key); 152 if (value == null) { 153 return Collections.emptyMap(); 154 } 155 return (Map<String, Object>) value; 156 } 157 158 /** 159 * Defines how properties should be exposed. 160 */ 161 public enum Mode { 162 163 /** 164 * Expose all available data, including custom properties. 165 */ 166 FULL, 167 168 /** 169 * Expose a pre-defined set of core settings only. 170 */ 171 SIMPLE 172 173 } 174 175}