/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.tools; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.URLEncoder; import java.util.ArrayList; import java.util.StringTokenizer; import com.slamd.common.Constants; import com.slamd.jobs.JSSEBlindTrustSocketFactory; import com.unboundid.util.Base64; /** * This class defines a command-line utility that may be used to export job data * from the SLAMD server. It does this by communicating with the SLAMD * administrative interface over HTTP to ensure that all appropriate validity * checking is performed, and that any required authentication is honored. * * * @author Neil A. Wilson */ public class ExportData { // Variables used processing. private boolean exportAll = false; private boolean exportUnclassified = false; private boolean useSSL = false; private boolean verboseMode = false; private int slamdPort = 8080; private String authID = null; private String authPW = null; private String eol = Constants.EOL; private String filePath = null; private String postURI = "/slamd"; private String slamdHost = "127.0.0.1"; private String[] realFolderExports = new String[0]; private String[] virtualFolderExports = new String[0]; /** * Invokes the constructor and provides it with the command-line arguments. * * @param args The command-line arguments provided to this program. */ public static void main(String[] args) { ExportData exporter = new ExportData(args); if (exporter.sendRequest()) { System.out.println("The export completed successfully."); } else { System.out.println("An error occurred that prevented the export from " + "completing successfully."); } } /** * Parses the provided command-line arguments. * * @param args The command-line arguments provided to this program. */ public ExportData(String[] args) { ArrayList<String> realList = new ArrayList<String>(); ArrayList<String> virtualList = new ArrayList<String>(); for (int i=0; i< args.length; i++) { if (args[i].equals("-h")) { slamdHost = args[++i]; } else if (args[i].equals("-p")) { slamdPort = Integer.parseInt(args[++i]); } else if (args[i].equals("-u")) { postURI = args[++i]; } else if (args[i].equals("-S")) { useSSL = true; } else if (args[i].equals("-A")) { authID = args[++i]; } else if (args[i].equals("-P")) { authPW = args[++i]; } else if (args[i].equals("-f")) { filePath = args[++i]; } else if (args[i].equals("-a")) { exportAll = true; } else if (args[i].equals("-U")) { exportUnclassified = true; } else if (args[i].equals("-R")) { realList.add(args[++i]); } else if (args[i].equals("-V")) { virtualList.add(args[++i]); } else if (args[i].equals("-v")) { verboseMode = true; } else if (args[i].equals("-H")) { displayUsage(); System.exit(0); } else { System.err.println("Unrecognized argument \"" + args[i] + '"'); displayUsage(); System.exit(1); } } // Make sure that the file path has been provided. if (filePath == null) { System.err.println("No export file path provided (use -f)"); displayUsage(); System.exit(1); } // Convert the folder lists into arrays. realFolderExports = new String[realList.size()]; virtualFolderExports = new String[virtualList.size()]; realList.toArray(realFolderExports); virtualList.toArray(virtualFolderExports); // Make sure that at the user specified something to export if ((! exportAll) && (! exportUnclassified) && (realFolderExports.length == 0) && (virtualFolderExports.length == 0)) { System.err.println("No data specified to include in the export"); displayUsage(); System.exit(1); } } /** * Sends the request to the SLAMD server and parses the response. * * @return <CODE>true</CODE> if the data was exported properly, or * <CODE>false</CODE> if not. */ public boolean sendRequest() { // Construct the data string to POST to the server. StringBuilder buf = new StringBuilder(); addParameter(buf, Constants.SERVLET_PARAM_SECTION, Constants.SERVLET_SECTION_JOB); addParameter(buf, Constants.SERVLET_PARAM_SUBSECTION, Constants.SERVLET_SECTION_JOB_EXPORT_JOB_DATA); if (exportAll) { addParameter(buf, Constants.SERVLET_PARAM_EXPORT_CHOICE, Constants.EXPORT_CHOICE_ALL); } else { addParameter(buf, Constants.SERVLET_PARAM_EXPORT_CHOICE, Constants.EXPORT_CHOICE_SELECTED); if (exportUnclassified) { addParameter(buf, Constants.SERVLET_PARAM_EXPORT_UNCLASSIFIED, Constants.SERVLET_PARAM_EXPORT_UNCLASSIFIED); } for (int i=0; i < realFolderExports.length; i++) { addParameter(buf, Constants.SERVLET_PARAM_EXPORT_REAL_FOLDER, realFolderExports[i]); } for (int i=0; i < virtualFolderExports.length; i++) { addParameter(buf, Constants.SERVLET_PARAM_EXPORT_VIRTUAL_FOLDER, virtualFolderExports[i]); } } addParameter(buf, Constants.SERVLET_PARAM_SUBMIT, Constants.SUBMIT_STRING_EXPORT); // Establish a connection to the SLAMD server's admin interface. Socket socket = null; BufferedReader reader = null; BufferedWriter writer = null; try { if (useSSL) { debug("Establishing an SSL-based connection to " + slamdHost + ':' + slamdPort); JSSEBlindTrustSocketFactory socketFactory = new JSSEBlindTrustSocketFactory(); socket = socketFactory.makeSocket(slamdHost, slamdPort); } else { debug("Establishing a connection to " + slamdHost + ':' + slamdPort); socket = new Socket(slamdHost, slamdPort); } reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (Exception e) { if (verboseMode) { e.printStackTrace(); } System.err.println("ERROR: Unable to connect to SLAMD server's " + "admin interface: " + e); return false; } // Write the HTTP request to the server. try { writeLine(writer, "POST " + postURI + " HTTP/1.1"); writeLine(writer, "Host: " + slamdHost); writeLine(writer, "Connection: close"); writeLine(writer, "Content-Type: application/x-www-form-urlencoded"); if ((authID != null) && (authID.length() > 0) && (authPW != null) && (authPW.length() > 0)) { String authStr = authID + ':' + authPW; byte[] authBytes = authStr.getBytes("UTF-8"); writeLine(writer, "Authorization: Basic " + Base64.encode(authBytes)); } writeLine(writer, "Content-Length: " + buf.length()); writeLine(writer, ""); writeLine(writer, buf.toString()); writer.flush(); } catch (IOException ioe) { if (verboseMode) { ioe.printStackTrace(); } System.err.println("ERROR: Unable to POST to server -- " + ioe); return false; } // Read the HTTP response header from the server. try { String line; while (((line = readLine(reader)) != null) && (line.length() > 0)) { String lowerLine = line.toLowerCase(); if (lowerLine.startsWith("http/1.1")) { StringTokenizer st = new StringTokenizer(line, " "); String protocol = st.nextToken(); String resultCode = st.nextToken(); String message = line.substring(line.indexOf(resultCode) + resultCode.length()).trim(); if (! resultCode.equals("200")) { System.err.println("ERROR: Unexpected HTTP result code " + resultCode); System.err.println("Message was " + message); return false; } } } } catch (Exception e) { if (verboseMode) { e.printStackTrace(); } System.err.println("ERROR: Unable to read response header -- " + e); return false; } try { BufferedWriter exportWriter = new BufferedWriter(new FileWriter(filePath, false)); int bytesRead = 0; int eolLength = System.getProperty("line.separator").length(); int nextTarget = 1024 * 1024; String line; while ((line = reader.readLine()) != null) { exportWriter.write(line); exportWriter.newLine(); bytesRead += line.length() + eolLength; if (bytesRead >= nextTarget) { System.out.println(bytesRead + " bytes exported"); nextTarget += (1024 * 1024); } } System.out.println("Export complete -- " + bytesRead + " bytes exported"); exportWriter.close(); reader.close(); writer.close(); } catch (IOException ioe) { if (verboseMode) { ioe.printStackTrace(); } } return true; } /** * Adds information about the specified parameter to the list of parameters in * a form that may be submitted in an HTTP POST. * * @param buffer The string buffer to which the parameter information should * be written. * @param name The name of the parameter to add. * @param value The value of the parameter to add. */ private void addParameter(StringBuilder buffer, String name, String value) { if (buffer.length() > 0) { buffer.append('&'); } buffer.append(name); buffer.append('='); try { buffer.append(URLEncoder.encode(value, "UTF-8")); } catch (UnsupportedEncodingException uee) { if (verboseMode) { uee.printStackTrace(); } System.err.println("ERROR: Unable to encode \"" + value + "\" as UTF-8 data."); } } /** * Writes the provided line to the provided writer. * * @param writer The writer to which the line should be written. * @param line The line to be written. * * @throws IOException If a problem occurs while writing the line. */ private void writeLine(BufferedWriter writer, String line) throws IOException { writer.write(line + eol); debug("CLIENT: " + line); } /** * Reads a line of output from the provided reader. * * @param reader The reader from which to read the output. * * @return The line read from the reader. * * @throws IOException If a problem occurs while reading the line. */ private String readLine(BufferedReader reader) throws IOException { String line = reader.readLine(); if (line != null) { debug("SERVER: " + line); } return line; } /** * Prints the provided message if the program is operating in verbose mode. * * @param message The message to be printed. */ public void debug(String message) { if (verboseMode) { System.out.println(message); } } /** * Displays usage information for this program. */ public static void displayUsage() { String eol = Constants.EOL; System.out.println( "USAGE: java -cp {classpath} ExportData {options}" + eol + " where {options} include:" + eol + "-f {filePath} -- The path to the data file to be created" + eol + "-a -- Indicates that all data in the server should be" + eol + " included in the export" + eol + "-U -- Indicates that unclassified job data should be" + eol + " included in the export" + eol + "-R {folderName} -- The name of a real job folder to include in the" + eol + " export (may be used multiple times)" + eol + "-V {folderName} -- The name of a virtual job folder to include in the" + eol + " export (may be used multiple times)" + eol + "-F {folderName} -- The name of a folder to include in the export" + eol + "-h {slamdHost} -- The address of the SLAMD server" + eol + "-p {slamdPort} -- The port of the SLAMD server's admin interface" + eol + "-A {authID} -- The username to use to authenticate to the server" + eol + "-P {authPW} -- The password to use to authenticate to the server" + eol + "-u {uri} -- The URI to which the request should be sent" + eol + "-S -- Communicate with the SLAMD server over SSL" + eol + "-v -- Enable verbose mode" + eol + "-H -- Display usage information and exit" ); } }