All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jxta.impl.shell.ShellApp Maven / Gradle / Ivy

Go to download

The JXTA Shell is a sample application that demonstrates key concepts of JXTA JXSE. The JXTA Shell enables users to interact with the JXTA platform through a command-line interpreter. Much like the UNIX® shell, the JXTA Shell is useful for accessing and managing core platform objects (peers, groups, pipes), debugging communications problems, checking the status of peers and/or peer groups, and communicating with other JXTA services and applications. As in UNIX®, the JXTA shell allows users to "pipe" shell commands together connecting commands "on-the-fly." Pipes in Project JXTA are useful for collecting information at one peer and perhaps processing the command at another.

The newest version!
/*
 * Copyright (c) 2001 Sun Microsystems, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *       Sun Microsystems, Inc. for Project JXTA."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact Project JXTA at http://www.jxta.org.
 *
 * 5. Products derived from this software may not be called "JXTA",
 *    nor may "JXTA" appear in their name, without prior written
 *    permission of Sun.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL SUN MICROSYSTEMS OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of Project JXTA.  For more
 * information on Project JXTA, please see
 * .
 *
 * This license is based on the BSD license adopted by the Apache Foundation.
 *
 * $Id: ShellApp.java,v 1.44 2007/02/09 23:12:41 hamada Exp $
 */


package net.jxta.impl.shell;

import net.jxta.document.Advertisement;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.endpoint.TextMessageElement;
import net.jxta.id.ID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.pipe.InputPipe;
import net.jxta.pipe.OutputPipe;
import net.jxta.platform.Application;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * This class is the base class any JXTA Shell application must extend.
 */
public abstract class ShellApp implements Application {

    /**
     * The command is still running.
     */
    public final static int appSpawned = -1;

    /**
     * The command completed successfully.
     */
    public final static int appNoError = 0;

    /**
     * An error occurred resulting from incorrect or missing parameters.
     */
    public final static int appParamError = 1;

    /**
     * Something bad happened. Don't know what it means, don't care why it
     * happened.
     */
    public final static int appMiscError = Integer.MAX_VALUE;

    /**
     * The default peergroup associated with this app.
     */
    private PeerGroup group = null;

    /**
     * The assigned id for this application, if any.
     */
    private ID id = null;

    /**
     * The implementation advertisement for this application, if any.
     */
    private Advertisement implAdv = null;

    /**
     * The environment for this application. Copied from the host shell.
     */
    private ShellEnv env = null;

    /**
     * The "stdin" input pipe for this command.
     */
    private InputPipe inputPipe = null;

    /**
     * The "stdout" output pipe for this command.
     */
    private OutputPipe outputPipe = null;

    /**
     * The console input. Differs from the stdin if stdin has been
     * redirected. consin is not normally redirected.
     */
    private InputPipe consin = null;

    /**
     * The console output. Differs from the stdout if stdout has been
     * redirected. consout is not normally redirected.
     */
    private OutputPipe consout = null;

    /**
     * if the result is a shell object then store it using this name.
     */
    private String returnVarName = null;

    /**
     * Has this app begun running?
     */
    protected volatile boolean started = false;

    /**
     * Has this app in the process of quitting?
     */
    protected volatile boolean stopped = false;

    /**
     * If this thread is enabled, then this is the root thread of another
     * command which this command can use to determine when it should finish.
     */
    private Thread dependsOn = null;

    /**
     * private buffer for input pipe
     */
    private List buffered = new ArrayList();

    /**
     * private buffer for console input pipe.
     */
    private List consbuffer = new ArrayList();

    /**
     * {@inheritDoc}
     */
    public final void init(PeerGroup pg, ID assignedID, Advertisement impl) {
        setGroup(pg);
        setAssignedID(assignedID);
        setImplAdvertisement(impl);

        started = true;
    }

    /**
     * {@inheritDoc}
     */
    public void stopApp() {
        stopped = true;
    }

    /**
     * Return a String containing a single line description of the function of
     * this ShellApp.
     */
    public String getDescription() {
        return "No description available for this ShellApp";
    }

    /**
     * Print to the stdout a full description of the functionality of this
     * Shell command.
     */
    public void help() {
        println("No help available for this ShellApp");
    }

    // Accessor Methods

    /**
     * Return the group in which this ShellApp is executing. Most ShellApps
     * should instead use the value of the "stdgroup" environment variable.
     *
     * @return the current peer group.
     */
    protected PeerGroup getGroup() {
        return group;
    }

    /**
     * Set the group will be the default group for this ShellApp. Most ShellApps
     * should instead use the value of the "stdgroup" environment variable.
     */
    private PeerGroup setGroup(PeerGroup g) {
        PeerGroup old = group;
        group = g;
        return old;
    }

    /**
     * Return the assignedID for this ShellApp if any.
     *
     * @return the assigned ID for this ShellApp or null if none
     *         was specified.
     */
    protected final ID getAssignedID() {
        return this.id;
    }


    /**
     * Set the assignedID for this ShellApp
     *
     * @param id The assigned ID for this application
     */
    private void setAssignedID(ID id) {
        this.id = id;
    }

    /**
     * Return the implementation advertisement for this ShellApp, if any.
     *
     * @return implementation Advertisement for this ShellApp or null
     *         if none was specified.
     */
    protected final Advertisement getImplAdvertisement() {
        return this.implAdv;
    }

    /**
     * Sets the implementation advertisement for this ShellApp.
     *
     * @param adv The implementation advertisement for this ShellApp.
     */
    private Advertisement setImplAdvertisement(Advertisement adv) {
        Advertisement old = this.implAdv;
        this.implAdv = adv;
        return old;
    }

    /**
     * Return a modifiable instance of this ShellApp's environment.
     *
     * @return This ShellApp's environment.
     */
    protected final ShellEnv getEnv() {
        return env;
    }

    /**
     * Sets this ShellApp's environment.
     *
     * @return The environment for this ShellApp to use.
     */
    protected final ShellEnv setEnv(ShellEnv e) {
        ShellEnv old = env;
        env = e;
        return old;
    }

    protected final Thread setJoinedThread(Thread dependsOn) {
        Thread old = this.dependsOn;
        this.dependsOn = dependsOn;
        return old;
    }

    /**
     * Return the standard input pipe.
     *
     * @return the standard input pipe.
     */
    protected final InputPipe getInputPipe() {
        return inputPipe;
    }

    /**
     * Set the standard input pipe to the provided pipe. The previous pipe is
     * returned.
     *
     * @param ip the new standard input pipe.
     * @return the old standard input pipe.
     */
    protected final InputPipe setInputPipe(InputPipe ip) {
        InputPipe old = inputPipe;

        inputPipe = ip;

        buffered.clear();

        return old;
    }

    /**
     * Return the standard output pipe.
     *
     * @return the standard output pipe.
     */
    protected final OutputPipe getOutputPipe() {
        return outputPipe;
    }

    /**
     * Set the standard output pipe to the provided pipe. The previous pipe is
     * returned.
     *
     * @param op the new standard output pipe.
     * @return the old standard output pipe.
     */
    protected final OutputPipe setOutputPipe(OutputPipe op) {
        OutputPipe old = outputPipe;
        outputPipe = op;
        return old;
    }

    /**
     * Return the console input pipe.
     *
     * @return the console input pipe.
     */
    protected final InputPipe getInputConsPipe() {
        return consin;
    }

    /**
     * Set the console input pipe to the provided pipe. The previous pipe is
     * returned.
     *
     * @param ip the new input output pipe.
     * @return the old input output pipe.
     */
    protected final InputPipe setInputConsPipe(InputPipe ip) {
        InputPipe old = consin;

        consin = ip;

        consbuffer.clear();

        return old;
    }

    /**
     * Return the console output pipe.
     *
     * @return the console output pipe.
     */
    protected final OutputPipe getOutputConsPipe() {
        return consout;
    }

    /**
     * Set the console output pipe to the provided pipe. The previous pipe is
     * returned.
     *
     * @param op the new console output pipe.
     * @return the old console output pipe.
     */
    protected final OutputPipe setOutputConsPipe(OutputPipe op) {
        OutputPipe old = consout;
        consout = op;
        return old;
    }

    /**
     * Return the name of the environment into which this ShellApp should
     * return any environment variable result.
     *
     * @return the name of the environment variable or null if no name has been
     *         set.
     */
    protected final String getReturnVariable() {
        return returnVarName;
    }

    /**
     * Set the name of the environment into which this ShellApp should
     * return any environment variable result.
     *
     * @param varName name of the environment variable.
     */
    protected final void setReturnVariable(String varName) {
        returnVarName = varName;
    }

    // IO Operations

    /**
     * Print to standard output
     *
     * @param line the line to print
     */
    protected final void print(String line) {
        if (null == outputPipe) {
            return;
        }

        pipePrint(outputPipe, line);
    }

    /**
     * Print to standard output appending a newline.
     *
     * @param line the line to print
     */
    protected final void println(String line) {
        if (null == outputPipe) {
            return;
        }

        pipePrintln(outputPipe, line);
    }

    /**
     * Poll for input on standard input.
     *
     * @return an input line or null if no input is available.
     */
    protected final String pollInput() throws IOException {
        if (null == inputPipe) {
            return null;
        }

        if (inputPipe == consin)
            return consPollInput();
        else
            return pipePollInput(inputPipe, buffered);
    }

    /**
     * Wait for input on standard input.
     *
     * @return an input line or null if standard input has been
     *         closed.
     */
    protected final String waitForInput() throws IOException {
        if (null == inputPipe) {
            return null;
        }

        if (inputPipe == consin)
            return consWaitForInput();
        else
            return pipeWaitForInput(inputPipe, buffered, true);
    }

    /**
     * Print to the console output.
     *
     * @param line the line to print
     */
    protected final void consprint(String line) {
        if (null == consout) {
            return;
        }

        pipePrint(consout, line);
    }

    /**
     * Print to console output appending a newline.
     *
     * @param line the line to print
     */
    protected final void consprintln(String line) {
        if (null == consout) {
            return;
        }

        pipePrintln(consout, line);
    }

    /**
     * Poll for input on console input.
     *
     * @return an input line or null  if no input is available.
     */
    protected final String consPollInput() throws IOException {
        if (null == consin) {
            return null;
        }

        return pipePollInput(consin, consbuffer);
    }

    /**
     * Wait for input on console input.
     *
     * @return an input line or null if standard input has been
     *         closed.
     */
    protected final String consWaitForInput() throws IOException {
        if (null == consin) {
            return null;
        }

        String msg = pipeWaitForInput(consin, consbuffer, false);

        // create an EOT
        if (msg != null) {
            if (-1 != msg.indexOf('\u0004')) {
                msg = null;
            }
        }

        return msg;
    }

    protected final String getCmdShortName() {
        return getCmdShortName(this.getClass());
    }

    public static String getCmdShortName(Class clas) {
        String cmdClass = clas.getName();
        String cmdName;

        int lastDot = cmdClass.lastIndexOf(".");

        if (-1 != lastDot) {
            int secondLast = cmdClass.lastIndexOf(".", lastDot - 1);

            String cmdPackage = "";
            if (-1 != secondLast) {
                cmdPackage = cmdClass.substring(secondLast + 1, lastDot);
            }
            cmdName = cmdClass.substring(lastDot + 1);

            if (!cmdName.equals(cmdPackage)) {
                cmdName = cmdPackage + "." + cmdName;
            }
        } else {
            cmdName = cmdClass;
        }

        return cmdName;
    }

    /**
     * print a message from the specified class on the specified pipe.
     *
     * @param clas    the class which is printing the message
     * @param consout the pipe on which to print the message
     * @param message the message to print.
     */
    public static void consoleMessage(Class clas, OutputPipe consout, String message) {

        pipePrintln(consout, "# " + getCmdShortName(clas) + " - " + message);
    }

    /**
     * Print a message on the console. The message will identify this ShellApp
     * as the source of the message.
     *
     * @param message The message to print.
     */
    protected final void consoleMessage(String message) {
        consoleMessage(getClass(), consout, message);
    }

    /**
     * Print a stack trace to the console with the specified annotation. The
     * message will identify this ShellApp as the source of the exception.
     *
     * @param clas       the class which is printing the message
     * @param consout    the pipe on which to print the message
     * @param annotation Explanation or annotation for the stack trace.
     * @param failure    the stack trace.
     */
    public static void printStackTrace(Class clas, OutputPipe consout, String annotation, Throwable failure) {
        consoleMessage(clas, consout, annotation);

        StringWriter theStackTrace = new StringWriter();
        failure.printStackTrace(new PrintWriter(theStackTrace));
        pipePrintln(consout, theStackTrace.toString());
    }

    /**
     * Print a stack trace to the console with the specified annotation. The
     * message will identify this ShellApp as the source of the exception.
     *
     * @param annotation Explanation or annotation for the stack trace.
     * @param failure    the stack trace.
     */
    protected final void printStackTrace(String annotation, Throwable failure) {
        printStackTrace(getClass(), consout, annotation, failure);
    }

    /**
     * Load a shell application.
     *
     * @param returnvar The env variable in which the command should put its result.
     * @param appName   The name of the application to load.
     * @param env       The enviroment to use.
     */
    protected ShellApp loadApp(String returnvar, String appName, ShellEnv env) {
        ShellApp app;

        try {
            app = new ShellCmds(env).getInstance(appName);
        } catch (Exception failed) {
            printStackTrace("Exception in command : " + appName, failed);
            return null;
        }

        if (null == app) {
            return null;
        }

        // Set up the default environment and pipes

        app.setEnv(env);

        app.setJoinedThread(dependsOn);

        ShellObject obj = env.get("consin");
        if (null != obj) {
            app.setInputConsPipe((InputPipe) obj.getObject());
        }

        obj = env.get("consout");
        if (null != obj) {
            app.setOutputConsPipe((OutputPipe) obj.getObject());
        }

        obj = env.get("stdin");
        if (null != obj) {
            app.setInputPipe((InputPipe) obj.getObject());
        }

        obj = env.get("stdout");
        if (null != obj) {
            app.setOutputPipe((OutputPipe) obj.getObject());
        }

        // Set the variable name for the return value (if any).
        app.setReturnVariable(returnvar);

        // now init it!
        app.init((PeerGroup) env.get("stdgroup").getObject(), null, null);

        return app;
    }

    /**
     * Load and run a shell application.
     *
     * @param returnvar The env variable in which the command should put its result.
     * @param appName   The name of the application to load.
     * @param env       The enviroment to use.
     */
    protected int exec(String returnvar, String appName, String[] args, ShellEnv env) {
        ShellApp app = loadApp(returnvar, appName, env);

        if (null == app) {
            consoleMessage("Could not load application : " + appName);
            return ShellApp.appMiscError;
        }

        return exec(app, args);
    }

    /**
     * Run a loaded shell Application.
     *
     * @param app  the application
     * @param args arguments for the application.
     */
    protected int exec(ShellApp app, String[] args) {
        try {
            // start the app
            int result = app.startApp(args);

            if (ShellApp.appSpawned != result) {
                if ((ShellApp.appNoError != result) && (getEnv().contains("echo"))) {
                    consoleMessage("'" + app + "' returned error code : " + result);
                }

                app.stopApp();
            }

            return result;
        } catch (Throwable e) {
            printStackTrace("Exception in command : " + app, e);
            return ShellApp.appMiscError;
        }
    }

    // Private implementations

    /**
     * print to the specified pipe.
     *
     * @param pipe The destination pipe.
     * @param line The text to print.
     */
    public static void pipePrint(OutputPipe pipe, String line) {
        if (null == pipe) {
            return;
        }

        //Create a message off this string.
        try {
            Message msg = new Message();

            MessageElement elem = new StringMessageElement("ShellOutputPipe", line, null);

            msg.addMessageElement(elem);

            pipe.send(msg);
        } catch (IOException failure) {
            failure.printStackTrace();
        }
    }

    /**
     * print to the specified pipe appending a newline after text.
     *
     * @param pipe The destination pipe.
     * @param line The text to print.
     */
    public static void pipePrintln(OutputPipe pipe, String line) {
        pipePrint(pipe, line + "\n");
    }

    /*
    *  Return a string containing one line of input from the specified pipe.
    *
    *  @param input the pipe to poll.
    *  @param lineBuffer a vector containing buffered lines associated with
    *  this pipe. If not present then it is VERY LIKELY YOU WILL LOSE MESSAGES.
    *  @return String containing the new line or null if no line was available.
    *  @throws IOException if the input pipe somehow becomes uninitialized.
    */
    protected String pipePollInput(InputPipe input, List lineBuffer) throws IOException {
        boolean onePoll = false;

        do {
            if (null != lineBuffer) {
                // Check in the buffer first
                synchronized (lineBuffer) {
                    if (!lineBuffer.isEmpty()) {
                        try {
                            return lineBuffer.remove(0);
                        } catch (Exception e) {
                            // This is a very strange case, but if that happens, let just
                            // wait for a message on the InputPipe.
                        }
                    }
                }
            }

            if (input == null) {
                throw new IOException("Input pipe null");
            }

            if (onePoll) {
                return null;
            }

            Message msg = null;

            try {
                msg = input.poll(1000);
            } catch (InterruptedException woken) {
                Thread.interrupted();
            }

            // no message was waiting.
            if (null == msg) {
                return null;
            }

            onePoll = true;

            // Get all the chunks of data in the message
            Iterator elems = msg.getMessageElementsOfNamespace(null);
            while (elems.hasNext()) {
                Reader plainReader = null;
                BufferedReader inputReader = null;
                try {
                    MessageElement elem = elems.next();
                    elems.remove();
                    String name = elem.getElementName();

                    // because of pipe redirection we accept both input and
                    // and output elements.
                    if (!name.equals("ShellInputPipe") && !name.equals("ShellOutputPipe")) {
                        continue;
                    }

                    if (elem instanceof TextMessageElement) {
                        plainReader = ((TextMessageElement) elem).getReader();
                    } else {
                        InputStream inputStream = elem.getStream();
                        plainReader = new InputStreamReader(inputStream);
                    }

                    inputReader = new BufferedReader(plainReader);

                    do {
                        String command = inputReader.readLine();
                        if (null != command)
                            if (null != lineBuffer)
                                synchronized (lineBuffer) {
                                    lineBuffer.add(command);
                                }
                            else
                                return command;
                        else
                            break;
                    } while (true);
                } catch (Throwable e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (null != plainReader) {
                            plainReader.close();
                        }
                    } catch (IOException ignored) {
                        //ignored
                    }

                    try {
                        if (null != inputReader) {
                            inputReader.close();
                        }
                    } catch (IOException ignored) {
                        //ignored
                    }
                }
            }
            msg.clear();
        } while (true);
    }

    private String pipeWaitForInput(InputPipe input, List lineBuffer, boolean join) throws IOException {
        String msg;

        while (true) {
            msg = pipePollInput(input, lineBuffer);

            if ((null != msg) || stopped)
                break;

            if (join && ((null == dependsOn) || (!dependsOn.isAlive())))
                break;
        }

        return msg;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy