Browse Source
Add a EmbeddedServerPortFileWriter which can be used to write server port information to a file. Fixes gh-1275 Closes gh-1491pull/1840/head
5 changed files with 313 additions and 27 deletions
@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
|
||||
/* |
||||
* Copyright 2010-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.actuate.system; |
||||
|
||||
import java.io.File; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; |
||||
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; |
||||
import org.springframework.context.ApplicationListener; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* An {@link ApplicationListener} that saves embedded server port and management port into |
||||
* file. This application listener will be triggered whenever the servlet container |
||||
* starts, and the file name can be overridden at runtime with a System property or |
||||
* environment variable named "PORTFILE" or "portfile". |
||||
* |
||||
* @author David Liu |
||||
* @author Phillip Webb |
||||
* @since 1.2.0 |
||||
*/ |
||||
public class EmbeddedServerPortFileWriter implements |
||||
ApplicationListener<EmbeddedServletContainerInitializedEvent> { |
||||
|
||||
private static final String DEFAULT_FILE_NAME = "application.port"; |
||||
|
||||
private static final String[] PROPERTY_VARIABLES = { "PORTFILE", "portfile" }; |
||||
|
||||
private static final Log logger = LogFactory.getLog(ApplicationPidFileWriter.class); |
||||
|
||||
private final File file; |
||||
|
||||
/** |
||||
* Create a new {@link ApplicationPidFileWriter} instance using the filename |
||||
* 'application.pid'. |
||||
*/ |
||||
public EmbeddedServerPortFileWriter() { |
||||
this.file = new File(DEFAULT_FILE_NAME); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link ApplicationPidFileWriter} instance with a specified filename. |
||||
* @param filename the name of file containing pid |
||||
*/ |
||||
public EmbeddedServerPortFileWriter(String filename) { |
||||
this(new File(filename)); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link ApplicationPidFileWriter} instance with a specified file. |
||||
* @param file the file containing pid |
||||
*/ |
||||
public EmbeddedServerPortFileWriter(File file) { |
||||
Assert.notNull(file, "File must not be null"); |
||||
String override = SystemProperties.get(PROPERTY_VARIABLES); |
||||
if (override != null) { |
||||
this.file = new File(override); |
||||
} |
||||
else { |
||||
this.file = file; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { |
||||
File portFile = getPortFile(event.getApplicationContext()); |
||||
try { |
||||
String port = String.valueOf(event.getEmbeddedServletContainer().getPort()); |
||||
createParentFolder(portFile); |
||||
FileCopyUtils.copy(port.getBytes(), portFile); |
||||
portFile.deleteOnExit(); |
||||
} |
||||
catch (Exception ex) { |
||||
logger.warn(String.format("Cannot create pid file %s", this.file)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return the actual port file that should be written for the given application |
||||
* context. The default implementation builds a file from the source file and the |
||||
* application context namespace. |
||||
* @param applicationContext the source application context |
||||
* @return the file that should be written |
||||
*/ |
||||
protected File getPortFile(EmbeddedWebApplicationContext applicationContext) { |
||||
String contextName = applicationContext.getNamespace(); |
||||
if (StringUtils.isEmpty(contextName)) { |
||||
return this.file; |
||||
} |
||||
String name = this.file.getName(); |
||||
String extension = StringUtils.getFilenameExtension(this.file.getName()); |
||||
name = name.substring(0, name.length() - extension.length() - 1); |
||||
if (isUpperCase(name)) { |
||||
name = name + "-" + contextName.toUpperCase(); |
||||
} |
||||
else { |
||||
name = name + "-" + contextName.toLowerCase(); |
||||
} |
||||
if (StringUtils.hasLength(extension)) { |
||||
name = name + "." + extension; |
||||
} |
||||
return new File(this.file.getParentFile(), name); |
||||
} |
||||
|
||||
private boolean isUpperCase(String name) { |
||||
for (int i = 0; i < name.length(); i++) { |
||||
if (!Character.isUpperCase(name.charAt(i))) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private void createParentFolder(File file) { |
||||
File parent = file.getParentFile(); |
||||
if (parent != null) { |
||||
parent.mkdirs(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* Copyright 2012-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.actuate.system; |
||||
|
||||
/** |
||||
* @author Phillip Webb |
||||
*/ |
||||
class SystemProperties { |
||||
|
||||
public static String get(String... properties) { |
||||
for (String property : properties) { |
||||
try { |
||||
String override = System.getProperty(property); |
||||
override = (override != null ? override : System.getenv(property)); |
||||
if (override != null) { |
||||
return override; |
||||
} |
||||
} |
||||
catch (Throwable ex) { |
||||
System.err.println("Could not resolve '" + property |
||||
+ "' as system property: " + ex); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
/* |
||||
* Copyright 2010-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.actuate.system; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileReader; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.TemporaryFolder; |
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer; |
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; |
||||
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.junit.Assert.assertThat; |
||||
import static org.mockito.BDDMockito.given; |
||||
import static org.mockito.Mockito.mock; |
||||
|
||||
/** |
||||
* Tests {@link EmbeddedServerPortFileWriter}. |
||||
* |
||||
* @author David Liu |
||||
* @author Phillip Webb |
||||
*/ |
||||
public class EmbeddedServerPortFileWriterTests { |
||||
|
||||
@Rule |
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder(); |
||||
|
||||
@Before |
||||
@After |
||||
public void reset() { |
||||
System.clearProperty("PORTFILE"); |
||||
} |
||||
|
||||
@Test |
||||
public void createPortFile() throws Exception { |
||||
File file = this.temporaryFolder.newFile(); |
||||
EmbeddedServerPortFileWriter listener = new EmbeddedServerPortFileWriter(file); |
||||
listener.onApplicationEvent(mockEvent("", 8080)); |
||||
assertThat(FileCopyUtils.copyToString(new FileReader(file)), equalTo("8080")); |
||||
} |
||||
|
||||
@Test |
||||
public void overridePortFile() throws Exception { |
||||
File file = this.temporaryFolder.newFile(); |
||||
System.setProperty("PORTFILE", this.temporaryFolder.newFile().getAbsolutePath()); |
||||
EmbeddedServerPortFileWriter listener = new EmbeddedServerPortFileWriter(file); |
||||
listener.onApplicationEvent(mockEvent("", 8080)); |
||||
assertThat(FileCopyUtils.copyToString(new FileReader(System |
||||
.getProperty("PORTFILE"))), equalTo("8080")); |
||||
} |
||||
|
||||
@Test |
||||
public void createManagementPortFile() throws Exception { |
||||
File file = this.temporaryFolder.newFile(); |
||||
EmbeddedServerPortFileWriter listener = new EmbeddedServerPortFileWriter(file); |
||||
listener.onApplicationEvent(mockEvent("", 8080)); |
||||
listener.onApplicationEvent(mockEvent("management", 9090)); |
||||
assertThat(FileCopyUtils.copyToString(new FileReader(file)), equalTo("8080")); |
||||
String managementFile = file.getName(); |
||||
managementFile = managementFile.substring(0, managementFile.length() |
||||
- StringUtils.getFilenameExtension(managementFile).length() - 1); |
||||
managementFile = managementFile + "-management." |
||||
+ StringUtils.getFilenameExtension(file.getName()); |
||||
assertThat(FileCopyUtils.copyToString(new FileReader(new File(file |
||||
.getParentFile(), managementFile))), equalTo("9090")); |
||||
} |
||||
|
||||
@Test |
||||
public void createUpperCaseManagementPortFile() throws Exception { |
||||
File file = this.temporaryFolder.newFile(); |
||||
file = new File(file.getParentFile(), file.getName().toUpperCase()); |
||||
EmbeddedServerPortFileWriter listener = new EmbeddedServerPortFileWriter(file); |
||||
listener.onApplicationEvent(mockEvent("management", 9090)); |
||||
String managementFile = file.getName(); |
||||
managementFile = managementFile.substring(0, managementFile.length() |
||||
- StringUtils.getFilenameExtension(managementFile).length() - 1); |
||||
managementFile = managementFile + "-MANAGEMENT." |
||||
+ StringUtils.getFilenameExtension(file.getName()); |
||||
assertThat(FileCopyUtils.copyToString(new FileReader(new File(file |
||||
.getParentFile(), managementFile))), equalTo("9090")); |
||||
|
||||
} |
||||
|
||||
private EmbeddedServletContainerInitializedEvent mockEvent(String name, int port) { |
||||
EmbeddedWebApplicationContext applicationContext = mock(EmbeddedWebApplicationContext.class); |
||||
EmbeddedServletContainer source = mock(EmbeddedServletContainer.class); |
||||
given(applicationContext.getNamespace()).willReturn(name); |
||||
given(source.getPort()).willReturn(port); |
||||
EmbeddedServletContainerInitializedEvent event = new EmbeddedServletContainerInitializedEvent( |
||||
applicationContext, source); |
||||
return event; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue