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.web.server; 018 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021 022import org.springframework.beans.factory.SmartInitializingSingleton; 023import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; 024import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory; 025import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; 026import org.springframework.boot.autoconfigure.AutoConfigureOrder; 027import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 028import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; 029import org.springframework.boot.context.event.ApplicationFailedEvent; 030import org.springframework.boot.context.properties.EnableConfigurationProperties; 031import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext; 032import org.springframework.boot.web.context.WebServerApplicationContext; 033import org.springframework.context.ApplicationContext; 034import org.springframework.context.ApplicationEvent; 035import org.springframework.context.ApplicationListener; 036import org.springframework.context.ConfigurableApplicationContext; 037import org.springframework.context.annotation.Configuration; 038import org.springframework.context.event.ContextClosedEvent; 039import org.springframework.core.Ordered; 040import org.springframework.core.env.ConfigurableEnvironment; 041import org.springframework.core.env.Environment; 042import org.springframework.core.env.PropertySource; 043import org.springframework.core.io.DefaultResourceLoader; 044import org.springframework.util.Assert; 045 046/** 047 * {@link EnableAutoConfiguration Auto-configuration} for the management context. If the 048 * {@code management.server.port} is the same as the {@code server.port} the management 049 * context will be the same as the main application context. If the 050 * {@code management.server.port} is different to the {@code server.port} the management 051 * context will be a separate context that has the main application context as its parent. 052 * 053 * @author Andy Wilkinson 054 * @since 2.0.0 055 */ 056@Configuration 057@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) 058@EnableConfigurationProperties({ WebEndpointProperties.class, 059 ManagementServerProperties.class }) 060public class ManagementContextAutoConfiguration { 061 062 private static final Log logger = LogFactory 063 .getLog(ManagementContextAutoConfiguration.class); 064 065 @Configuration 066 @ConditionalOnManagementPort(ManagementPortType.SAME) 067 static class SameManagementContextConfiguration 068 implements SmartInitializingSingleton { 069 070 private final Environment environment; 071 072 SameManagementContextConfiguration(Environment environment) { 073 this.environment = environment; 074 } 075 076 @Override 077 public void afterSingletonsInstantiated() { 078 verifySslConfiguration(); 079 if (this.environment instanceof ConfigurableEnvironment) { 080 addLocalManagementPortPropertyAlias( 081 (ConfigurableEnvironment) this.environment); 082 } 083 } 084 085 private void verifySslConfiguration() { 086 Boolean enabled = this.environment 087 .getProperty("management.server.ssl.enabled", Boolean.class, false); 088 Assert.state(!enabled, 089 "Management-specific SSL cannot be configured as the management " 090 + "server is not listening on a separate port"); 091 } 092 093 /** 094 * Add an alias for 'local.management.port' that actually resolves using 095 * 'local.server.port'. 096 * @param environment the environment 097 */ 098 private void addLocalManagementPortPropertyAlias( 099 ConfigurableEnvironment environment) { 100 environment.getPropertySources() 101 .addLast(new PropertySource<Object>("Management Server") { 102 103 @Override 104 public Object getProperty(String name) { 105 if ("local.management.port".equals(name)) { 106 return environment.getProperty("local.server.port"); 107 } 108 return null; 109 } 110 111 }); 112 } 113 114 @Configuration 115 @EnableManagementContext(ManagementContextType.SAME) 116 static class EnableSameManagementContextConfiguration { 117 118 } 119 120 } 121 122 @Configuration 123 @ConditionalOnManagementPort(ManagementPortType.DIFFERENT) 124 static class DifferentManagementContextConfiguration 125 implements SmartInitializingSingleton { 126 127 private final ApplicationContext applicationContext; 128 129 private final ManagementContextFactory managementContextFactory; 130 131 DifferentManagementContextConfiguration(ApplicationContext applicationContext, 132 ManagementContextFactory managementContextFactory) { 133 this.applicationContext = applicationContext; 134 this.managementContextFactory = managementContextFactory; 135 } 136 137 @Override 138 public void afterSingletonsInstantiated() { 139 if (this.applicationContext instanceof WebServerApplicationContext 140 && ((WebServerApplicationContext) this.applicationContext) 141 .getWebServer() != null) { 142 ConfigurableWebServerApplicationContext managementContext = this.managementContextFactory 143 .createManagementContext(this.applicationContext, 144 EnableChildManagementContextConfiguration.class, 145 PropertyPlaceholderAutoConfiguration.class); 146 managementContext.setServerNamespace("management"); 147 managementContext.setId(this.applicationContext.getId() + ":management"); 148 setClassLoaderIfPossible(managementContext); 149 CloseManagementContextListener.addIfPossible(this.applicationContext, 150 managementContext); 151 managementContext.refresh(); 152 } 153 else { 154 logger.warn("Could not start embedded management container on " 155 + "different port (management endpoints are still available " 156 + "through JMX)"); 157 } 158 } 159 160 private void setClassLoaderIfPossible(ConfigurableApplicationContext child) { 161 if (child instanceof DefaultResourceLoader) { 162 ((DefaultResourceLoader) child) 163 .setClassLoader(this.applicationContext.getClassLoader()); 164 } 165 } 166 167 } 168 169 /** 170 * {@link ApplicationListener} to propagate the {@link ContextClosedEvent} and 171 * {@link ApplicationFailedEvent} from a parent to a child. 172 */ 173 private static class CloseManagementContextListener 174 implements ApplicationListener<ApplicationEvent> { 175 176 private final ApplicationContext parentContext; 177 178 private final ConfigurableApplicationContext childContext; 179 180 CloseManagementContextListener(ApplicationContext parentContext, 181 ConfigurableApplicationContext childContext) { 182 this.parentContext = parentContext; 183 this.childContext = childContext; 184 } 185 186 @Override 187 public void onApplicationEvent(ApplicationEvent event) { 188 if (event instanceof ContextClosedEvent) { 189 onContextClosedEvent((ContextClosedEvent) event); 190 } 191 if (event instanceof ApplicationFailedEvent) { 192 onApplicationFailedEvent((ApplicationFailedEvent) event); 193 } 194 } 195 196 private void onContextClosedEvent(ContextClosedEvent event) { 197 propagateCloseIfNecessary(event.getApplicationContext()); 198 } 199 200 private void onApplicationFailedEvent(ApplicationFailedEvent event) { 201 propagateCloseIfNecessary(event.getApplicationContext()); 202 } 203 204 private void propagateCloseIfNecessary(ApplicationContext applicationContext) { 205 if (applicationContext == this.parentContext) { 206 this.childContext.close(); 207 } 208 } 209 210 public static void addIfPossible(ApplicationContext parentContext, 211 ConfigurableApplicationContext childContext) { 212 if (parentContext instanceof ConfigurableApplicationContext) { 213 add((ConfigurableApplicationContext) parentContext, childContext); 214 } 215 } 216 217 private static void add(ConfigurableApplicationContext parentContext, 218 ConfigurableApplicationContext childContext) { 219 parentContext.addApplicationListener( 220 new CloseManagementContextListener(parentContext, childContext)); 221 } 222 223 } 224 225}