1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.launcher; 19 20 import java.io.File; 21 import java.io.InputStream; 22 import java.io.IOException; 23 24 /** 25 * A class for detecting if the parent JVM that launched this process has 26 * terminated. 27 * 28 * @author Patrick Luby 29 */ 30 public class ParentListener extends Thread { 31 32 //------------------------------------------------------------------ Fields 33 34 /** 35 * Cached heartbeat file. 36 */ 37 private File heartbeatFile = null; 38 39 //------------------------------------------------------------ Constructors 40 41 /** 42 * Validates and caches a lock file created by the parent JVM. 43 * 44 * @param path the lock file that the parent JVM has an open 45 * FileOutputStream 46 * @throws IOException if the heartbeat cannot be converted into a valid 47 * File object 48 */ 49 public ParentListener(String path) throws IOException { 50 51 if (path == null) 52 throw new IOException(); 53 54 // Make sure we have a valid path 55 heartbeatFile = new File(path); 56 heartbeatFile.getCanonicalPath(); 57 58 } 59 60 //----------------------------------------------------------------- Methods 61 62 /** 63 * Periodically check that the parent JVM has not terminated. On all 64 * platforms other than Windows, this method will check that System.in has 65 * not been closed. On Windows NT, 2000, and XP the lock file specified in 66 * the {@link #ParentListener(String)} constructor is monitored as reading 67 * System.in will block the entire process on Windows machines that use 68 * some versions of Unix shells such as MKS, etc. No monitoring is done 69 * on Window 95, 98, and ME. 70 */ 71 public void run() { 72 73 String osname = System.getProperty("os.name").toLowerCase(); 74 75 // We need to use file locking on Windows since reading System.in 76 // will block the entire process on some Windows machines. 77 if (osname.indexOf("windows") >= 0) { 78 79 // Do nothing if this is a Windows 9x platform since our file 80 // locking mechanism does not work on the early versions of 81 // Windows 82 if (osname.indexOf("nt") == -1 && osname.indexOf("2000") == -1 && osname.indexOf("xp") == -1) 83 return; 84 85 // If we can delete the heartbeatFile on Windows, it means that 86 // the parent JVM has closed its FileOutputStream on the file. 87 // Note that the parent JVM's stream should only be closed when 88 // it exits. 89 for ( ; ; ) { 90 if (heartbeatFile.delete()) 91 break; 92 // Wait awhile before we try again 93 yield(); 94 try { 95 sleep(5000); 96 } catch (Exception e) {} 97 } 98 99 } else { 100 101 // Cache System.in in case the application redirects 102 InputStream is = System.in; 103 int bytesAvailable = 0; 104 int bytesRead = 0; 105 byte[] buf = new byte[1024]; 106 try { 107 while (true) { 108 synchronized (is) { 109 // Mark the stream position so that other threads can 110 // reread the strea 111 is.mark(buf.length); 112 // Read one more byte than has already been read to 113 // force the stream to wait for input 114 bytesAvailable = is.available(); 115 if (bytesAvailable < buf.length) { 116 bytesRead = is.read(buf, 0, bytesAvailable + 1); 117 // Reset so that we "unread" the bytes that we read 118 is.reset(); 119 if (bytesRead == -1) 120 break; 121 } else { 122 // Make the buffer larger 123 if (buf.length < Integer.MAX_VALUE / 2) 124 buf = new byte[buf.length * 2]; 125 } 126 } 127 yield(); 128 } 129 } catch (IOException ioe) {} 130 131 } 132 133 // Clean up before exiting 134 if (heartbeatFile != null) 135 heartbeatFile.delete(); 136 137 // Exit this process since the parent JVM has exited 138 System.exit(0); 139 140 } 141 142 }