/**
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.client;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.waarp.commandexec.utils.LocalExecDefaultResult;
import org.waarp.commandexec.utils.LocalExecResult;
import org.waarp.common.crypto.ssl.WaarpSslUtility;
import org.waarp.common.future.WaarpFuture;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
/**
* Handles a client-side channel for LocalExec
*
*
*/
public class LocalExecClientHandler extends SimpleChannelInboundHandler<String> {
/**
* Internal Logger
*/
private static final WaarpLogger logger = WaarpLoggerFactory
.getLogger(LocalExecClientHandler.class);
protected LocalExecResult result;
protected StringBuilder back;
protected boolean firstMessage = true;
protected WaarpFuture future;
protected LocalExecClientInitializer factory = null;
protected long delay;
protected String command;
protected Channel channel;
protected WaarpFuture ready = new WaarpFuture(true);
/**
* Constructor
*/
public LocalExecClientHandler(LocalExecClientInitializer factory) {
this.factory = factory;
}
/**
* Initialize the client status for a new execution
*
* @param delay
* @param command
*/
public void initExecClient(long delay, String command) {
this.result = new LocalExecResult(LocalExecDefaultResult.NoStatus);
this.back = new StringBuilder();
this.firstMessage = true;
this.future = new WaarpFuture(true);
this.delay = delay;
this.command = command;
// Sends the received line to the server.
if (channel == null) {
try {
ready.await();
} catch (InterruptedException e) {
}
}
logger.debug("write command: " + this.command);
try {
if (this.delay != 0) {
channel.writeAndFlush(this.delay + " " + this.command + "\n").await(30000);
} else {
channel.writeAndFlush(this.command + "\n").await(30000);
}
} catch (InterruptedException e) {
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channel = ctx.channel();
factory.addChannel(channel);
ready.setSuccess();
super.channelActive(ctx);
}
/**
* When closed, <br>
* If no messaged were received => NoMessage error is set to future<br>
* Else if an error was detected => Set the future to error (with or without exception)<br>
* Else if no error occurs => Set success to the future<br>
*
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (future == null || !future.isDone()) {
// Should not be
finalizeMessage();
}
super.channelInactive(ctx);
}
/**
* Finalize a message
*/
private void finalizeMessage() {
if (result == null) {
if (this.future != null) {
this.future.cancel();
}
return;
}
if (firstMessage) {
result.set(LocalExecDefaultResult.NoMessage);
} else {
result.setResult(back.toString());
}
if (result.getStatus() < 0) {
if (result.getException() != null) {
this.future.setFailure(result.getException());
} else {
this.future.cancel();
}
} else {
this.future.setSuccess();
}
}
/**
* Waiting for the close of the exec
*
* @return The LocalExecResult
*/
public LocalExecResult waitFor(long delay) {
if (delay <= 0) {
this.future.awaitUninterruptibly();
} else {
this.future.awaitUninterruptibly(delay);
}
result.setSuccess(this.future.isSuccess());
return result;
}
/**
* Action to do before close
*/
public void actionBeforeClose(Channel channel) {
// here nothing to do
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String mesg) throws Exception {
// Add the line received from the server.
// If first message, then take the status and then the message
if (firstMessage) {
firstMessage = false;
int pos = mesg.indexOf(' ');
try {
result.setStatus(Integer.parseInt(mesg.substring(0, pos)));
} catch (NumberFormatException e1) {
// Error
logger.debug(this.command + ":" + "Bad Transmission: " + mesg + "\n\t" + back.toString());
result.set(LocalExecDefaultResult.BadTransmition);
back.append(mesg);
actionBeforeClose(ctx.channel());
WaarpSslUtility.closingSslChannel(ctx.channel());
return;
}
mesg = mesg.substring(pos + 1);
if (mesg.startsWith(LocalExecDefaultResult.ENDOFCOMMAND)) {
logger.debug(this.command + ":" + "Receive End of Command");
result.setResult(LocalExecDefaultResult.NoMessage.getResult());
back.append(result.getResult());
this.finalizeMessage();
} else {
result.setResult(mesg);
back.append(mesg);
}
} else if (mesg.startsWith(LocalExecDefaultResult.ENDOFCOMMAND)) {
logger.debug(this.command + ":" + "Receive End of Command");
this.finalizeMessage();
} else {
back.append('\n').append(mesg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.warn(this.command + ":" + "Unexpected exception from Outband while get information: " + firstMessage,
cause);
if (firstMessage) {
firstMessage = false;
result.set(LocalExecDefaultResult.BadTransmition);
result.setException((Exception) cause);
back = new StringBuilder("Error in LocalExec: ").append(result.getException().getMessage()).append('\n');
} else {
back.append("\nERROR while receiving answer: ");
result.setException((Exception) cause);
back.append(result.getException().getMessage()).append('\n');
}
actionBeforeClose(ctx.channel());
WaarpSslUtility.closingSslChannel(ctx.channel());
}
}