View Javadoc

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 }