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    }