Skip to content

Commit 316cb87

Browse files
author
Phillip Webb
committed
Create ApplicationPid and remove SystemUtils
Create a new ApplicationPid class to remove the need for SystemUtils and refactor existing calls.
1 parent b291332 commit 316cb87

File tree

8 files changed

+243
-229
lines changed

8 files changed

+243
-229
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/ApplicationPidListener.java

Lines changed: 39 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,106 +17,76 @@
1717
package org.springframework.boot.actuate.system;
1818

1919
import java.io.File;
20-
import java.io.FileNotFoundException;
21-
import java.io.FileOutputStream;
22-
import java.io.IOException;
2320
import java.util.concurrent.atomic.AtomicBoolean;
2421

2522
import org.apache.commons.logging.Log;
2623
import org.apache.commons.logging.LogFactory;
24+
import org.springframework.boot.ApplicationPid;
2725
import org.springframework.boot.context.event.ApplicationStartedEvent;
28-
import org.springframework.boot.util.SystemUtils;
2926
import org.springframework.context.ApplicationListener;
3027
import org.springframework.core.Ordered;
28+
import org.springframework.util.Assert;
3129

3230
/**
3331
* An {@link org.springframework.context.ApplicationListener} that saves application PID
34-
* into file
32+
* into file. This application listener will be triggered exactly once per JVM.
3533
*
36-
* @since 1.0.2
37-
*
3834
* @author Jakub Kubrynski
3935
* @author Dave Syer
36+
* @author Phillip Webb
37+
* @since 1.0.2
4038
*/
4139
public class ApplicationPidListener implements
4240
ApplicationListener<ApplicationStartedEvent>, Ordered {
4341

44-
private static final String DEFAULT_PID_FILE_NAME = "application.pid";
42+
private static final Log logger = LogFactory.getLog(ApplicationPidListener.class);
4543

46-
private static final AtomicBoolean pidFileCreated = new AtomicBoolean(false);
44+
private static final String DEFAULT_FILE_NAME = "application.pid";
45+
46+
private static final AtomicBoolean created = new AtomicBoolean(false);
4747

4848
private int order = Ordered.HIGHEST_PRECEDENCE + 13;
4949

50-
private static final Log logger = LogFactory.getLog(ApplicationPidListener.class);
50+
private final File file;
5151

52-
private String pidFileName = DEFAULT_PID_FILE_NAME;
52+
/**
53+
* Create a new {@link ApplicationPidListener} instance using the filename
54+
* 'application.pid'.
55+
*/
56+
public ApplicationPidListener() {
57+
this.file = new File(DEFAULT_FILE_NAME);
58+
}
5359

5460
/**
55-
* Sets the pid file name. This file will contain current process id.
56-
*
57-
* @param pidFileName the name of file containing pid
61+
* Create a new {@link ApplicationPidListener} instance with a specified filename.
62+
* @param filename the name of file containing pid
5863
*/
59-
public ApplicationPidListener(String pidFileName) {
60-
this.pidFileName = pidFileName;
64+
public ApplicationPidListener(String filename) {
65+
Assert.notNull(filename, "Filename must not be null");
66+
this.file = new File(filename);
6167
}
6268

63-
public ApplicationPidListener() {
69+
/**
70+
* Create a new {@link ApplicationPidListener} instance with a specified file.
71+
* @param file the file containing pid
72+
*/
73+
public ApplicationPidListener(File file) {
74+
Assert.notNull(file, "File must not be null");
75+
this.file = file;
6476
}
6577

6678
@Override
6779
public void onApplicationEvent(ApplicationStartedEvent event) {
68-
if (pidFileCreated.get()) {
69-
return;
70-
}
71-
72-
String applicationPid;
73-
try {
74-
applicationPid = SystemUtils.getApplicationPid();
75-
}
76-
catch (IllegalStateException ignore) {
77-
return;
78-
}
79-
80-
if (pidFileCreated.compareAndSet(false, true)) {
81-
File file = new File(this.pidFileName);
82-
FileOutputStream fileOutputStream = null;
80+
if (created.compareAndSet(false, true)) {
8381
try {
84-
File parent = file.getParentFile();
85-
if (parent != null) {
86-
parent.mkdirs();
87-
}
88-
fileOutputStream = new FileOutputStream(file);
89-
fileOutputStream.write(applicationPid.getBytes());
82+
new ApplicationPid().write(this.file);
9083
}
91-
catch (FileNotFoundException e) {
92-
logger.warn(String
93-
.format("Cannot create pid file %s !", this.pidFileName));
94-
}
95-
catch (Exception e) {
96-
logger.warn(String.format("Cannot write to pid file %s!",
97-
this.pidFileName));
98-
}
99-
finally {
100-
if (fileOutputStream != null) {
101-
try {
102-
fileOutputStream.close();
103-
}
104-
catch (IOException e) {
105-
logger.warn(String.format("Cannot close pid file %s!",
106-
this.pidFileName));
107-
}
108-
}
84+
catch (Exception ex) {
85+
logger.warn(String.format("Cannot create pid file %s", this.file));
10986
}
11087
}
11188
}
11289

113-
/**
114-
* Allow pid file to be re-written
115-
*/
116-
public static void reset() {
117-
pidFileCreated.set(false);
118-
}
119-
12090
public void setOrder(int order) {
12191
this.order = order;
12292
}
@@ -126,4 +96,10 @@ public int getOrder() {
12696
return this.order;
12797
}
12898

99+
/**
100+
* Reset the created flag for testing purposes.
101+
*/
102+
static void reset() {
103+
created.set(false);
104+
}
129105
}

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/system/ApplicationPidListenerTest.java

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,57 +17,47 @@
1717
package org.springframework.boot.actuate.system;
1818

1919
import java.io.File;
20+
import java.io.FileReader;
2021

2122
import org.junit.After;
23+
import org.junit.Before;
24+
import org.junit.Rule;
2225
import org.junit.Test;
26+
import org.junit.rules.TemporaryFolder;
2327
import org.springframework.boot.SpringApplication;
2428
import org.springframework.boot.context.event.ApplicationStartedEvent;
29+
import org.springframework.util.FileCopyUtils;
2530

26-
import static org.junit.Assert.assertTrue;
31+
import static org.hamcrest.Matchers.isEmptyString;
32+
import static org.hamcrest.Matchers.not;
33+
import static org.junit.Assert.assertThat;
2734

2835
/**
36+
* Tests fpr {@link ApplicationPidListener}.
37+
*
2938
* @author Jakub Kubrynski
3039
* @author Dave Syer
3140
*/
3241
public class ApplicationPidListenerTest {
3342

34-
private static final String[] NO_ARGS = {};
43+
private static final ApplicationStartedEvent EVENT = new ApplicationStartedEvent(
44+
new SpringApplication(), new String[] {});
3545

46+
@Rule
47+
public TemporaryFolder temporaryFolder = new TemporaryFolder();
48+
49+
@Before
3650
@After
37-
public void init() {
51+
public void resetListener() {
3852
ApplicationPidListener.reset();
3953
}
4054

4155
@Test
42-
public void shouldCreatePidFile() {
43-
// given
44-
String pidFileName = "test.pid";
45-
ApplicationPidListener sut = new ApplicationPidListener(pidFileName);
46-
47-
// when
48-
sut.onApplicationEvent(new ApplicationStartedEvent(new SpringApplication(),
49-
NO_ARGS));
50-
51-
// then
52-
File pidFile = new File(pidFileName);
53-
assertTrue(pidFile.exists());
54-
pidFile.delete();
55-
}
56-
57-
@Test
58-
public void shouldCreatePidFileParentDirectory() {
59-
// given
60-
String pidFileName = "target/pid/test.pid";
61-
ApplicationPidListener sut = new ApplicationPidListener(pidFileName);
62-
63-
// when
64-
sut.onApplicationEvent(new ApplicationStartedEvent(new SpringApplication(),
65-
NO_ARGS));
66-
67-
// then
68-
File pidFile = new File(pidFileName);
69-
assertTrue(pidFile.exists());
70-
pidFile.delete();
56+
public void createPidFile() throws Exception {
57+
File file = this.temporaryFolder.newFile();
58+
ApplicationPidListener listener = new ApplicationPidListener(file);
59+
listener.onApplicationEvent(EVENT);
60+
assertThat(FileCopyUtils.copyToString(new FileReader(file)), not(isEmptyString()));
7161
}
7262

7363
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2012-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
import java.io.File;
20+
import java.io.FileWriter;
21+
import java.io.IOException;
22+
import java.lang.management.ManagementFactory;
23+
24+
import org.springframework.util.Assert;
25+
import org.springframework.util.ObjectUtils;
26+
27+
/**
28+
* An application process ID.
29+
*
30+
* @author Phillip Webb
31+
*/
32+
public class ApplicationPid {
33+
34+
private final String pid;
35+
36+
public ApplicationPid() {
37+
this.pid = getPid();
38+
}
39+
40+
protected ApplicationPid(String pid) {
41+
this.pid = pid;
42+
}
43+
44+
private String getPid() {
45+
try {
46+
String jvmName = ManagementFactory.getRuntimeMXBean().getName();
47+
return jvmName.split("@")[0];
48+
}
49+
catch (Throwable ex) {
50+
return null;
51+
}
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return (this.pid == null ? "???" : this.pid);
57+
}
58+
59+
@Override
60+
public int hashCode() {
61+
return ObjectUtils.nullSafeHashCode(this.pid);
62+
}
63+
64+
@Override
65+
public boolean equals(Object obj) {
66+
if (obj == this) {
67+
return true;
68+
}
69+
if (obj != null && obj instanceof ApplicationPid) {
70+
return ObjectUtils.nullSafeEquals(this.pid, ((ApplicationPid) obj).pid);
71+
}
72+
return false;
73+
}
74+
75+
/**
76+
* Write the PID to the specified file.
77+
* @throws IllegalStateException if no PID is available.
78+
* @throws IOException if the file cannot be written
79+
*/
80+
public void write(File file) throws IOException {
81+
Assert.state(this.pid != null, "No PID available");
82+
createParentFolder(file);
83+
FileWriter writer = new FileWriter(file);
84+
try {
85+
writer.append(this.pid);
86+
}
87+
finally {
88+
writer.close();
89+
}
90+
}
91+
92+
private void createParentFolder(File file) {
93+
File parent = file.getParentFile();
94+
if (parent != null) {
95+
parent.mkdirs();
96+
}
97+
}
98+
99+
}

spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616

1717
package org.springframework.boot.logging;
1818

19+
import java.util.HashMap;
20+
import java.util.List;
21+
import java.util.Map;
22+
1923
import org.apache.commons.logging.Log;
2024
import org.apache.commons.logging.LogFactory;
25+
import org.springframework.boot.ApplicationPid;
2126
import org.springframework.boot.SpringApplication;
2227
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
2328
import org.springframework.boot.context.event.ApplicationStartedEvent;
24-
import org.springframework.boot.util.SystemUtils;
2529
import org.springframework.context.ApplicationEvent;
2630
import org.springframework.context.ApplicationListener;
2731
import org.springframework.context.event.SmartApplicationListener;
@@ -33,10 +37,6 @@
3337
import org.springframework.util.MultiValueMap;
3438
import org.springframework.util.ResourceUtils;
3539

36-
import java.util.HashMap;
37-
import java.util.List;
38-
import java.util.Map;
39-
4040
/**
4141
* An {@link ApplicationListener} that configures a logging framework depending on what it
4242
* finds on the classpath and in the {@link Environment}. If the environment contains a
@@ -124,13 +124,7 @@ public void onApplicationEvent(ApplicationEvent event) {
124124
}
125125
else {
126126
if (System.getProperty(PID_KEY) == null) {
127-
String applicationPid;
128-
try {
129-
applicationPid = SystemUtils.getApplicationPid();
130-
} catch (IllegalStateException e) {
131-
applicationPid = "????";
132-
}
133-
System.setProperty(PID_KEY, applicationPid);
127+
System.setProperty(PID_KEY, new ApplicationPid().toString());
134128
}
135129
LoggingSystem loggingSystem = LoggingSystem.get(ClassUtils
136130
.getDefaultClassLoader());

0 commit comments

Comments
 (0)