com.ibm.jaql.util.shell.JaqlShellArguments.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.jaql.util.shell.JaqlShellArguments.java

Source

/*
 * Copyright (C) IBM Corp. 2008.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.ibm.jaql.util.shell;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.List;

import org.apache.commons.cli2.CommandLine;
import org.apache.commons.cli2.DisplaySetting;
import org.apache.commons.cli2.Group;
import org.apache.commons.cli2.Option;
import org.apache.commons.cli2.OptionException;
import org.apache.commons.cli2.builder.ArgumentBuilder;
import org.apache.commons.cli2.builder.DefaultOptionBuilder;
import org.apache.commons.cli2.builder.GroupBuilder;
import org.apache.commons.cli2.commandline.Parser;
import org.apache.commons.cli2.util.HelpFormatter;
import org.apache.commons.cli2.validation.NumberValidator;
import org.apache.commons.lang.StringUtils;

import com.ibm.jaql.io.Adapter;
import com.ibm.jaql.io.OutputAdapter;
import com.ibm.jaql.json.type.BufferedJsonRecord;
import com.ibm.jaql.json.type.JsonRecord;
import com.ibm.jaql.json.type.JsonString;
import com.ibm.jaql.lang.JaqlQuery;
import com.ibm.jaql.lang.util.JaqlUtil;
import com.ibm.jaql.util.EchoedReader;

/** Parser for arguments of JaqlShell. */
public class JaqlShellArguments {
    static final String DEFAULT_HDFS_DIR = "/tmp/jaql/dfs";
    static final int DEFAULT_NUM_NODES = 1;
    static final boolean DEFAULT_BATCH_MODE = false;

    boolean useExistingCluster = false;
    String[] jars = new String[0];
    OutputAdapter outputAdapter;
    String[] searchPath = null;
    String hdfsDir = DEFAULT_HDFS_DIR;
    int numNodes = DEFAULT_NUM_NODES;
    ChainedReader chainedIn = new ChainedReader();
    boolean batchMode = DEFAULT_BATCH_MODE;
    OutputAdapter logAdapter;

    private JaqlShellArguments() {
    };

    /**
     * Enable or disable the output to console. It is in effect only if running
     * Jaql shell in batch mode with mini clusters. It means to disable console
     * print in batch mode. It is used to disable console print in
     * <code>MiniHBaseCluster</code>, <code>MiniHBaseCluster</code> and
     * <code>MiniDFSCluster</code>. Among these classes,
     * <code>MiniDFSCluster</code> prints startup information to stderr.
     * 
     * @param enable <code>true</code> to enable output to stdout and stderr;
     *          <code>false</code> to disable to output to stdout and stderr.
     * 
     * @see ConsolePrintEnabler
     */
    public void enableConsolePrint(boolean enable) {
        if (batchMode && !useExistingCluster)
            ConsolePrintEnabler.enable(enable);
    }

    /**
     * Adds an input stream to the chained input stream.
     * 
     * @param in An input stream
     */
    private void addInputStream(InputStream in) {
        try {
            Reader reader = new InputStreamReader(in, "UTF-8");
            if (!batchMode) {
                Writer writer = new OutputStreamWriter(System.out, "UTF-8");
                reader = new EchoedReader(reader, writer);
            }
            chainedIn.add(reader);
        } catch (UnsupportedEncodingException e) {
            JaqlUtil.rethrow(e);
        }
    }

    /**
     * Adds STDIN to the chained input stream.
     */
    private void addStdin() {
        try {
            chainedIn.add(new InputStreamReader(System.in, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            JaqlUtil.rethrow(e);
        }
    }

    @SuppressWarnings("unchecked")
    static JaqlShellArguments parseArgs(String... args) {
        // option builders
        final DefaultOptionBuilder obuilder = new DefaultOptionBuilder();
        final ArgumentBuilder abuilder = new ArgumentBuilder();
        final GroupBuilder gbuilder = new GroupBuilder();

        // create standard options
        Option optHelp = obuilder.withShortName("h").withShortName("?").withLongName("help")
                .withDescription("print this message").create();

        Option optJars = obuilder.withShortName("j").withLongName("jars")
                .withDescription(
                        "comma-separated list of jar files to include user defined expressions or data stores")
                .withArgument(abuilder.withName("args").withMinimum(1).withMaximum(1).create()).create();

        Option optSearchPath = obuilder.withShortName("jp").withLongName("jaql-path")
                .withDescription("colon seperated list of all search path entries")
                .withArgument(abuilder.withName("args").withMinimum(1).withMaximum(1).create()).create();

        Option optBatch = obuilder.withShortName("b").withLongName("batch")
                .withDescription("run in batch mode (i.e., do not read from stdin)").create();

        Option optOutOptions = obuilder.withShortName("o").withLongName("outoptions")
                .withDescription("output options: json, del and xml or an output IO descriptor. "
                        + "This option is ignored when not running in batch mode.")
                .withArgument(abuilder.withName("outoptions").withMinimum(1).withMaximum(1).create()).create();

        Option optEval = obuilder.withShortName("e").withLongName("eval")
                .withDescription("evaluate Jaql expression")
                .withArgument(abuilder.withName("expr").withMinimum(1).withMaximum(1).create()).create();

        // create mini-cluster options
        Option optCluster = obuilder.withShortName("c").withLongName("cluster")
                .withDescription("use existing cluster (i.e., do not launch a mini-cluster)").create();

        Option optNumNodes = obuilder.withShortName("n").withLongName("no-nodes")
                .withDescription("mini-cluster option: number of nodes to spawn")
                .withArgument(abuilder.withName("arg").withMinimum(1).withMaximum(1)
                        .withValidator(NumberValidator.getIntegerInstance()).create())
                .create();

        Option optDir = obuilder.withShortName("d").withLongName("hdfs-dir")
                .withDescription("mini-cluster option: root HDFs directory")
                .withArgument(abuilder.withName("arg").withMinimum(1).withMaximum(1).create()).create();

        Group clusterOptions = gbuilder.withName("Cluster options").withOption(optCluster).withOption(optDir)
                .withOption(optNumNodes).create();

        // create input files option
        Option optInputFiles = abuilder.withName("file").withDescription("list of input files").withMinimum(0)
                .create();

        Option optLog = obuilder.withShortName("l").withLongName("log")
                .withDescription("log options: json, del and xml or an output IO descriptor. ")
                .withArgument(abuilder.withName("arg").withMinimum(1).withMaximum(1).create()).create();

        // combine all options
        Group options = gbuilder.withName("options").withOption(optHelp).withOption(optJars)
                .withOption(optSearchPath).withOption(optBatch).withOption(optLog).withOption(optOutOptions)
                .withOption(optEval).withOption(optInputFiles).withOption(clusterOptions).create();

        // parse and print help if necessary
        CommandLine cl;
        try {
            Parser parser = new Parser();
            parser.setGroup(options);
            cl = parser.parse(args);
        } catch (OptionException e) {
            printHelpAndExit(e, null, options);
            return null;
        }
        if (cl.hasOption(optHelp)) {
            printHelpAndExit(null, options);
        }

        // validate arguments
        JaqlShellArguments result = new JaqlShellArguments();

        // mini-cluster options         
        if (cl.hasOption(optCluster)) {
            result.useExistingCluster = true;
        }
        if (cl.hasOption(optDir)) {
            if (result.useExistingCluster) {
                printHelpAndExit("Options " + optCluster.getPreferredName() + " and " + optDir.getPreferredName()
                        + " are mutually exclusive", options);
            }
            result.hdfsDir = (String) cl.getValue(optDir);
        }
        if (cl.hasOption(optNumNodes)) {
            if (result.useExistingCluster) {
                printHelpAndExit("Options " + optCluster.getPreferredName() + " and "
                        + optNumNodes.getPreferredName() + " are mutually exclusive", options);
            }
            result.numNodes = ((Number) cl.getValue(optNumNodes)).intValue();
        }

        // jar files
        if (cl.hasOption(optJars)) {
            result.jars = ((String) cl.getValue(optJars)).split(",");
            for (String jar : result.jars) {
                if (!new File(jar).exists()) {
                    printHelpAndExit("Jar file " + jar + " not found", options);
                }
            }
        }

        // search path directories
        if (cl.hasOption(optSearchPath)) {
            result.searchPath = ((String) cl.getValue(optSearchPath)).split(":");
            for (String dir : result.searchPath) {
                if (!new File(dir).exists() || !new File(dir).isDirectory()) {
                    printHelpAndExit("Search-path entry " + dir + " not found or is no directory", options);
                }
            }
        }

        if (cl.hasOption(optBatch)) {
            result.batchMode = true;
            if (cl.hasOption(optOutOptions)) {
                String format = (String) cl.getValue(optOutOptions);
                try {
                    result.outputAdapter = getOutputAdapter(format);
                } catch (Exception e) {
                    printHelpAndExit(e,
                            "\"" + format + "\" is neither a valid output format nor a valid IO descriptor",
                            options);
                }
            }
        }

        // input
        if (cl.hasOption(optEval)) {
            String eval = (String) cl.getValue(optEval);
            if (!eval.endsWith(";"))
                eval += ";";
            result.addInputStream(new ByteArrayInputStream(eval.getBytes()));
        }
        if (cl.hasOption(optInputFiles)) {
            List<String> files = (List<String>) cl.getValues(optInputFiles);
            for (String file : files) {
                try {
                    result.addInputStream(new FileInputStream(file));
                } catch (FileNotFoundException e) {
                    printHelpAndExit(e, "Input file " + file + " not found", options);
                }
            }
        }

        // error log
        if (cl.hasOption(optLog)) {
            String path = (String) cl.getValue(optLog);
            try {
                BufferedJsonRecord logFD = new BufferedJsonRecord();
                logFD.add(Adapter.TYPE_NAME, new JsonString("local"));
                logFD.add(Adapter.LOCATION_NAME, new JsonString(path));
                OutputAdapter oa = (OutputAdapter) JaqlUtil.getAdapterStore().output.getAdapter(logFD);
                result.logAdapter = oa;
            } catch (Exception e) {
                printHelpAndExit(e, "\"" + path + "\" invalid", options);
            }
        }

        if (!result.batchMode) {
            result.addStdin();
        }

        return result;
    }

    private static OutputAdapter getOutputAdapter(String outOptions) throws Exception {
        if (outOptions.equals(""))
            outOptions = "jsonStream";

        // special casing here, to enable shortcut notation
        if (outOptions.equals("json") || outOptions.equals("del") || outOptions.equals("xml")) {
            outOptions += "Stream";
        }

        // construct json record for json, del and xml
        if (StringUtils.isAlpha(outOptions)) {
            outOptions = "{" + Adapter.TYPE_NAME + ": '" + outOptions + "'}";
        }
        outOptions += ";";

        // get output adapter
        JaqlQuery query = new JaqlQuery();
        query.setQueryString(outOptions);
        JsonRecord options = (JsonRecord) query.evaluate();
        OutputAdapter oa = (OutputAdapter) JaqlUtil.getAdapterStore().output.getAdapter(options);
        if (oa == null)
            throw new NullPointerException();
        else
            return oa;
    }

    private static void printHelpAndExit(String message, Group options) {
        printHelpAndExit(null, message, options);
    }

    @SuppressWarnings("unchecked")
    private static void printHelpAndExit(Exception e, String message, Group options) {
        if (message != null)
            System.err.println(message);
        if (e != null)
            e.printStackTrace();
        HelpFormatter hf = new HelpFormatter();
        hf.setShellCommand("jaqlshell");
        hf.setGroup(options);
        hf.getFullUsageSettings().remove(DisplaySetting.DISPLAY_GROUP_EXPANDED);
        hf.getLineUsageSettings().add(DisplaySetting.DISPLAY_ARGUMENT_BRACKETED);
        hf.print();
        hf.printHelp();
        System.exit(1);
    }
}