001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2015
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 */
028package edu.wisc.ssec.mcidasv.servermanager;
029
030import java.io.InputStream;
031import java.io.InputStreamReader;
032
033import org.bushe.swing.event.EventBus;
034
035import edu.wisc.ssec.mcidasv.Constants;
036import edu.wisc.ssec.mcidasv.McIDASV;
037
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041/**
042 * Thread that actually execs the {@literal "mcservl"} process.
043 */
044public class AddeThread extends Thread {
045
046    private static final Logger logger = LoggerFactory.getLogger(AddeThread.class);
047
048    /** Mcserv events. */
049    public enum McservEvent { 
050        /** Mcservl is actively listening. */
051        ACTIVE("Local servers are running."),
052        /** Mcservl has died unexpectedly. */
053        DIED("Local servers quit unexpectedly."),
054        /** Mcservl started listening. */
055        STARTED("Local servers started listening on port %s."),
056        /** Mcservl has stopped listening. */
057        STOPPED("Local servers have been stopped.");
058
059        /** */
060        private final String message;
061
062        /**
063         * Creates an object that represents the status of a mcservl process.
064         * 
065         * @param message Should not be {@code null}.
066         */
067        McservEvent(final String message) {
068            this.message = message;
069        }
070
071        /**
072         * 
073         * 
074         * @return Format string associated with an event.
075         */
076        public String getMessage() {
077            return message;
078        }
079    };
080
081    /** */
082    Process proc;
083
084    /** Server manager. */
085    private final EntryStore entryStore;
086
087    /**
088     * Creates a thread that controls a mcservl process.
089     * 
090     * @param entryStore Server manager.
091     */
092    public AddeThread(final EntryStore entryStore) {
093        this.entryStore = entryStore;
094    }
095
096    public void run() {
097        StringBuilder err = new StringBuilder();
098        String[] cmds = entryStore.getAddeCommands();
099        String[] env = (McIDASV.isWindows()) ? entryStore.getWindowsAddeEnv() : entryStore.getUnixAddeEnv();
100
101        StringBuilder temp = new StringBuilder(512);
102        for (String cmd : cmds) {
103            temp.append(cmd).append(' ');
104        }
105        logger.info("Starting mcservl: {}", temp.toString());
106        temp = new StringBuilder(1024).append("{ ");
107        for (String e : env) {
108            temp.append(e).append(", ");
109        }
110        temp.append('}');
111        logger.debug("env={}", temp.toString());
112        temp = null;
113
114        try {
115            //start ADDE binary with "-p PORT" and set environment appropriately
116            proc = Runtime.getRuntime().exec(cmds, env);
117
118            //create thread for reading inputStream (process' stdout)
119            StreamReaderThread outThread = new StreamReaderThread(proc.getInputStream(), new StringBuilder());
120
121            //create thread for reading errorStream (process' stderr)
122            StreamReaderThread errThread = new StreamReaderThread(proc.getErrorStream(), err);
123
124            //start both threads
125            outThread.start();
126            errThread.start();
127
128            //wait for process to end
129            int result = proc.waitFor();
130
131            //finish reading whatever's left in the buffers
132            outThread.join();
133            errThread.join();
134
135            if (result != 0) {
136//                entryStore.stopLocalServer(entryStore.getRestarting());
137                entryStore.stopLocalServer();
138                String errString = err.toString();
139
140                // If the server couldn't start for a known reason, try again on another port
141                //  Retry up to 10 times
142                if (((result == 35584) || (errString.indexOf("Error binding to port") >= 0)) &&
143                    (Integer.parseInt(EntryStore.getLocalPort()) < (Integer.parseInt(Constants.LOCAL_ADDE_PORT) + 30)))
144                {
145                    EntryStore.setLocalPort(EntryStore.nextLocalPort());
146//                    entryStore.startLocalServer(entryStore.getRestarting());
147                    entryStore.startLocalServer();
148                }
149            }
150        } catch (InterruptedException e) {
151            McservEvent type = McservEvent.DIED;
152//            if (entryStore.getRestarting()) {
153                type = McservEvent.STARTED;
154//            }
155            EventBus.publish(type);
156        } catch (Exception e) {
157            EventBus.publish(McservEvent.DIED);
158        }
159    }
160
161    /**
162     * 
163     */
164    public void stopProcess() {
165        proc.destroy();
166    }
167
168//    /**
169//     * 
170//     */
171//    public String toString() {
172//        return String.format("[AddeThread@%x: ADDE_ENV=%s, ADDE_COMMANDS=%s]", hashCode(), ADDE_ENV, ADDE_COMMANDS);
173//    }
174
175    /**
176     * Thread to read the stderr and stdout of mcservl
177     */
178    private static class StreamReaderThread extends Thread {
179        /** */
180        private final StringBuilder mOut;
181
182        /** */
183        private final InputStreamReader mIn;
184
185        /** */
186        public StreamReaderThread(final InputStream in, final StringBuilder out) {
187            mOut = out;
188            mIn = new InputStreamReader(in);
189        }
190
191        /** */
192        public void run() {
193            int ch;
194            try {
195                while (-1 != (ch = mIn.read())) {
196                    mOut.append((char)ch);
197                }
198            } catch (Exception e) {
199                mOut.append("\nRead error: "+e.getMessage());
200            }
201        }
202    }
203}