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.web.context; 018 019import java.util.HashMap; 020import java.util.Map; 021 022import org.springframework.beans.factory.annotation.Value; 023import org.springframework.boot.web.server.WebServer; 024import org.springframework.context.ApplicationContext; 025import org.springframework.context.ApplicationContextInitializer; 026import org.springframework.context.ApplicationListener; 027import org.springframework.context.ConfigurableApplicationContext; 028import org.springframework.core.env.ConfigurableEnvironment; 029import org.springframework.core.env.Environment; 030import org.springframework.core.env.MapPropertySource; 031import org.springframework.core.env.MutablePropertySources; 032import org.springframework.core.env.PropertySource; 033import org.springframework.util.StringUtils; 034 035/** 036 * {@link ApplicationContextInitializer} that sets {@link Environment} properties for the 037 * ports that {@link WebServer} servers are actually listening on. The property 038 * {@literal "local.server.port"} can be injected directly into tests using 039 * {@link Value @Value} or obtained via the {@link Environment}. 040 * <p> 041 * If the {@link WebServerInitializedEvent} has a 042 * {@link WebServerApplicationContext#getServerNamespace() server namespace} , it will be 043 * used to construct the property name. For example, the "management" actuator context 044 * will have the property name {@literal "local.management.port"}. 045 * <p> 046 * Properties are automatically propagated up to any parent context. 047 * 048 * @author Dave Syer 049 * @author Phillip Webb 050 * @since 2.0.0 051 */ 052public class ServerPortInfoApplicationContextInitializer 053 implements ApplicationContextInitializer<ConfigurableApplicationContext>, 054 ApplicationListener<WebServerInitializedEvent> { 055 056 @Override 057 public void initialize(ConfigurableApplicationContext applicationContext) { 058 applicationContext.addApplicationListener(this); 059 } 060 061 @Override 062 public void onApplicationEvent(WebServerInitializedEvent event) { 063 String propertyName = "local." + getName(event.getApplicationContext()) + ".port"; 064 setPortProperty(event.getApplicationContext(), propertyName, 065 event.getWebServer().getPort()); 066 } 067 068 private String getName(WebServerApplicationContext context) { 069 String name = context.getServerNamespace(); 070 return StringUtils.hasText(name) ? name : "server"; 071 } 072 073 private void setPortProperty(ApplicationContext context, String propertyName, 074 int port) { 075 if (context instanceof ConfigurableApplicationContext) { 076 setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), 077 propertyName, port); 078 } 079 if (context.getParent() != null) { 080 setPortProperty(context.getParent(), propertyName, port); 081 } 082 } 083 084 @SuppressWarnings("unchecked") 085 private void setPortProperty(ConfigurableEnvironment environment, String propertyName, 086 int port) { 087 MutablePropertySources sources = environment.getPropertySources(); 088 PropertySource<?> source = sources.get("server.ports"); 089 if (source == null) { 090 source = new MapPropertySource("server.ports", new HashMap<>()); 091 sources.addFirst(source); 092 } 093 ((Map<String, Object>) source.getSource()).put(propertyName, port); 094 } 095 096}