From 316cb875836cb69e38309b62e9ad56b11a3f71b6 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 23 Apr 2014 10:45:05 +0100 Subject: [PATCH] Create ApplicationPid and remove SystemUtils Create a new ApplicationPid class to remove the need for SystemUtils and refactor existing calls. --- .../system/ApplicationPidListener.java | 104 +++++++----------- .../system/ApplicationPidListenerTest.java | 54 ++++----- .../springframework/boot/ApplicationPid.java | 99 +++++++++++++++++ .../logging/LoggingApplicationListener.java | 18 +-- .../boot/util/SystemUtils.java | 63 ----------- .../boot/util/package-info.java | 21 ---- .../boot/ApplicationPidTests.java | 77 +++++++++++++ .../boot/util/SystemUtilsTest.java | 38 ------- 8 files changed, 244 insertions(+), 230 deletions(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/ApplicationPid.java delete mode 100644 spring-boot/src/main/java/org/springframework/boot/util/SystemUtils.java delete mode 100644 spring-boot/src/main/java/org/springframework/boot/util/package-info.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/ApplicationPidTests.java delete mode 100644 spring-boot/src/test/java/org/springframework/boot/util/SystemUtilsTest.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/ApplicationPidListener.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/ApplicationPidListener.java index 17d51c7daa5..8973ae4fadf 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/ApplicationPidListener.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/ApplicationPidListener.java @@ -17,106 +17,76 @@ package org.springframework.boot.actuate.system; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.ApplicationPid; import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.boot.util.SystemUtils; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; +import org.springframework.util.Assert; /** * An {@link org.springframework.context.ApplicationListener} that saves application PID - * into file + * into file. This application listener will be triggered exactly once per JVM. * - * @since 1.0.2 - * * @author Jakub Kubrynski * @author Dave Syer + * @author Phillip Webb + * @since 1.0.2 */ public class ApplicationPidListener implements ApplicationListener, Ordered { - private static final String DEFAULT_PID_FILE_NAME = "application.pid"; + private static final Log logger = LogFactory.getLog(ApplicationPidListener.class); - private static final AtomicBoolean pidFileCreated = new AtomicBoolean(false); + private static final String DEFAULT_FILE_NAME = "application.pid"; + + private static final AtomicBoolean created = new AtomicBoolean(false); private int order = Ordered.HIGHEST_PRECEDENCE + 13; - private static final Log logger = LogFactory.getLog(ApplicationPidListener.class); - - private String pidFileName = DEFAULT_PID_FILE_NAME; + private final File file; /** - * Sets the pid file name. This file will contain current process id. - * - * @param pidFileName the name of file containing pid + * Create a new {@link ApplicationPidListener} instance using the filename + * 'application.pid'. */ - public ApplicationPidListener(String pidFileName) { - this.pidFileName = pidFileName; + public ApplicationPidListener() { + this.file = new File(DEFAULT_FILE_NAME); } - public ApplicationPidListener() { + /** + * Create a new {@link ApplicationPidListener} instance with a specified filename. + * @param filename the name of file containing pid + */ + public ApplicationPidListener(String filename) { + Assert.notNull(filename, "Filename must not be null"); + this.file = new File(filename); + } + + /** + * Create a new {@link ApplicationPidListener} instance with a specified file. + * @param file the file containing pid + */ + public ApplicationPidListener(File file) { + Assert.notNull(file, "File must not be null"); + this.file = file; } @Override public void onApplicationEvent(ApplicationStartedEvent event) { - if (pidFileCreated.get()) { - return; - } - - String applicationPid; - try { - applicationPid = SystemUtils.getApplicationPid(); - } - catch (IllegalStateException ignore) { - return; - } - - if (pidFileCreated.compareAndSet(false, true)) { - File file = new File(this.pidFileName); - FileOutputStream fileOutputStream = null; + if (created.compareAndSet(false, true)) { try { - File parent = file.getParentFile(); - if (parent != null) { - parent.mkdirs(); - } - fileOutputStream = new FileOutputStream(file); - fileOutputStream.write(applicationPid.getBytes()); + new ApplicationPid().write(this.file); } - catch (FileNotFoundException e) { - logger.warn(String - .format("Cannot create pid file %s !", this.pidFileName)); - } - catch (Exception e) { - logger.warn(String.format("Cannot write to pid file %s!", - this.pidFileName)); - } - finally { - if (fileOutputStream != null) { - try { - fileOutputStream.close(); - } - catch (IOException e) { - logger.warn(String.format("Cannot close pid file %s!", - this.pidFileName)); - } - } + catch (Exception ex) { + logger.warn(String.format("Cannot create pid file %s", this.file)); } } } - /** - * Allow pid file to be re-written - */ - public static void reset() { - pidFileCreated.set(false); - } - public void setOrder(int order) { this.order = order; } @@ -126,4 +96,10 @@ public class ApplicationPidListener implements return this.order; } + /** + * Reset the created flag for testing purposes. + */ + static void reset() { + created.set(false); + } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/system/ApplicationPidListenerTest.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/system/ApplicationPidListenerTest.java index 168c7765b9d..0e70cd5f56c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/system/ApplicationPidListenerTest.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/system/ApplicationPidListenerTest.java @@ -17,57 +17,47 @@ 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.SpringApplication; import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.util.FileCopyUtils; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; /** + * Tests fpr {@link ApplicationPidListener}. + * * @author Jakub Kubrynski * @author Dave Syer */ public class ApplicationPidListenerTest { - private static final String[] NO_ARGS = {}; + private static final ApplicationStartedEvent EVENT = new ApplicationStartedEvent( + new SpringApplication(), new String[] {}); + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before @After - public void init() { + public void resetListener() { ApplicationPidListener.reset(); } @Test - public void shouldCreatePidFile() { - // given - String pidFileName = "test.pid"; - ApplicationPidListener sut = new ApplicationPidListener(pidFileName); - - // when - sut.onApplicationEvent(new ApplicationStartedEvent(new SpringApplication(), - NO_ARGS)); - - // then - File pidFile = new File(pidFileName); - assertTrue(pidFile.exists()); - pidFile.delete(); - } - - @Test - public void shouldCreatePidFileParentDirectory() { - // given - String pidFileName = "target/pid/test.pid"; - ApplicationPidListener sut = new ApplicationPidListener(pidFileName); - - // when - sut.onApplicationEvent(new ApplicationStartedEvent(new SpringApplication(), - NO_ARGS)); - - // then - File pidFile = new File(pidFileName); - assertTrue(pidFile.exists()); - pidFile.delete(); + public void createPidFile() throws Exception { + File file = this.temporaryFolder.newFile(); + ApplicationPidListener listener = new ApplicationPidListener(file); + listener.onApplicationEvent(EVENT); + assertThat(FileCopyUtils.copyToString(new FileReader(file)), not(isEmptyString())); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/ApplicationPid.java b/spring-boot/src/main/java/org/springframework/boot/ApplicationPid.java new file mode 100644 index 00000000000..ac5384fca4d --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/ApplicationPid.java @@ -0,0 +1,99 @@ +/* + * 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; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.management.ManagementFactory; + +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * An application process ID. + * + * @author Phillip Webb + */ +public class ApplicationPid { + + private final String pid; + + public ApplicationPid() { + this.pid = getPid(); + } + + protected ApplicationPid(String pid) { + this.pid = pid; + } + + private String getPid() { + try { + String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + return jvmName.split("@")[0]; + } + catch (Throwable ex) { + return null; + } + } + + @Override + public String toString() { + return (this.pid == null ? "???" : this.pid); + } + + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(this.pid); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj != null && obj instanceof ApplicationPid) { + return ObjectUtils.nullSafeEquals(this.pid, ((ApplicationPid) obj).pid); + } + return false; + } + + /** + * Write the PID to the specified file. + * @throws IllegalStateException if no PID is available. + * @throws IOException if the file cannot be written + */ + public void write(File file) throws IOException { + Assert.state(this.pid != null, "No PID available"); + createParentFolder(file); + FileWriter writer = new FileWriter(file); + try { + writer.append(this.pid); + } + finally { + writer.close(); + } + } + + private void createParentFolder(File file) { + File parent = file.getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java index 56dbb778195..64b05be1929 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java @@ -16,12 +16,16 @@ package org.springframework.boot.logging; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.ApplicationPid; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.boot.util.SystemUtils; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.SmartApplicationListener; @@ -33,10 +37,6 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.ResourceUtils; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * An {@link ApplicationListener} that configures a logging framework depending on what it * finds on the classpath and in the {@link Environment}. If the environment contains a @@ -124,13 +124,7 @@ public class LoggingApplicationListener implements SmartApplicationListener { } else { if (System.getProperty(PID_KEY) == null) { - String applicationPid; - try { - applicationPid = SystemUtils.getApplicationPid(); - } catch (IllegalStateException e) { - applicationPid = "????"; - } - System.setProperty(PID_KEY, applicationPid); + System.setProperty(PID_KEY, new ApplicationPid().toString()); } LoggingSystem loggingSystem = LoggingSystem.get(ClassUtils .getDefaultClassLoader()); diff --git a/spring-boot/src/main/java/org/springframework/boot/util/SystemUtils.java b/spring-boot/src/main/java/org/springframework/boot/util/SystemUtils.java deleted file mode 100644 index a59680faed9..00000000000 --- a/spring-boot/src/main/java/org/springframework/boot/util/SystemUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.util; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.util.StringUtils; - -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; - -/** - * Class containing methods related to system utilities - * - * @author Jakub Kubrynski - */ -public class SystemUtils { - - private static final Log LOG = LogFactory.getLog(SystemUtils.class); - - /** - * Looks for application PID - * @return application PID - * @throws java.lang.IllegalStateException if PID could not be determined - */ - public static String getApplicationPid() { - String pid = null; - try { - RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); - String jvmName = runtimeBean.getName(); - if (StringUtils.isEmpty(jvmName)) { - LOG.warn("Cannot get JVM name"); - } - if (!jvmName.contains("@")) { - LOG.warn("JVM name doesn't contain process id"); - } - pid = jvmName.split("@")[0]; - } catch (Throwable e) { - LOG.warn("Cannot get RuntimeMXBean", e); - } - - if (pid == null) { - throw new IllegalStateException("Application PID not found"); - } - - return pid; - } - -} diff --git a/spring-boot/src/main/java/org/springframework/boot/util/package-info.java b/spring-boot/src/main/java/org/springframework/boot/util/package-info.java deleted file mode 100644 index 9c4687b21c1..00000000000 --- a/spring-boot/src/main/java/org/springframework/boot/util/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -/** - * Utility classes - */ -package org.springframework.boot.util; - diff --git a/spring-boot/src/test/java/org/springframework/boot/ApplicationPidTests.java b/spring-boot/src/test/java/org/springframework/boot/ApplicationPidTests.java new file mode 100644 index 00000000000..1b2339565a2 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/ApplicationPidTests.java @@ -0,0 +1,77 @@ +/* + * 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; + +import java.io.File; +import java.io.FileReader; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.springframework.util.FileCopyUtils; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link ApplicationPid}. + * @author Phillip Webb + */ +public class ApplicationPidTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void toStringWithPid() throws Exception { + assertThat(new ApplicationPid("123").toString(), equalTo("123")); + } + + @Test + public void toStringWithoutPid() throws Exception { + assertThat(new ApplicationPid(null).toString(), equalTo("???")); + } + + @Test + public void throwIllegalStateWritingMissingPid() throws Exception { + ApplicationPid pid = new ApplicationPid(null); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("No PID available"); + pid.write(this.temporaryFolder.newFile()); + } + + @Test + public void writePid() throws Exception { + ApplicationPid pid = new ApplicationPid("123"); + File file = this.temporaryFolder.newFile(); + pid.write(file); + String actual = FileCopyUtils.copyToString(new FileReader(file)); + assertThat(actual, equalTo("123")); + } + + @Test + public void getPidFromJvm() throws Exception { + assertThat(new ApplicationPid().toString(), not(isEmptyOrNullString())); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/util/SystemUtilsTest.java b/spring-boot/src/test/java/org/springframework/boot/util/SystemUtilsTest.java deleted file mode 100644 index ecb4c82aa53..00000000000 --- a/spring-boot/src/test/java/org/springframework/boot/util/SystemUtilsTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.util; - -import org.junit.Test; - -import static org.junit.Assert.assertNotNull; - -/** - * Tests for {@link org.springframework.boot.util.SystemUtils}. - * - * @author Jakub Kubrynski - */ -public class SystemUtilsTest { - - @Test - public void shouldGetApplicationPid() throws Exception { - //when - String applicationPid = SystemUtils.getApplicationPid(); - - //then - assertNotNull(applicationPid); - } -}