/*
* Copyright 2008-2009 the original author or authors.
*
* 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 net.hasor.rsf.console;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import net.hasor.rsf.InterAddress;
import net.hasor.rsf.RsfContext;
import net.hasor.rsf.RsfSettings;
import net.hasor.rsf.domain.RsfConstants;
import net.hasor.rsf.utils.NameThreadFactory;
import net.hasor.rsf.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
/**
* Handles a server-side channel.
*/
@Sharable
public class TelnetHandler extends SimpleChannelInboundHandler<String> {
protected static Logger logger = LoggerFactory.getLogger(RsfConstants.LoggerName_Console);
protected static Logger rxdLogger = LoggerFactory.getLogger(RsfConstants.LoggerName_ConsoleRXD);
private static final AttributeKey<RsfCommandRequest> RequestKEY = AttributeKey.newInstance("CommandRequest");
private static final AttributeKey<RsfCommandSession> SessionKEY = AttributeKey.newInstance("CommandSession");
private static final String CMD = "rsf>";
private RsfContext rsfContext;
private CommandManager commandManager;
private ScheduledExecutorService executor;
private String[] inBoundAddress;
//
public TelnetHandler(RsfContext rsfContext) {
this.rsfContext = rsfContext;
int workSize = 1;
String shortName = "RSF-Console-Work";
this.executor = Executors.newScheduledThreadPool(workSize, new NameThreadFactory(shortName, rsfContext.getClassLoader()));
ThreadPoolExecutor threadPool = (ThreadPoolExecutor) this.executor;
threadPool.setCorePoolSize(workSize);
threadPool.setMaximumPoolSize(workSize);
logger.info("rsfConsole -> create TelnetHandler , threadShortName={} , workThreadSize = {}.", shortName, workSize);
//
this.inBoundAddress = rsfContext.getSettings().getConsoleInBoundAddress();
this.commandManager = rsfContext.getAppContext().getInstance(CommandManager.class);
logger.info("rsfConsole -> inBoundAddress is :{}.", StringUtils.join(this.inBoundAddress, ","));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress inetAddress = (InetSocketAddress) ctx.channel().remoteAddress();
String remoteAddress = inetAddress.getAddress().getHostAddress();
//
boolean contains = false;
for (String addr : this.inBoundAddress) {
if (addr.equals(remoteAddress))
contains = true;
}
//
if (!contains) {
logger.warn("rsfConsole -> reject inBound socket ,remoteAddress = {}.", remoteAddress);
ctx.write("--------------------------------------------\r\n\r\n");
ctx.write("I'm sorry you are not allowed to connect RSF Console.\r\n\r\n");
ctx.write(" your address is :" + remoteAddress + "\r\n");
ctx.write("--------------------------------------------\r\n");
ctx.flush();
ctx.close();
return;
} else {
logger.info("rsfConsole -> accept inBound socket ,remoteAddress = {}.", remoteAddress);
}
//
RsfSettings settings = this.rsfContext.getSettings();
List<String> rsfAddressList = getStrings(settings.getBindAddressSet());
//
Attribute<RsfCommandSession> attr = ctx.attr(SessionKEY);
if (attr.get() == null) {
logger.info("rsfConsole -> new RsfCommandSession.");
attr.set(new RsfCommandSession(this.rsfContext, ctx));
}
logger.info("rsfConsole -> send Welcome info.");
// Send greeting for a new connection.
ctx.write("--------------------------------------------\r\n\r\n");
ctx.write("Welcome to RSF Console!\r\n");
ctx.write("\r\n");
ctx.write(" login : " + new Date() + " now. form " + ctx.channel().remoteAddress() + "\r\n");
ctx.write(" workAt : " + ctx.channel().localAddress() + "\r\n");
for (int i = 0; i < rsfAddressList.size(); i++) {
if (i == 0) {
ctx.write("rsfAddress : " + rsfAddressList.get(i) + "\r\n");
} else {
ctx.write(" : " + rsfAddressList.get(i) + "\r\n");
}
}
ctx.write(" unitName : " + this.rsfContext.getSettings().getUnitName() + "\r\n\r\n");
ctx.write("Tips: You can enter a 'help' or 'help -a' for more information.\r\n");
ctx.write("use the 'exit' or 'quit' out of the console.\r\n");
ctx.write("--------------------------------------------\r\n");
ctx.write(CMD);
ctx.flush();
}
private List<String> getStrings(Map<String, InterAddress> bindPortMap) throws UnknownHostException {
List<String> hostSchema = new ArrayList<String>();
List<String> portType = new ArrayList<String>(bindPortMap.keySet());
Collections.sort(portType);
//
for (String key : portType) {
try {
InterAddress addr = bindPortMap.get(key);
hostSchema.add(addr.toHostSchema());
} catch (Exception e) {
hostSchema.add(InetAddress.getLocalHost().getHostAddress());
}
}
return hostSchema;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception {
request = request.trim();
rxdLogger.info("RXD({})-> {}", ctx.channel().remoteAddress(), request);
//
Attribute<RsfCommandRequest> attr = ctx.attr(RequestKEY);
boolean close = false;
String result = "";
boolean doRequest = false;
if (StringUtils.isBlank(request)) {
if (attr != null && attr.get() != null) {
doRequest = true;
}
} else {
doRequest = true;
}
//
if (!doRequest) {
logger.info("rsfConsole -> receive RXD :" + request);
}
//
if (doRequest) {
RsfCommandResponse response = this.doRequest(attr, ctx, request);
if (response != null) {
close = response.isCloseConnection();
logger.info("rsfConsole -> receive RXD, response isComplete = {}, isCloseConnection = {}", response.isComplete(), response.isCloseConnection());
if (response.isComplete()) {
result = response.getResult() + "\r\n" + CMD;
} else {
result = response.getResult() + "\r\n";
}
}
} else {
result = CMD;
}
//
if (StringUtils.isNotBlank(result)) {
rxdLogger.info("TXD({})-> {}", ctx.channel().remoteAddress(), result);
ChannelFuture future = ctx.writeAndFlush(result);
if (close) {
logger.info("rsfConsole -> close connection.");
future.addListener(ChannelFutureListener.CLOSE);
}
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LoggerFactory.getLogger(TelnetHandler.class).error("rsfConsole error->" + cause.getMessage(), cause);
clearAttr(ctx);
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("rsfConsole -> channelInactive.");
clearAttr(ctx);
super.channelInactive(ctx);
}
private void clearAttr(ChannelHandlerContext ctx) {
Attribute<RsfCommandSession> sessionAttr = ctx.attr(SessionKEY);
Attribute<RsfCommandRequest> attr = ctx.attr(RequestKEY);
if (sessionAttr != null) {
logger.info("rsfConsole -> clearAttr ,remove sessionAttr.");
sessionAttr.remove();
}
if (attr != null) {
logger.info("rsfConsole -> clearAttr ,remove requestAttr.");
attr.remove();
}
}
private RsfCommandResponse doRequest(final Attribute<RsfCommandRequest> cmdAttr, final ChannelHandlerContext ctx, final String inputString) {
// .准备环境
logger.info("rsfConsole -> doRequest, pre environment.");
RsfCommandRequest requestCmd = cmdAttr.get();
Attribute<RsfCommandSession> sessionAttr = ctx.attr(SessionKEY);
boolean newCommand = (requestCmd == null);
//
// .确定是否将本次输入追加到上一个命令中
if (requestCmd == null) {
String requestCMD = inputString;
String requestArgs = "";
int cmdIndex = inputString.indexOf(" ");
if (inputString.indexOf(" ") > 0) {
requestCMD = inputString.substring(0, cmdIndex);
requestArgs = inputString.substring(cmdIndex + 1);
}
RsfInstruct rsfInstruct = this.commandManager.findCommand(requestCMD);
if (rsfInstruct == null) {
String msgStr = "'" + requestCMD + "' is bad command.";
logger.info("rsfConsole -> " + msgStr);
return new RsfCommandResponse(msgStr, true, false);
}
//
logger.info("rsfConsole -> doRequest, RsfCommandRequest in ctx exist. -> command = {} , args = {}", requestCMD, requestArgs);
requestCmd = new RsfCommandRequest(requestCMD, sessionAttr.get(), rsfInstruct, requestArgs);
cmdAttr.set(requestCmd);
}
//
// .不同模式命令的处理
if (requestCmd.inputMultiLine()) {
/*多行模式*/
if (requestCmd.getStatus() == CommandRequestStatus.Prepare) {
if (!newCommand) {
requestCmd.appendRequestBody(inputString);
}
if (StringUtils.isBlank(inputString)) {
requestCmd.inReady();//命令准备执行
}
} else if (requestCmd.getStatus() == CommandRequestStatus.Ready) {
if (StringUtils.isBlank(inputString)) {
requestCmd.inStandBy();//命令准备执行
} else {
requestCmd.appendRequestBody(inputString);
}
}
} else {
/*单行模式*/
requestCmd.inStandBy();
}
//
//3.执行命令
if (requestCmd.getStatus() == CommandRequestStatus.StandBy) {
logger.info("rsfConsole -> doRequest, doRunning.");
requestCmd.doCommand(executor, new Runnable() {
public void run() {
cmdAttr.remove();
ctx.writeAndFlush("\r\n" + CMD);
}
});
//命令进入就绪状态
return requestCmd.getResponse();
} else if (requestCmd.getStatus() == CommandRequestStatus.Running) {
return new RsfCommandResponse("command is running, please wait a moment.", false, false);
}
//
return null;
}
}