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.system;
018
019import java.io.File;
020import java.io.FileNotFoundException;
021import java.io.FileWriter;
022import java.io.IOException;
023import java.lang.management.ManagementFactory;
024import java.nio.file.Files;
025import java.nio.file.attribute.PosixFilePermission;
026import java.util.Set;
027
028import org.springframework.util.Assert;
029import org.springframework.util.ObjectUtils;
030
031/**
032 * An application process ID.
033 *
034 * @author Phillip Webb
035 * @since 2.0.0
036 */
037public class ApplicationPid {
038
039        private static final PosixFilePermission[] WRITE_PERMISSIONS = {
040                        PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_WRITE,
041                        PosixFilePermission.OTHERS_WRITE };
042
043        private final String pid;
044
045        public ApplicationPid() {
046                this.pid = getPid();
047        }
048
049        protected ApplicationPid(String pid) {
050                this.pid = pid;
051        }
052
053        private String getPid() {
054                try {
055                        String jvmName = ManagementFactory.getRuntimeMXBean().getName();
056                        return jvmName.split("@")[0];
057                }
058                catch (Throwable ex) {
059                        return null;
060                }
061        }
062
063        @Override
064        public boolean equals(Object obj) {
065                if (obj == this) {
066                        return true;
067                }
068                if (obj != null && obj instanceof ApplicationPid) {
069                        return ObjectUtils.nullSafeEquals(this.pid, ((ApplicationPid) obj).pid);
070                }
071                return false;
072        }
073
074        @Override
075        public int hashCode() {
076                return ObjectUtils.nullSafeHashCode(this.pid);
077        }
078
079        @Override
080        public String toString() {
081                return (this.pid != null) ? this.pid : "???";
082        }
083
084        /**
085         * Write the PID to the specified file.
086         * @param file the PID file
087         * @throws IllegalStateException if no PID is available.
088         * @throws IOException if the file cannot be written
089         */
090        public void write(File file) throws IOException {
091                Assert.state(this.pid != null, "No PID available");
092                createParentFolder(file);
093                if (file.exists()) {
094                        assertCanOverwrite(file);
095                }
096                try (FileWriter writer = new FileWriter(file)) {
097                        writer.append(this.pid);
098                }
099        }
100
101        private void createParentFolder(File file) {
102                File parent = file.getParentFile();
103                if (parent != null) {
104                        parent.mkdirs();
105                }
106        }
107
108        private void assertCanOverwrite(File file) throws IOException {
109                if (!file.canWrite() || !canWritePosixFile(file)) {
110                        throw new FileNotFoundException(file.toString() + " (permission denied)");
111                }
112        }
113
114        private boolean canWritePosixFile(File file) throws IOException {
115                try {
116                        Set<PosixFilePermission> permissions = Files
117                                        .getPosixFilePermissions(file.toPath());
118                        for (PosixFilePermission permission : WRITE_PERMISSIONS) {
119                                if (permissions.contains(permission)) {
120                                        return true;
121                                }
122                        }
123                        return false;
124                }
125                catch (UnsupportedOperationException ex) {
126                        // Assume that we can
127                        return true;
128                }
129        }
130
131}