001    /*
002     * $Id: AddeThread.java,v 1.11 2012/04/23 15:32:55 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    package edu.wisc.ssec.mcidasv.servermanager;
031    
032    import java.io.InputStream;
033    import java.io.InputStreamReader;
034    
035    import org.bushe.swing.event.EventBus;
036    
037    import edu.wisc.ssec.mcidasv.Constants;
038    import edu.wisc.ssec.mcidasv.McIDASV;
039    
040    /**
041     * Thread that actually execs mcservl
042     */
043    public class AddeThread extends Thread {
044    
045        /** Mcserv events. */
046        public enum McservEvent { 
047            /** Mcservl is actively listening. */
048            ACTIVE("Local servers are running."),
049            /** Mcservl has died unexpectedly. */
050            DIED("Local servers quit unexpectedly."),
051            /** Mcservl started listening. */
052            STARTED("Local servers started listening on port %s."),
053            /** Mcservl has stopped listening. */
054            STOPPED("Local servers have been stopped.");
055    
056            /** */
057            private final String message;
058    
059            /**
060             * Creates an object that represents the status of a mcservl process.
061             * 
062             * @param message Should not be {@code null}.
063             */
064            private McservEvent(final String message) {
065                this.message = message;
066            }
067    
068            /**
069             * 
070             * 
071             * @return Format string associated with an event.
072             */
073            public String getMessage() {
074                return message;
075            }
076        };
077    
078        /** */
079        Process proc;
080    
081        /** Server manager. */
082        private final EntryStore entryStore;
083    
084        /**
085         * Creates a thread that controls a mcservl process.
086         * 
087         * @param entryStore Server manager.
088         */
089        public AddeThread(final EntryStore entryStore) {
090            this.entryStore = entryStore;
091        }
092    
093        public void run() {
094            StringBuilder err = new StringBuilder();
095            String[] cmds = entryStore.getAddeCommands();
096            String[] env = (McIDASV.isWindows()) ? entryStore.getWindowsAddeEnv() : entryStore.getUnixAddeEnv();
097            try {
098                //start ADDE binary with "-p PORT" and set environment appropriately
099                proc = Runtime.getRuntime().exec(cmds, env);
100    
101                //create thread for reading inputStream (process' stdout)
102                StreamReaderThread outThread = new StreamReaderThread(proc.getInputStream(), new StringBuilder());
103    
104                //create thread for reading errorStream (process' stderr)
105                StreamReaderThread errThread = new StreamReaderThread(proc.getErrorStream(), err);
106    
107                //start both threads
108                outThread.start();
109                errThread.start();
110    
111                //wait for process to end
112                int result = proc.waitFor();
113    
114                //finish reading whatever's left in the buffers
115                outThread.join();
116                errThread.join();
117    
118                if (result != 0) {
119    //                entryStore.stopLocalServer(entryStore.getRestarting());
120                    entryStore.stopLocalServer();
121                    String errString = err.toString();
122    
123                    // If the server couldn't start for a known reason, try again on another port
124                    //  Retry up to 10 times
125                    if ((result==35584 || errString.indexOf("Error binding to port") >= 0) &&
126                            Integer.parseInt(EntryStore.getLocalPort()) < Integer.parseInt(Constants.LOCAL_ADDE_PORT) + 30) {
127                        EntryStore.setLocalPort(EntryStore.nextLocalPort());
128    //                    entryStore.startLocalServer(entryStore.getRestarting());
129                        entryStore.startLocalServer();
130                    }
131                }
132            } catch (InterruptedException e) {
133                McservEvent type = McservEvent.DIED;
134    //            if (entryStore.getRestarting()) {
135                    type = McservEvent.STARTED;
136    //            }
137                EventBus.publish(type);
138            } catch (Exception e) {
139                EventBus.publish(McservEvent.DIED);
140            }
141        }
142    
143        /**
144         * 
145         */
146        public void stopProcess() {
147            proc.destroy();
148        }
149    
150    //    /**
151    //     * 
152    //     */
153    //    public String toString() {
154    //        return String.format("[AddeThread@%x: ADDE_ENV=%s, ADDE_COMMANDS=%s]", hashCode(), ADDE_ENV, ADDE_COMMANDS);
155    //    }
156    
157        /**
158         * Thread to read the stderr and stdout of mcservl
159         */
160        private static class StreamReaderThread extends Thread {
161            /** */
162            private final StringBuilder mOut;
163    
164            /** */
165            private final InputStreamReader mIn;
166    
167            /** */
168            public StreamReaderThread(final InputStream in, final StringBuilder out) {
169                mOut = out;
170                mIn = new InputStreamReader(in);
171            }
172    
173            /** */
174            public void run() {
175                int ch;
176                try {
177                    while (-1 != (ch = mIn.read())) {
178                        mOut.append((char)ch);
179                    }
180                } catch (Exception e) {
181                    mOut.append("\nRead error: "+e.getMessage());
182                }
183            }
184        }
185    }