org.waarp.commandexec.server.LocalExecServerHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.commandexec.server.LocalExecServerHandler.java

Source

/**
   This file is part of Waarp Project.
    
   Copyright 2009, Frederic Bregier, and individual contributors by the @author
   tags. See the COPYRIGHT.txt in the distribution for a full listing of
   individual contributors.
    
   All Waarp Project is free software: you can redistribute it and/or 
   modify it under the terms of the GNU General Public License as published 
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
    
   Waarp 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 General Public License for more details.
    
   You should have received a copy of the GNU General Public License
   along with Waarp .  If not, see <http://www.gnu.org/licenses/>.
 */
package org.waarp.commandexec.server;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.RejectedExecutionException;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import org.waarp.commandexec.utils.LocalExecDefaultResult;
import org.waarp.common.crypto.ssl.WaarpSslUtility;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.WaarpStringUtils;

/**
 * Handles a server-side channel for LocalExec.
 *
 *
 */
public class LocalExecServerHandler extends SimpleChannelInboundHandler<String> {
    // Fixed delay, but could change if necessary at construction
    private long delay = LocalExecDefaultResult.MAXWAITPROCESS;
    protected LocalExecServerInitializer factory = null;
    static protected boolean isShutdown = false;

    /**
     * Internal Logger
     */
    private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(LocalExecServerHandler.class);

    protected volatile boolean answered = false;

    /**
     * Is the Local Exec Server going Shutdown
     * 
     * @param channel
     *            associated channel
     * @return True if in Shutdown
     */
    public static boolean isShutdown(Channel channel) {
        if (isShutdown) {
            channel.writeAndFlush(LocalExecDefaultResult.ConnectionRefused.status + " "
                    + LocalExecDefaultResult.ConnectionRefused.result + "\n");
            try {
                channel.writeAndFlush(LocalExecDefaultResult.ENDOFCOMMAND + "\n").await(30000);
            } catch (InterruptedException e) {
            }
            WaarpSslUtility.closingSslChannel(channel);
            return true;
        }
        return false;
    }

    /**
     * Print stack trace
     * 
     * @param thread
     * @param stacks
     */
    static private void printStackTrace(Thread thread, StackTraceElement[] stacks) {
        System.err.print(thread.toString() + " : ");
        for (int i = 0; i < stacks.length - 1; i++) {
            System.err.print(stacks[i].toString() + " ");
        }
        System.err.println(stacks[stacks.length - 1].toString());
    }

    /**
     * Shutdown thread
     * 
     * @author Frederic Bregier
     *
     */
    private static class GGLEThreadShutdown extends Thread {
        long delay = 3000;
        LocalExecServerInitializer factory;

        public GGLEThreadShutdown(LocalExecServerInitializer factory) {
            this.factory = factory;
        }

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        @Override
        public void run() {
            Timer timer = null;
            timer = new Timer(true);
            GGLETimerTask ggleTimerTask = new GGLETimerTask();
            timer.schedule(ggleTimerTask, delay);
            factory.releaseResources();
            System.exit(0);
        }

    }

    /**
     * TimerTask to terminate the server
     * 
     * @author Frederic Bregier
     *
     */
    private static class GGLETimerTask extends TimerTask {
        /**
         * Internal Logger
         */
        private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(GGLETimerTask.class);

        /*
         * (non-Javadoc)
         *
         * @see java.util.TimerTask#run()
         */
        @Override
        public void run() {
            logger.error("System will force EXIT");
            Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
            for (Thread thread : map.keySet()) {
                printStackTrace(thread, map.get(thread));
            }
            System.exit(0);
        }
    }

    /**
     * Constructor with a specific delay
     * 
     * @param newdelay
     */
    public LocalExecServerHandler(LocalExecServerInitializer factory, long newdelay) {
        this.factory = factory;
        delay = newdelay;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (isShutdown(ctx.channel())) {
            answered = true;
            return;
        }
        answered = false;
        factory.addChannel(ctx.channel());
    }

    /**
     * Change the delay to the specific value. Need to be called before any receive message.
     * 
     * @param newdelay
     */
    public void setNewDelay(long newdelay) {
        delay = newdelay;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        answered = false;
        String request = msg;

        // Generate and write a response.
        String response;
        response = LocalExecDefaultResult.NoStatus.status + " " + LocalExecDefaultResult.NoStatus.result;
        ExecuteWatchdog watchdog = null;
        try {
            if (request.length() == 0) {
                // No command
                response = LocalExecDefaultResult.NoCommand.status + " " + LocalExecDefaultResult.NoCommand.result;
            } else {
                String[] args = request.split(" ");
                int cpt = 0;
                long tempDelay;
                try {
                    tempDelay = Long.parseLong(args[0]);
                    cpt++;
                } catch (NumberFormatException e) {
                    tempDelay = delay;
                }
                if (tempDelay < 0) {
                    // Shutdown Order
                    isShutdown = true;
                    logger.warn("Shutdown order received");
                    response = LocalExecDefaultResult.ShutdownOnGoing.status + " "
                            + LocalExecDefaultResult.ShutdownOnGoing.result;
                    Thread thread = new GGLEThreadShutdown(factory);
                    thread.start();
                    return;
                }
                String binary = args[cpt++];
                File exec = new File(binary);
                if (exec.isAbsolute()) {
                    // If true file, is it executable
                    if (!exec.canExecute()) {
                        logger.error("Exec command is not executable: " + request);
                        response = LocalExecDefaultResult.NotExecutable.status + " "
                                + LocalExecDefaultResult.NotExecutable.result;
                        return;
                    }
                }
                // Create command with parameters
                CommandLine commandLine = new CommandLine(binary);
                for (; cpt < args.length; cpt++) {
                    commandLine.addArgument(args[cpt]);
                }
                DefaultExecutor defaultExecutor = new DefaultExecutor();
                ByteArrayOutputStream outputStream;
                outputStream = new ByteArrayOutputStream();
                PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(outputStream);
                defaultExecutor.setStreamHandler(pumpStreamHandler);
                int[] correctValues = { 0, 1 };
                defaultExecutor.setExitValues(correctValues);
                if (tempDelay > 0) {
                    // If delay (max time), then setup Watchdog
                    watchdog = new ExecuteWatchdog(tempDelay);
                    defaultExecutor.setWatchdog(watchdog);
                }
                int status = -1;
                try {
                    // Execute the command
                    status = defaultExecutor.execute(commandLine);
                } catch (ExecuteException e) {
                    if (e.getExitValue() == -559038737) {
                        // Cannot run immediately so retry once
                        try {
                            Thread.sleep(LocalExecDefaultResult.RETRYINMS);
                        } catch (InterruptedException e1) {
                        }
                        try {
                            status = defaultExecutor.execute(commandLine);
                        } catch (ExecuteException e1) {
                            try {
                                pumpStreamHandler.stop();
                            } catch (IOException e3) {
                            }
                            logger.error("Exception: " + e.getMessage() + " Exec in error with "
                                    + commandLine.toString());
                            response = LocalExecDefaultResult.BadExecution.status + " "
                                    + LocalExecDefaultResult.BadExecution.result;
                            try {
                                outputStream.close();
                            } catch (IOException e2) {
                            }
                            return;
                        } catch (IOException e1) {
                            try {
                                pumpStreamHandler.stop();
                            } catch (IOException e3) {
                            }
                            logger.error("Exception: " + e.getMessage() + " Exec in error with "
                                    + commandLine.toString());
                            response = LocalExecDefaultResult.BadExecution.status + " "
                                    + LocalExecDefaultResult.BadExecution.result;
                            try {
                                outputStream.close();
                            } catch (IOException e2) {
                            }
                            return;
                        }
                    } else {
                        try {
                            pumpStreamHandler.stop();
                        } catch (IOException e3) {
                        }
                        logger.error(
                                "Exception: " + e.getMessage() + " Exec in error with " + commandLine.toString());
                        response = LocalExecDefaultResult.BadExecution.status + " "
                                + LocalExecDefaultResult.BadExecution.result;
                        try {
                            outputStream.close();
                        } catch (IOException e2) {
                        }
                        return;
                    }
                } catch (IOException e) {
                    try {
                        pumpStreamHandler.stop();
                    } catch (IOException e3) {
                    }
                    logger.error("Exception: " + e.getMessage() + " Exec in error with " + commandLine.toString());
                    response = LocalExecDefaultResult.BadExecution.status + " "
                            + LocalExecDefaultResult.BadExecution.result;
                    try {
                        outputStream.close();
                    } catch (IOException e2) {
                    }
                    return;
                }
                try {
                    pumpStreamHandler.stop();
                } catch (IOException e3) {
                }
                if (defaultExecutor.isFailure(status) && watchdog != null && watchdog.killedProcess()) {
                    // kill by the watchdoc (time out)
                    logger.error("Exec is in Time Out");
                    response = LocalExecDefaultResult.TimeOutExecution.status + " "
                            + LocalExecDefaultResult.TimeOutExecution.result;
                    try {
                        outputStream.close();
                    } catch (IOException e2) {
                    }
                } else {
                    try {
                        response = status + " " + outputStream.toString(WaarpStringUtils.UTF8.name());
                    } catch (UnsupportedEncodingException e) {
                        response = status + " " + outputStream.toString();
                    }
                    try {
                        outputStream.close();
                    } catch (IOException e2) {
                    }
                }
            }
        } finally {
            // We do not need to write a ByteBuf here.
            // We know the encoder inserted at LocalExecInitializer will do the
            // conversion.
            ctx.channel().writeAndFlush(response + "\n");
            answered = true;
            if (watchdog != null) {
                watchdog.stop();
            }
            logger.info("End of Command: " + request + " : " + response);
            ctx.channel().writeAndFlush(LocalExecDefaultResult.ENDOFCOMMAND + "\n");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (!answered) {
            logger.error("Unexpected exception from Outband while not answered.", cause);
        }
        Throwable e1 = cause;
        // Look if Nothing to do since execution will stop later on and
        // an error will occur on client side
        // since no message arrived before close (or partially)
        if (e1 instanceof CancelledKeyException) {
        } else if (e1 instanceof ClosedChannelException) {
        } else if (e1 instanceof NullPointerException) {
            if (ctx.channel().isActive()) {
                if (answered) {
                    logger.debug("Exception while answered: ", cause);
                }
                WaarpSslUtility.closingSslChannel(ctx.channel());
            }
        } else if (e1 instanceof IOException) {
            if (ctx.channel().isActive()) {
                if (answered) {
                    logger.debug("Exception while answered: ", cause);
                }
                WaarpSslUtility.closingSslChannel(ctx.channel());
            }
        } else if (e1 instanceof RejectedExecutionException) {
            if (ctx.channel().isActive()) {
                if (answered) {
                    logger.debug("Exception while answered: ", cause);
                }
                WaarpSslUtility.closingSslChannel(ctx.channel());
            }
        }
    }
}