Repository: karaf Updated Branches: refs/heads/karaf-4.1.x cedef2979 -> e02e0fccc
[KARAF-3825] Use pidfile to get status and stop Karaf when shutdown port is not available Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/e02e0fcc Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/e02e0fcc Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/e02e0fcc Branch: refs/heads/karaf-4.1.x Commit: e02e0fcccba92191cf65e1ea46ccc3ea0847d1d1 Parents: cedef29 Author: Jean-Baptiste Onofré <[email protected]> Authored: Mon Jul 24 15:49:49 2017 +0200 Committer: Jean-Baptiste Onofré <[email protected]> Committed: Mon Jul 24 15:51:12 2017 +0200 ---------------------------------------------------------------------- main/pom.xml | 1 + .../main/java/org/apache/karaf/jpm/Process.java | 47 ++++++ .../org/apache/karaf/jpm/ProcessBuilder.java | 60 ++++++++ .../apache/karaf/jpm/ProcessBuilderFactory.java | 25 ++++ .../jpm/impl/ProcessBuilderFactoryImpl.java | 27 ++++ .../karaf/jpm/impl/ProcessBuilderImpl.java | 48 ++++++ .../org/apache/karaf/jpm/impl/ProcessImpl.java | 149 +++++++++++++++++++ .../org/apache/karaf/jpm/impl/ScriptUtils.java | 123 +++++++++++++++ .../main/java/org/apache/karaf/main/Status.java | 23 ++- .../main/java/org/apache/karaf/main/Stop.java | 23 ++- 10 files changed, 522 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/pom.xml ---------------------------------------------------------------------- diff --git a/main/pom.xml b/main/pom.xml index 5aceaf7..c710a40 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -101,6 +101,7 @@ </Export-Package> <Private-Package> org.apache.karaf.main*, + org.apache.karaf.jpm*, org.apache.felix.utils.properties;-split-package:=merge-first, org.apache.karaf.util.config, org.apache.karaf.util.maven, http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/Process.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/Process.java b/main/src/main/java/org/apache/karaf/jpm/Process.java new file mode 100644 index 0000000..4f04d1a --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/Process.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm; + +import java.io.IOException; +import java.io.Serializable; + +/** + * Interface representing a process + */ +public interface Process extends Serializable { + + /** + * Retrieves the PID of the process + * @return the pid + */ + int getPid(); + + /** + * Check if this process is still running + * @return <code>true</code> if the process is running + * @throws IOException if an error occurs + */ + boolean isRunning() throws IOException; + + /** + * Destroy the process. + * + * @throws IOException If an error occurs. + */ + void destroy() throws IOException; + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/ProcessBuilder.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/ProcessBuilder.java b/main/src/main/java/org/apache/karaf/jpm/ProcessBuilder.java new file mode 100644 index 0000000..7b81bf2 --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/ProcessBuilder.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm; + +import java.io.File; +import java.io.IOException; + +/** + * Interface used to create new processes. + */ +public interface ProcessBuilder { + + /** + * Specify the current directory to run the command from. + * + * @param dir The directory to run the command from. + * @return The {@link ProcessBuilder} instance. + */ + ProcessBuilder directory(File dir); + + /** + * Set the command to execute. + * + * @param command The command to execute. + * @return The {@link ProcessBuilder} instance. + */ + ProcessBuilder command(String command); + + /** + * Create and start the process. + * + * @return The process that has been started. + * @throws IOException If the process can not be created. + */ + org.apache.karaf.jpm.Process start() throws IOException; + + /** + * Attach to an existing process. + * + * @param pid The process PID to attach. + * @return The process that has been attached. + * @throws IOException if the process can not be attached to. + */ + org.apache.karaf.jpm.Process attach(int pid) throws IOException; + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/ProcessBuilderFactory.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/ProcessBuilderFactory.java b/main/src/main/java/org/apache/karaf/jpm/ProcessBuilderFactory.java new file mode 100644 index 0000000..baa563b --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/ProcessBuilderFactory.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm; + + +/** + * Factory for process builders. + */ +public interface ProcessBuilderFactory { + ProcessBuilder newBuilder(); +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderFactoryImpl.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderFactoryImpl.java b/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderFactoryImpl.java new file mode 100644 index 0000000..12a6905 --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderFactoryImpl.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm.impl; + +import org.apache.karaf.jpm.ProcessBuilder; +import org.apache.karaf.jpm.ProcessBuilderFactory; + +public class ProcessBuilderFactoryImpl implements ProcessBuilderFactory { + + public ProcessBuilder newBuilder() { + return new ProcessBuilderImpl(); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderImpl.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderImpl.java b/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderImpl.java new file mode 100644 index 0000000..4beee74 --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/impl/ProcessBuilderImpl.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm.impl; + +import org.apache.karaf.jpm.Process; +import org.apache.karaf.jpm.ProcessBuilder; + +import java.io.File; +import java.io.IOException; + + +public class ProcessBuilderImpl implements ProcessBuilder { + + private File dir; + private String command; + + public ProcessBuilder directory(File dir) { + this.dir = dir; + return this; + } + + public ProcessBuilder command(String command) { + this.command = command; + return this; + } + + public Process start() throws IOException { + return ProcessImpl.create(dir, command); + } + + public Process attach(int pid) throws IOException { + return ProcessImpl.attach(pid); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/impl/ProcessImpl.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/impl/ProcessImpl.java b/main/src/main/java/org/apache/karaf/jpm/impl/ProcessImpl.java new file mode 100644 index 0000000..2db2778 --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/impl/ProcessImpl.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm.impl; + +import org.apache.karaf.jpm.Process; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; + +public class ProcessImpl implements Process { + + /** + * + */ + private static final long serialVersionUID = -8140632422386086507L; + + private int pid; + //private File input; + //private File output; + //private File error; + + public ProcessImpl(int pid/*, File input, File output, File error*/) { + this.pid = pid; + //this.input = input; + //this.output = output; + //this.error = error; + } + + public int getPid() { + return pid; + } + + public boolean isRunning() throws IOException { + if (ScriptUtils.isWindows()) { + Map<String, String> props = new HashMap<>(); + props.put("${pid}", Integer.toString(pid)); + int ret = ScriptUtils.execute("running", props); + return ret == 0; + } else { + try { + java.lang.Process process = new java.lang.ProcessBuilder("ps", "-p", Integer.toString(pid)).start(); + BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream())); + r.readLine(); // skip headers + String s = r.readLine(); + boolean running = s != null && s.length() > 0; + process.waitFor(); + return running; + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + + public void destroy() throws IOException { + int ret; + if (ScriptUtils.isWindows()) { + Map<String, String> props = new HashMap<>(); + props.put("${pid}", Integer.toString(pid)); + ret = ScriptUtils.execute("destroy", props); + } else { + ret = ScriptUtils.executeProcess(new ProcessBuilder("kill", "-9", Integer.toString(pid))); + } + if (ret != 0) { + throw new IOException("Unable to destroy process, it may already be terminated"); + } + } + + /* + public OutputStream getInputStream() throws FileNotFoundException { + return new FileOutputStream(input); + } + + public InputStream getOutputStream() throws FileNotFoundException { + return new FileInputStream(output); + } + + public InputStream getErrorStream() throws FileNotFoundException { + return new FileInputStream(error); + } + */ + + public int waitFor() throws InterruptedException { + return 0; + } + + public int exitValue() { + return 0; + } + + public static Process create(File dir, String command) throws IOException { + //File input = File.createTempFile("jpm.", ".input"); + //File output = File.createTempFile("jpm.", ".output"); + //File error = File.createTempFile("jpm.", ".error"); + File pidFile = File.createTempFile("jpm.", ".pid"); + try { + Map<String, String> props = new HashMap<>(); + //props.put("${in.file}", input.getCanonicalPath()); + //props.put("${out.file}", output.getCanonicalPath()); + //props.put("${err.file}", error.getCanonicalPath()); + props.put("${pid.file}", pidFile.getCanonicalPath()); + props.put("${dir}", dir != null ? dir.getCanonicalPath() : ""); + if (ScriptUtils.isWindows()) { + command = command.replaceAll("\"", "\"\""); + } + props.put("${command}", command); + int ret = ScriptUtils.execute("start", props); + if (ret != 0) { + throw new IOException("Unable to create process (error code: " + ret + ")"); + } + int pid = readPid(pidFile); + return new ProcessImpl(pid/*, input, output, error*/); + } finally { + pidFile.delete(); + } + } + + public static Process attach(int pid) throws IOException { + return new ProcessImpl(pid); + } + + private static int readPid(File pidFile) throws IOException { + InputStream is = new FileInputStream(pidFile); + try { + BufferedReader r = new BufferedReader(new InputStreamReader(is)); + String pidString = r.readLine(); + return Integer.valueOf(pidString); + } finally { + try { + is.close(); + } catch (IOException e) {} + } + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/jpm/impl/ScriptUtils.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/jpm/impl/ScriptUtils.java b/main/src/main/java/org/apache/karaf/jpm/impl/ScriptUtils.java new file mode 100644 index 0000000..bb58f81 --- /dev/null +++ b/main/src/main/java/org/apache/karaf/jpm/impl/ScriptUtils.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.karaf.jpm.impl; + +import java.io.*; +import java.util.Map; +import java.util.Scanner; + +public class ScriptUtils { + + public static int execute(String name, Map<String, String> props) throws IOException { + File script = File.createTempFile("jpm.", ".script"); + try { + if (isWindows()) { + String res = "windows/" + name + ".vbs"; + ScriptUtils.copyFilteredResource(res, script, props); + return executeProcess(new ProcessBuilder("cscript", + "/NOLOGO", + "//E:vbs", + script.getCanonicalPath())); + } else { + String res = "unix/" + name + ".sh"; + ScriptUtils.copyFilteredResource(res, script, props); + return executeProcess(new ProcessBuilder("/bin/sh", + script.getCanonicalPath())); + } + } finally { + script.delete(); + } + } + + public static int executeProcess(ProcessBuilder builder) throws IOException { + try { + Process process = builder.start(); + return process.waitFor(); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + public static void copyFilteredResource(String resource, File outFile, Map<String, String> props) throws IOException { + InputStream is = null; + try { + is = ScriptUtils.class.getResourceAsStream(resource); + // Read it line at a time so that we can use the platform line ending when we write it out. + PrintStream out = new PrintStream(new FileOutputStream(outFile)); + try { + Scanner scanner = new Scanner(is); + while (scanner.hasNextLine() ) { + String line = scanner.nextLine(); + line = filter(line, props); + out.println(line); + } + scanner.close(); + } finally { + safeClose(out); + } + } finally { + safeClose(is); + } + } + + private static void safeClose(InputStream is) throws IOException { + if (is == null) { + return; + } + try { + is.close(); + } catch (Throwable ignore) { + } + } + + private static void safeClose(OutputStream is) throws IOException { + if (is == null) { + return; + } + try { + is.close(); + } catch (Throwable ignore) { + } + } + + private static String filter(String line, Map<String, String> props) { + for (Map.Entry<String, String> i : props.entrySet()) { + int p1 = line.indexOf(i.getKey()); + if( p1 >= 0 ) { + String l1 = line.substring(0, p1); + String l2 = line.substring(p1+i.getKey().length()); + line = l1+i.getValue()+l2; + } + } + return line; + } + + private static final boolean windows; + + static { + windows = System.getProperty("os.name").toLowerCase().indexOf("windows") != -1; + } + + public static boolean isWindows() { + return windows; + } + + public static String getJavaCommandPath() throws IOException { + return new File(System.getProperty("java.home"), isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath(); + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/main/Status.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/main/Status.java b/main/src/main/java/org/apache/karaf/main/Status.java index 4ac8b1a..ca311ac 100644 --- a/main/src/main/java/org/apache/karaf/main/Status.java +++ b/main/src/main/java/org/apache/karaf/main/Status.java @@ -18,6 +18,8 @@ */ package org.apache.karaf.main; +import org.apache.karaf.jpm.impl.ProcessBuilderFactoryImpl; + import java.io.*; import java.net.ConnectException; import java.net.Socket; @@ -67,8 +69,16 @@ public class Status { } } } else { - System.err.println("Unable to find port..."); - System.exit(2); + // using the pid file + int pid = getPidFromPidFile(config.pidFile); + org.apache.karaf.jpm.Process process = new ProcessBuilderFactoryImpl().newBuilder().attach(pid); + if (process.isRunning()) { + System.out.println("Running ... (pid " + pid + ")"); + System.exit(0); + } else { + System.out.println("Not Running ..."); + System.exit(1); + } } } @@ -81,4 +91,13 @@ public class Status { return port; } + private static int getPidFromPidFile(String pidFile) throws IOException { + int pid; + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(pidFile))); + String pidString = r.readLine(); + pid = Integer.parseInt(pidString); + r.close(); + return pid; + } + } http://git-wip-us.apache.org/repos/asf/karaf/blob/e02e0fcc/main/src/main/java/org/apache/karaf/main/Stop.java ---------------------------------------------------------------------- diff --git a/main/src/main/java/org/apache/karaf/main/Stop.java b/main/src/main/java/org/apache/karaf/main/Stop.java index d6af16d..d9433be 100644 --- a/main/src/main/java/org/apache/karaf/main/Stop.java +++ b/main/src/main/java/org/apache/karaf/main/Stop.java @@ -18,6 +18,8 @@ */ package org.apache.karaf.main; +import org.apache.karaf.jpm.impl.ProcessBuilderFactoryImpl; + import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -66,8 +68,16 @@ public class Stop { } } } else { - System.err.println("Unable to find port..."); - System.exit(2); + // using the pid file + int pid = getPidFromPidFile(config.pidFile); + org.apache.karaf.jpm.Process process = new ProcessBuilderFactoryImpl().newBuilder().attach(pid); + if (process.isRunning()) { + process.destroy(); + System.exit(0); + } else { + System.out.println("Not Running ..."); + System.exit(1); + } } } @@ -81,4 +91,13 @@ public class Stop { return port; } + private static int getPidFromPidFile(String pidFile) throws IOException { + int pid; + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(pidFile))); + String pidString = r.readLine(); + pid = Integer.parseInt(pidString); + r.close(); + return pid; + } + }
