Java tutorial
/* * ///////////////////////////////////////////////////////////////////////////// * // This file is part of the "Hyrax Data Server" project. * // * // * // Copyright (c) 2013 OPeNDAP, Inc. * // Author: Nathan David Potter <ndp@opendap.org> * // * // This library is free software; you can redistribute it and/or * // modify it under the terms of the GNU Lesser General Public * // License as published by the Free Software Foundation; either * // version 2.1 of the License, or (at your option) any later version. * // * // This library is distributed in the hope that it will be useful, * // but WITHOUT ANY WARRANTY; without even the implied warranty of * // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * // Lesser General Public License for more details. * // * // You should have received a copy of the GNU Lesser General Public * // License along with this library; if not, write to the Free Software * // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * // * // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. * ///////////////////////////////////////////////////////////////////////////// */ package opendap.ppt; import opendap.xml.Util; import org.apache.commons.cli.*; import org.jdom.Document; import org.jdom.JDOMException; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; /** * OpenDAPClient is an object that handles the connection to, sending requests * to, and receiving response from a specified OpenDAP server running either * on this machine or another machine. * <p/> * Requests to the OpenDAP server can be taken in different ways by the * OpenDAPClient object. * <UL> * <LI>One request, ending with a semicolon.</LI> * <LI>Multiple requests, each ending with a semicolon.</LI> * <LI>Requests listed in a file, each request can span multiple lines in * the file and there can be more than one request per line. Each request * ends with a semicolon.</LI> * <LI>Interactive mode where the user inputs requests on the command line, * each ending with a semicolon, with multiple requests allowed per * line.</LI> * </UL> * <p/> * Response from the requests can sent to any File or OutputStream as * specified by using the setOutput methods. If no output is specified using * the setOutput methods thent he output is ignored. * <p/> * Thread safety of this object has not yet been determined. * * @author Patrick West <A * HREF="mailto:pwest@hao.ucar.edu">pwest@hao.ucar.edu</A> */ public class OPeNDAPClient { private int commandCount; private NewPPTClient _client = null; private OutputStream _stream = null; private boolean _isRunning; private Logger log = null; private String _id; /** * Creates a OpenDAPClient to handle OpenDAP requests. * <p/> * Sets the output of any responses from the OpenDAP server to null, * meaning that all responses will be thrown out. */ public OPeNDAPClient() { _stream = null; _isRunning = false; log = org.slf4j.LoggerFactory.getLogger(getClass()); commandCount = 0; } public String getID() { return _id; } public void setID(String ID) { _id = ID; } public int getCommandCount() { return commandCount; } public boolean isRunning() { return _isRunning; } public boolean isClosed() { return _client.isClosed(); } public boolean isConnected() { return _client.isConnected(); } public String showConnectionProperties() { return _client.showConnectionProperties(); } /** * Connect the OpenDAP client to the OpenDAP server. * <p/> * Connects to the OpenDAP server on the specified machine listening on * the specified port. * * @param hostStr The name of the host machine where the server is * running. * @param portVal The port on which the server on the host hostStr is * listening for requests. * @throws PPTException Thrown if unable to connect to the specified host * machine given the specified port. * @see String * @see PPTException */ public void startClient(String hostStr, int portVal) throws PPTException { _client = new NewPPTClient(hostStr, portVal); _client.initConnection(); _isRunning = true; } /** * Closes the connection to the OpeNDAP server and closes the output stream. * * @throws PPTException Thrown if unable to close the connection or close * the output stream. * machine given the specified port. * @see OutputStream * @see PPTException */ public void shutdownClient() throws PPTException { if (_client != null) _client.closeConnection(true); if (_stream != null) { try { _stream.close(); } catch (IOException e) { throw (new PPTException(e.getMessage())); } finally { _isRunning = false; } } _isRunning = false; } public int getChunkedReadBufferSize() { return _client.getChunkReadBufferSize(); } public void killClient() { _client.dieNow(); } /** * Sends a single OpeNDAP request ending in a semicolon (;) to the * OpeNDAP server. * <p/> * The response is written to the output stream if one is specified, * otherwise the output is ignored. * * @param cmd The OpenDAP request, ending in a semicolon, that is sent to * the OpenDAP server to handle. * * @param target The target OutputStream for the results of the command. * @param error The error OutputStream for errors returned by the server. * * @return True if successful, false if the server returned an error. * @throws PPTException Thrown if there is a problem sending the request * to the server or a problem receiving the response * from the server. * @see String * @see PPTException */ public boolean executeCommand(String cmd, OutputStream target, OutputStream error) throws PPTException { log.debug(cmd); _client.sendRequest(cmd); boolean success = _client.getResponse(target, error); commandCount++; return success; } /** * Sends a single XML request document. * <p/> * The response is written to the output stream if one is specified, * otherwise the output is ignored. * * @param request The XML request that is sent to * the BES to handle. * * * @return True if successful, false if the server returned an error. * @throws PPTException Thrown if there is a problem sending the request * to the server or a problem receiving the response * from the server. * @throws JDOMException if the response fails to parse. * @see String * @see PPTException */ /* public Document sendRequest( Document request) throws PPTException, JDOMException { _client.sendXMLRequest(request); Document doc = _client.getXMLResponse(); commandCount++; return doc; } */ /** * Sends a single XML request document. * <p/> * The response is written to the output stream if one is specified, * otherwise the output is ignored. * * @param request The XML request that is sent to * the BES to handle. * * @param target The target OutputStream for the results of the command. * @param error The error OutputStream for errors returned by the server. * * @return True if successful, false if the server returned an error. * @throws PPTException Thrown if there is a problem sending the request * to the server or a problem receiving the response * from the server. * @see String * @see PPTException */ public boolean sendRequest(Document request, OutputStream target, OutputStream error) throws PPTException { _client.sendXMLRequest(request); boolean val = _client.getResponse(target, error); commandCount++; return val; } /** * Execute each of the commands in the cmd_list, separated by a * semicolon. * <p/> * The response is written to the output stream if one is specified, * otherwise the output is ignored. * * @param cmd_list The list of OpenDAP requests, separated by semicolons * and ending in a semicolon, that will be sent to the * OpenDAP server to handle, one at a time. * @param target The target OutputStream for the results of the command. * @param error The error OutputStream for errors returned by the server. * * @return True if successful, false if the server returned an error. * @throws PPTException Thrown if there is a problem sending any of the * request to the server or a problem receiving any * of the response * s from the server. * @see String * @see PPTException */ public boolean executeCommands(String cmd_list, OutputStream target, OutputStream error) throws PPTException { boolean success = true; String cmds[] = cmd_list.split(";"); for (String cmd : cmds) { success = executeCommand(cmd + ";", target, error); if (!success) return success; } return success; } /** * Sends the requests listed in the specified file to the OpenDAP server, * each command ending with a semicolon. * <p/> * The requests do not have to be one per line but can span multiple * lines and there can be more than one command per line. * <p/> * The response is written to the output stream if one is specified, * otherwise the output is ignored. * * @param inputFile The file holding the list of OpenDAP requests, each * ending with a semicolon, that will be sent to the * OpenDAP server to handle. * @param target The target OutputStream for the results of the command. * @param error The error OutputStream for errors returned by the server. * * @return True if successful, false if the server returned an error. * @throws PPTException Thrown if there is a problem opening the file to * read, reading the requests from the file, sending * any of the requests to the server or a problem * receiving any of the responses from the server. * @see File * @see PPTException */ public boolean executeCommands(File inputFile, OutputStream target, OutputStream error) throws PPTException { BufferedReader reader; boolean success = true; try { reader = new BufferedReader(new FileReader(inputFile)); } catch (FileNotFoundException e) { throw (new PPTException(e.getMessage())); } try { String cmd = null; boolean done = false; while (!done && success) { String nextLine = reader.readLine(); if (nextLine == null) { if (cmd != null) { success = this.executeCommands(cmd, target, error); } done = true; } else { if (!nextLine.equals("")) { int i = nextLine.lastIndexOf(';'); if (i == -1) { if (cmd == null) { cmd = nextLine; } else { cmd += " " + nextLine; } } else { String sub = nextLine.substring(0, i); if (cmd == null) { cmd = sub; } else { cmd += " " + sub; } success = this.executeCommands(cmd, target, error); if (i == nextLine.length() || i == nextLine.length() - 1) { cmd = null; } else { cmd = nextLine.substring(i + 1, nextLine.length()); } } } } } } catch (IOException e) { throw (new PPTException(e.getMessage())); } finally { try { reader.close(); } catch (IOException e) { //Ignore the failure. } } return success; } /** * An interactive OpenDAP client that takes OpenDAP requests on the command * line. * <p/> * There can be more than one command per line, but commands can NOT span * multiple lines. The user will be prompted to enter a new OpenDAP request. * <p/> * OpenDAPClient: * <p/> * The response is written to the output stream if one is specified, * otherwise the output is ignored. * * @param out The target OutputStream for the results of the command. * @param err The error OutputStream for errors returned by the server. * * @throws PPTException Thrown if there is a problem sending any of the * requests to the server or a problem receiving any * of the responses from the server. * @see PPTException */ public void interact(OutputStream out, OutputStream err) throws PPTException { BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); try { boolean done = false; while (!done) { System.out.print("OPeNDAP> "); String fromUser = stdIn.readLine(); if (fromUser != null) { if (fromUser.compareTo("exit") == 0) { done = true; } else if (fromUser.compareTo("") == 0) { //continue; } else { this.executeCommands(fromUser, out, err); } } } } catch (Exception e) { _client.closeConnection(true); throw (new PPTException(e.getMessage(), e)); } } private static Options createCmdLineOptions() { Options options = new Options(); options.addOption("r", "reps", true, "Number of times to send the command. default: 1"); options.addOption("c", "maxCmds", true, "Number of commands to send before closing the BES connection and opening a new one. default: 1"); options.addOption("i", "besCmd", true, "Name of file containing the BES command to use. default: \"bes.cmd\""); options.addOption("p", "port", true, "Port number of BES. default: 10022"); options.addOption("n", "host", true, "Hostname of BES. default \"localhost\""); options.addOption("o", "outFile", true, "File into which to log BES responses. default: stdout"); options.addOption("e", "errFile", true, "File into which to log BES errors. default: stderr"); options.addOption("h", "help", false, "Print this usage statement."); return options; } /** * * @param args Command line arguments as defined by createCmdLineOptions() */ public static void main(String[] args) { Logger log = LoggerFactory.getLogger("OPeNDAPClient-MAIN"); String besCmdFileName = "bes.cmd"; String cmdString; int reps = 1; int maxCmds = 1; OutputStream besOut = System.out; OutputStream besErr = System.err; String hostName = "localhost"; int portNum = 10022; try { Options options = createCmdLineOptions(); CommandLineParser parser = new PosixParser(); CommandLine cmd = parser.parse(options, args); //--------------------------- // Command File if (cmd.hasOption("h")) { HelpFormatter formatter = new HelpFormatter(); formatter.setWidth(120); formatter.printHelp("OPeNDAPClient", options); return; } //--------------------------- // Command File if (cmd.hasOption("i")) { besCmdFileName = cmd.getOptionValue("i"); } log.info("BES Command Filename: " + besCmdFileName); Document cmdDoc = Util.getDocument(besCmdFileName); cmdString = new XMLOutputter(Format.getPrettyFormat()).outputString(cmdDoc); log.info("BES command has been read and parsed."); //--------------------------- // Command reps if (cmd.hasOption("r")) { reps = Integer.parseInt(cmd.getOptionValue("r")); } log.info("BES command will sent " + reps + " time" + (reps > 1 ? "s." : ".")); //--------------------------- // Max commands per client if (cmd.hasOption("c")) { maxCmds = Integer.parseInt(cmd.getOptionValue("c")); } log.info("The connection to the BES will be dropped and a new one opened after every " + maxCmds + " command" + (maxCmds > 1 ? "s." : ".")); //--------------------------- // BES output file if (cmd.hasOption("o")) { File besOutFile = new File(cmd.getOptionValue("o")); besOut = new FileOutputStream(besOutFile); log.info("BES output will be written to " + besOutFile.getAbsolutePath()); } else { log.info("BES output will be written to stdout"); } //--------------------------- // BES error file if (cmd.hasOption("e")) { File besErrFile = new File(cmd.getOptionValue("e")); besErr = new FileOutputStream(besErrFile); log.info("BES errors will be written to " + besErrFile.getAbsolutePath()); } else { log.info("BES errors will be written to stderr"); } //--------------------------- // Hostname if (cmd.hasOption("n")) { hostName = cmd.getOptionValue("n"); } //--------------------------- // Port Number if (cmd.hasOption("p")) { portNum = Integer.parseInt(cmd.getOptionValue("p")); } log.info("Using BES at " + hostName + ":" + portNum); } catch (Throwable t) { log.error("OUCH! Caught " + t.getClass().getName() + " Message: " + t.getMessage()); log.error("STACK TRACE: \n" + org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(t)); return; } int cmdsSent = 0; int connectionsMade = 0; try { log.info("-------------------------------------------------------------------------------"); log.info("-------------------------------------------------------------------------------"); log.info("Starting... \n\n\n"); OPeNDAPClient oc = new OPeNDAPClient(); oc.startClient(hostName, portNum); connectionsMade++; for (int r = 0; reps == 0 || r < reps; r++) { if (r > 0 && r % maxCmds == 0) { oc.shutdownClient(); boolean done = false; while (!done) { oc = new OPeNDAPClient(); try { oc.startClient(hostName, portNum); done = true; } catch (PPTEndOfStreamException e) { log.error( "Caught PPTEndOfStreamException - This BES connection is screwed. Retrying..."); } } connectionsMade++; } oc.executeCommand(cmdString, besOut, besErr); cmdsSent++; } } catch (Throwable t) { log.error("OUCH! Caught " + t.getClass().getName() + " Message: " + t.getMessage()); try { log.error("STACK TRACE: \n" + org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(t)); } catch (Throwable tt) { log.error("**** FAILED TO GENERATE STACK TRACE! ****"); } } finally { log.info("BES Command Filename: " + besCmdFileName); log.info("BES command will sent " + reps + " time" + (reps > 1 ? "s." : ".")); log.info("The connection to the BES will be dropped and a new one opened after every " + maxCmds + " command" + (maxCmds > 1 ? "s." : ".")); log.info("Using BES at " + hostName + ":" + portNum); log.info("Sent a total of " + cmdsSent + " command" + (connectionsMade > 1 ? "s." : ".")); log.info("Made a total of " + connectionsMade + " connection" + (connectionsMade > 1 ? "s" : "") + " to the BES."); } } }