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