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 new file mode 100644 index 00000000000..86fdd26bcbd --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/ApplicationPidListener.java @@ -0,0 +1,122 @@ +/* + * 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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.util.SystemUtils; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.SmartApplicationListener; +import org.springframework.core.Ordered; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An {@link org.springframework.context.ApplicationListener} that saves + * application PID into file + * + * @author Jakub Kubrynski + */ +public class ApplicationPidListener implements SmartApplicationListener { + + private static Class[] EVENT_TYPES = {ApplicationStartedEvent.class}; + + private static final String DEFAULT_PID_FILE_NAME = "application.pid"; + + private static final AtomicBoolean pidFileCreated = new AtomicBoolean(false); + + private int order = Ordered.HIGHEST_PRECEDENCE + 13; + + private final Log log = LogFactory.getLog(getClass()); + + private String pidFileName = DEFAULT_PID_FILE_NAME; + + @Override + public boolean supportsEventType(Class eventType) { + for (Class type : EVENT_TYPES) { + if (type.isAssignableFrom(eventType)) { + return true; + } + } + return false; + } + + @Override + public boolean supportsSourceType(Class sourceType) { + return SpringApplication.class.isAssignableFrom(sourceType); + } + + @Override + public void onApplicationEvent(ApplicationEvent applicationEvent) { + if (pidFileCreated.get()) { + return; + } + + String applicationPid; + try { + applicationPid = SystemUtils.getApplicationPid(); + } catch (IllegalStateException ignore) { + return; + } + + if (pidFileCreated.compareAndSet(false, true)) { + File file = new File(pidFileName); + FileOutputStream fileOutputStream = null; + try { + fileOutputStream = new FileOutputStream(file); + fileOutputStream.write(applicationPid.getBytes()); + } catch (FileNotFoundException e) { + log.warn(String.format("Cannot create pid file %s !", pidFileName)); + } catch (IOException e) { + log.warn(String.format("Cannot write to pid file %s!", pidFileName)); + } finally { + if (fileOutputStream != null) { + try { + fileOutputStream.close(); + } catch (IOException e) { + log.warn(String.format("Cannot close pid file %s!", pidFileName)); + } + } + } + } + } + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return order; + } + + /** + * Sets the pid file name. This file will contains current process id. + * + * @param pidFileName the name of file containing pid + */ + public void setPidFileName(String pidFileName) { + this.pidFileName = pidFileName; + } + +} 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 new file mode 100644 index 00000000000..a222ce377d2 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/system/ApplicationPidListenerTest.java @@ -0,0 +1,52 @@ +/* + * 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 org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.event.ApplicationStartedEvent; + +import java.io.File; + +import static org.junit.Assert.assertTrue; + +/** + * Created by jkubrynski@gmail.com / 2014-03-25 + */ +public class ApplicationPidListenerTest { + + private static final String[] NO_ARGS = {}; + + public static final String PID_FILE_NAME = "test.pid"; + + @Test + public void shouldCreatePidFile() { + //given + ApplicationPidListener sut = new ApplicationPidListener(); + sut.setPidFileName(PID_FILE_NAME); + + //when + sut.onApplicationEvent(new ApplicationStartedEvent( + new SpringApplication(), NO_ARGS)); + + //then + File pidFile = new File(PID_FILE_NAME); + assertTrue(pidFile.exists()); + pidFile.delete(); + } + +} 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 2ad90a87ba9..56dbb778195 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,16 +16,12 @@ package org.springframework.boot.logging; -import java.lang.management.ManagementFactory; -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.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; @@ -37,6 +33,10 @@ 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 @@ -69,11 +69,14 @@ import org.springframework.util.ResourceUtils; public class LoggingApplicationListener implements SmartApplicationListener { private static final Map ENVIRONMENT_SYSTEM_PROPERTY_MAPPING; + + public static final String PID_KEY = "PID"; + static { ENVIRONMENT_SYSTEM_PROPERTY_MAPPING = new HashMap(); ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("logging.file", "LOG_FILE"); ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("logging.path", "LOG_PATH"); - ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("PID", "PID"); + ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put(PID_KEY, PID_KEY); } private static MultiValueMap LOG_LEVEL_LOGGERS; @@ -120,8 +123,14 @@ public class LoggingApplicationListener implements SmartApplicationListener { .getClassLoader()); } else { - if (System.getProperty("PID") == null) { - System.setProperty("PID", getPid()); + if (System.getProperty(PID_KEY) == null) { + String applicationPid; + try { + applicationPid = SystemUtils.getApplicationPid(); + } catch (IllegalStateException e) { + applicationPid = "????"; + } + System.setProperty(PID_KEY, applicationPid); } LoggingSystem loggingSystem = LoggingSystem.get(ClassUtils .getDefaultClassLoader()); @@ -190,19 +199,6 @@ public class LoggingApplicationListener implements SmartApplicationListener { } } - private String getPid() { - try { - String name = ManagementFactory.getRuntimeMXBean().getName(); - if (name != null) { - return name.split("@")[0]; - } - } - catch (Throwable ex) { - // Ignore - } - return "????"; - } - public void setOrder(int order) { this.order = order; } 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 new file mode 100644 index 00000000000..a59680faed9 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/util/SystemUtils.java @@ -0,0 +1,63 @@ +/* + * 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 new file mode 100644 index 00000000000..9c4687b21c1 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/util/package-info.java @@ -0,0 +1,21 @@ +/* + * 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/util/SystemUtilsTest.java b/spring-boot/src/test/java/org/springframework/boot/util/SystemUtilsTest.java new file mode 100644 index 00000000000..ecb4c82aa53 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/util/SystemUtilsTest.java @@ -0,0 +1,38 @@ +/* + * 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); + } +}