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
018 package org.apache.commons.launcher;
019
020 import java.io.File;
021 import java.io.InputStream;
022 import 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 */
030 public 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 }