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.io.File; 020import java.util.Locale; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.boot.system.SystemProperties; 026import org.springframework.context.ApplicationContext; 027import org.springframework.context.ApplicationListener; 028import org.springframework.util.Assert; 029import org.springframework.util.FileCopyUtils; 030import org.springframework.util.StringUtils; 031 032/** 033 * An {@link ApplicationListener} that saves embedded server port and management port into 034 * file. This application listener will be triggered whenever the server starts, and the 035 * file name can be overridden at runtime with a System property or environment variable 036 * named "PORTFILE" or "portfile". 037 * 038 * @author David Liu 039 * @author Phillip Webb 040 * @author Andy Wilkinson 041 * @since 2.0.0 042 */ 043public class WebServerPortFileWriter 044 implements ApplicationListener<WebServerInitializedEvent> { 045 046 private static final String DEFAULT_FILE_NAME = "application.port"; 047 048 private static final String[] PROPERTY_VARIABLES = { "PORTFILE", "portfile" }; 049 050 private static final Log logger = LogFactory.getLog(WebServerPortFileWriter.class); 051 052 private final File file; 053 054 /** 055 * Create a new {@link WebServerPortFileWriter} instance using the filename 056 * 'application.port'. 057 */ 058 public WebServerPortFileWriter() { 059 this(new File(DEFAULT_FILE_NAME)); 060 } 061 062 /** 063 * Create a new {@link WebServerPortFileWriter} instance with a specified filename. 064 * @param filename the name of file containing port 065 */ 066 public WebServerPortFileWriter(String filename) { 067 this(new File(filename)); 068 } 069 070 /** 071 * Create a new {@link WebServerPortFileWriter} instance with a specified file. 072 * @param file the file containing port 073 */ 074 public WebServerPortFileWriter(File file) { 075 Assert.notNull(file, "File must not be null"); 076 String override = SystemProperties.get(PROPERTY_VARIABLES); 077 if (override != null) { 078 this.file = new File(override); 079 } 080 else { 081 this.file = file; 082 } 083 } 084 085 @Override 086 public void onApplicationEvent(WebServerInitializedEvent event) { 087 File portFile = getPortFile(event.getApplicationContext()); 088 try { 089 String port = String.valueOf(event.getWebServer().getPort()); 090 createParentFolder(portFile); 091 FileCopyUtils.copy(port.getBytes(), portFile); 092 portFile.deleteOnExit(); 093 } 094 catch (Exception ex) { 095 logger.warn(String.format("Cannot create port file %s", this.file)); 096 } 097 } 098 099 /** 100 * Return the actual port file that should be written for the given application 101 * context. The default implementation builds a file from the source file and the 102 * application context namespace if available. 103 * @param applicationContext the source application context 104 * @return the file that should be written 105 */ 106 protected File getPortFile(ApplicationContext applicationContext) { 107 String namespace = getServerNamespace(applicationContext); 108 if (StringUtils.isEmpty(namespace)) { 109 return this.file; 110 } 111 String name = this.file.getName(); 112 String extension = StringUtils.getFilenameExtension(this.file.getName()); 113 name = name.substring(0, name.length() - extension.length() - 1); 114 if (isUpperCase(name)) { 115 name = name + "-" + namespace.toUpperCase(Locale.ENGLISH); 116 } 117 else { 118 name = name + "-" + namespace.toLowerCase(Locale.ENGLISH); 119 } 120 if (StringUtils.hasLength(extension)) { 121 name = name + "." + extension; 122 } 123 return new File(this.file.getParentFile(), name); 124 } 125 126 private String getServerNamespace(ApplicationContext applicationContext) { 127 if (applicationContext instanceof WebServerApplicationContext) { 128 return ((WebServerApplicationContext) applicationContext) 129 .getServerNamespace(); 130 } 131 return null; 132 } 133 134 private boolean isUpperCase(String name) { 135 for (int i = 0; i < name.length(); i++) { 136 if (Character.isLetter(name.charAt(i)) 137 && !Character.isUpperCase(name.charAt(i))) { 138 return false; 139 } 140 } 141 return true; 142 } 143 144 private void createParentFolder(File file) { 145 File parent = file.getParentFile(); 146 if (parent != null) { 147 parent.mkdirs(); 148 } 149 } 150 151}