001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.launcher; 019 020import java.io.File; 021import java.io.InputStream; 022import java.io.IOException; 023 024/** 025 * A class for detecting if the parent JVM that launched this process has 026 * terminated. 027 * 028 * @author Patrick Luby 029 */ 030public class ParentListener extends Thread { 031 032 //------------------------------------------------------------------ Fields 033 034 /** 035 * Cached heartbeat file. 036 */ 037 private File heartbeatFile = null; 038 039 //------------------------------------------------------------ Constructors 040 041 /** 042 * Validates and caches a lock file created by the parent JVM. 043 * 044 * @param path the lock file that the parent JVM has an open 045 * FileOutputStream 046 * @throws IOException if the heartbeat cannot be converted into a valid 047 * File object 048 */ 049 public ParentListener(String path) throws IOException { 050 051 if (path == null) 052 throw new IOException(); 053 054 // Make sure we have a valid path 055 heartbeatFile = new File(path); 056 heartbeatFile.getCanonicalPath(); 057 058 } 059 060 //----------------------------------------------------------------- Methods 061 062 /** 063 * Periodically check that the parent JVM has not terminated. On all 064 * platforms other than Windows, this method will check that System.in has 065 * not been closed. On Windows NT, 2000, and XP the lock file specified in 066 * the {@link #ParentListener(String)} constructor is monitored as reading 067 * System.in will block the entire process on Windows machines that use 068 * some versions of Unix shells such as MKS, etc. No monitoring is done 069 * on Window 95, 98, and ME. 070 */ 071 public void run() { 072 073 String osname = System.getProperty("os.name").toLowerCase(); 074 075 // We need to use file locking on Windows since reading System.in 076 // will block the entire process on some Windows machines. 077 if (osname.indexOf("windows") >= 0) { 078 079 // Do nothing if this is a Windows 9x platform since our file 080 // locking mechanism does not work on the early versions of 081 // Windows 082 if (osname.indexOf("nt") == -1 && osname.indexOf("2000") == -1 && osname.indexOf("xp") == -1) 083 return; 084 085 // If we can delete the heartbeatFile on Windows, it means that 086 // the parent JVM has closed its FileOutputStream on the file. 087 // Note that the parent JVM's stream should only be closed when 088 // it exits. 089 for ( ; ; ) { 090 if (heartbeatFile.delete()) 091 break; 092 // Wait awhile before we try again 093 yield(); 094 try { 095 sleep(5000); 096 } catch (Exception e) {} 097 } 098 099 } 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}