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 }