/**
* 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.openr66.protocol.utils;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroupFuture;
import io.netty.channel.group.ChannelGroupFutureListener;
import io.netty.channel.local.LocalChannel;
import io.netty.handler.traffic.ChannelTrafficShapingHandler;
import io.netty.handler.traffic.TrafficCounter;
import org.slf4j.LoggerFactory;
import org.waarp.common.database.DbAdmin;
import org.waarp.common.file.DataBlock;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.logging.WaarpSlf4JLoggerFactory;
import org.waarp.openr66.context.R66FiniteDualStates;
import org.waarp.openr66.context.task.localexec.LocalExecClient;
import org.waarp.openr66.database.DbConstant;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.configuration.Messages;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
import org.waarp.openr66.protocol.localhandler.packet.AbstractLocalPacket;
import org.waarp.openr66.protocol.localhandler.packet.DataPacket;
import org.waarp.openr66.protocol.localhandler.packet.EndTransferPacket;
import org.waarp.openr66.protocol.localhandler.packet.LocalPacketFactory;
import org.waarp.openr66.protocol.localhandler.packet.RequestPacket;
import org.waarp.openr66.protocol.networkhandler.GlobalTrafficHandler;
import org.waarp.openr66.protocol.networkhandler.NetworkTransaction;
import org.waarp.openr66.protocol.networkhandler.packet.NetworkPacket;
import ch.qos.logback.classic.LoggerContext;
/**
* Channel Utils
*
* @author Frederic Bregier
*/
public class ChannelUtils extends Thread {
/**
* Internal Logger
*/
private static final WaarpLogger logger = WaarpLoggerFactory
.getLogger(ChannelUtils.class);
public static final Integer NOCHANNEL = Integer.MIN_VALUE;
/**
* Get the Remote InetAddress
*
* @param channel
* @return the remote InetAddress
*/
public final static InetAddress getRemoteInetAddress(Channel channel) {
InetSocketAddress socketAddress = (InetSocketAddress) channel.remoteAddress();
if (socketAddress == null) {
socketAddress = new InetSocketAddress(20);
}
return socketAddress.getAddress();
}
/**
* Get the Local InetAddress
*
* @param channel
* @return the local InetAddress
*/
public final static InetAddress getLocalInetAddress(Channel channel) {
final InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress();
return socketAddress.getAddress();
}
/**
* Get the Remote InetSocketAddress
*
* @param channel
* @return the remote InetSocketAddress
*/
public final static InetSocketAddress getRemoteInetSocketAddress(Channel channel) {
return (InetSocketAddress) channel.remoteAddress();
}
/**
* Get the Local InetSocketAddress
*
* @param channel
* @return the local InetSocketAddress
*/
public final static InetSocketAddress getLocalInetSocketAddress(Channel channel) {
return (InetSocketAddress) channel.localAddress();
}
/**
* Finalize resources attached to handlers
*
* @author Frederic Bregier
*/
private static class R66ChannelGroupFutureListener implements
ChannelGroupFutureListener {
String name;
EventLoopGroup group;
public R66ChannelGroupFutureListener(String name, EventLoopGroup group) {
this.name = name;
this.group = group;
}
public void operationComplete(ChannelGroupFuture future)
throws Exception {
logger.info("Start with shutdown external resources for " + name);
if (group != null) {
group.shutdownGracefully();
}
logger.info("Done with shutdown " + name);
}
}
/**
* Terminate all registered channels
*
* @return the number of previously registered network channels
*/
private static int terminateCommandChannels() {
if (Configuration.configuration.getServerChannelGroup() == null) {
return 0;
}
final int result = Configuration.configuration.getServerChannelGroup().size();
logger.info("ServerChannelGroup: " + result);
Configuration.configuration.getServerChannelGroup().close()
.addListener(
new R66ChannelGroupFutureListener(
"ServerChannelGroup",
Configuration.configuration.getHandlerGroup()));
return result;
}
/**
* Terminate all registered Http channels
*
* @return the number of previously registered http network channels
*/
private static int terminateHttpChannels() {
if (Configuration.configuration.getHttpChannelGroup() == null) {
return 0;
}
final int result = Configuration.configuration.getHttpChannelGroup().size();
logger.debug("HttpChannelGroup: " + result);
Configuration.configuration.getHttpChannelGroup().close();
return result;
}
/**
* Return the current number of network connections
*
* @param configuration
* @return the current number of network connections
*/
public final static int nbCommandChannels(Configuration configuration) {
return configuration.getServerChannelGroup().size();
}
/**
* To be used only with LocalChannel (NetworkChannel could be using SSL)
*
* @param channel
*/
public final static void close(final LocalChannel channel) {
channel.eventLoop().schedule(new Runnable() {
public void run() {
channel.close();
}
}, Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
}
/**
*
* @param localChannelReference
* @param block
* @return the ChannelFuture of this write operation
* @throws OpenR66ProtocolPacketException
*/
public static ChannelFuture writeBackDataBlock(
LocalChannelReference localChannelReference, DataBlock block)
throws OpenR66ProtocolPacketException {
ByteBuf md5 = Unpooled.EMPTY_BUFFER;
DbTaskRunner runner = localChannelReference.getSession().getRunner();
if (RequestPacket.isMD5Mode(runner.getMode())) {
md5 = FileUtils.getHash(block.getBlock(), Configuration.configuration.getDigest());
}
if (runner.getRank() % 100 == 1 || localChannelReference.getSessionState() != R66FiniteDualStates.DATAS) {
localChannelReference.sessionNewState(R66FiniteDualStates.DATAS);
}
DataPacket data = new DataPacket(runner.getRank(), block.getBlock(), md5);// was block.getBlock().copy()
ChannelFuture future = writeAbstractLocalPacket(localChannelReference, data, false);
runner.incrementRank();
return future;
}
/**
* Write the EndTransfer
*
* @param localChannelReference
* @throws OpenR66ProtocolPacketException
*/
public final static void writeEndTransfer(
LocalChannelReference localChannelReference)
throws OpenR66ProtocolPacketException {
EndTransferPacket packet = new EndTransferPacket(LocalPacketFactory.REQUESTPACKET);
localChannelReference.sessionNewState(R66FiniteDualStates.ENDTRANSFERS);
writeAbstractLocalPacket(localChannelReference, packet, true);
}
/**
* Write the EndTransfer plus Global Hash
*
* @param localChannelReference
* @param hash
* @throws OpenR66ProtocolPacketException
*/
public final static void writeEndTransfer(
LocalChannelReference localChannelReference, String hash)
throws OpenR66ProtocolPacketException {
EndTransferPacket packet = new EndTransferPacket(
LocalPacketFactory.REQUESTPACKET, hash);
localChannelReference.sessionNewState(R66FiniteDualStates.ENDTRANSFERS);
writeAbstractLocalPacket(localChannelReference, packet, true);
}
/**
* Write an AbstractLocalPacket to the network Channel
*
* @param localChannelReference
* @param packet
* @param wait
* @return the ChannelFuture on write operation
* @throws OpenR66ProtocolPacketException
*/
public static ChannelFuture writeAbstractLocalPacket(
LocalChannelReference localChannelReference, AbstractLocalPacket packet,
boolean wait)
throws OpenR66ProtocolPacketException {
final NetworkPacket networkPacket;
try {
networkPacket = new NetworkPacket(localChannelReference
.getLocalId(), localChannelReference.getRemoteId(), packet, localChannelReference);
} catch (OpenR66ProtocolPacketException e) {
logger.error(Messages.getString("ChannelUtils.6") + packet.toString(), //$NON-NLS-1$
e);
throw e;
}
if (wait) {
ChannelFuture future = localChannelReference.getNetworkChannel().writeAndFlush(networkPacket);
localChannelReference.getNetworkChannelObject().use();
try {
future.await(Configuration.configuration.getTIMEOUTCON());
return future;
} catch (InterruptedException e) {
return future;
}
} else {
return localChannelReference.getNetworkChannel().writeAndFlush(networkPacket);
}
}
/**
* Write an AbstractLocalPacket to the Local Channel
*
* @param localChannelReference
* @param packet
* @return the ChannelFuture on write operation
* @throws OpenR66ProtocolPacketException
*/
public final static ChannelFuture writeAbstractLocalPacketToLocal(
LocalChannelReference localChannelReference, AbstractLocalPacket packet)
throws OpenR66ProtocolPacketException {
return localChannelReference.getLocalChannel().writeAndFlush(packet);
}
/**
* Compute Wait for Traffic in Write (ugly turn around)
*
* @param localChannelReference
* @param size
* @return the wait in ms
*/
public static final long willBeWaitingWriting(LocalChannelReference localChannelReference,
int size) {
ChannelTrafficShapingHandler cts = localChannelReference.getChannelTrafficShapingHandler();
return willBeWaitingWriting(cts, size);
}
/**
* Compute Wait for Traffic in Write (ugly turn around)
*
* @param cts
* @param size
* @return the wait in ms
*/
public static final long willBeWaitingWriting(ChannelTrafficShapingHandler cts, int size) {
long currentTime = System.currentTimeMillis();
if (cts != null && Configuration.configuration.getServerChannelWriteLimit() > 0) {
TrafficCounter tc = cts.trafficCounter();
if (tc != null) {
long wait = waitTraffic(Configuration.configuration.getServerChannelWriteLimit(),
tc.currentWrittenBytes() + size,
tc.lastTime(), currentTime);
if (wait > 0) {
return wait;
}
}
}
if (Configuration.configuration.getServerGlobalWriteLimit() > 0) {
GlobalTrafficHandler gts = Configuration.configuration
.getGlobalTrafficShapingHandler();
if (gts != null) {
TrafficCounter tc = gts.trafficCounter();
if (tc != null) {
long wait = waitTraffic(Configuration.configuration.getServerGlobalWriteLimit(),
tc.currentWrittenBytes() + size,
tc.lastTime(), currentTime);
if (wait > 0) {
return wait;
}
}
}
}
return 0;
}
private static final long waitTraffic(long limit, long bytes, long lastTime,
long curtime) {
long interval = curtime - lastTime;
if (interval == 0) {
// Time is too short, so just lets continue
return 0;
}
return ((bytes * 1000 / limit - interval) / 10) * 10;
}
/**
* Exit global ChannelFactory
*/
public static void exit() {
logger.info("Current launched threads before exit: " + ManagementFactory.getThreadMXBean().getThreadCount());
if (Configuration.configuration.getConstraintLimitHandler() != null) {
Configuration.configuration.getConstraintLimitHandler().release();
}
// First try to StopAll
TransferUtils.stopSelectedTransfers(DbConstant.admin.getSession(), 0,
null, null, null, null, null, null, null, null, null, true, true, true);
Configuration.configuration.setShutdown(true);
Configuration.configuration.prepareServerStop();
final long delay = Configuration.configuration.getTIMEOUTCON();
// Inform others that shutdown
if (Configuration.configuration.getLocalTransaction() != null) {
Configuration.configuration.getLocalTransaction().shutdownLocalChannels();
}
logger.info("Unbind server network services");
Configuration.configuration.unbindServer();
logger.warn(Messages.getString("ChannelUtils.7") + delay + " ms"); //$NON-NLS-1$
try {
Thread.sleep(delay);
} catch (final InterruptedException e) {
}
NetworkTransaction.closeRetrieveExecutors();
if (Configuration.configuration.getLocalTransaction() != null) {
Configuration.configuration.getLocalTransaction().debugPrintActiveLocalChannels();
}
if (Configuration.configuration.getGlobalTrafficShapingHandler() != null) {
Configuration.configuration.getGlobalTrafficShapingHandler().release();
}
logger.info("Exit Shutdown Http");
terminateHttpChannels();
logger.info("Exit Shutdown Local");
if (Configuration.configuration.getLocalTransaction() != null) {
Configuration.configuration.getLocalTransaction().closeAll();
}
logger.info("Exit Shutdown LocalExec");
if (Configuration.configuration.isUseLocalExec()) {
LocalExecClient.releaseResources();
}
logger.info("Exit Shutdown Command");
terminateCommandChannels();
logger.info("Exit Shutdown Db Connection");
DbAdmin.closeAllConnection();
logger.info("Exit Shutdown ServerStop");
Configuration.configuration.serverStop();
logger.warn(Messages.getString("ChannelUtils.15")); //$NON-NLS-1$
System.err.println(Messages.getString("ChannelUtils.15")); //$NON-NLS-1$
stopLogger();
//Thread.currentThread().interrupt();
}
public final static void stopLogger() {
if (WaarpLoggerFactory.getDefaultFactory() instanceof WaarpSlf4JLoggerFactory) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.stop();
}
}
/**
* This function is the top function to be called when the server is to be shutdown.
*/
@Override
public void run() {
logger.info("Should restart? " + R66ShutdownHook.isRestart());
R66ShutdownHook.terminate(false);
}
/**
* Start Shutdown
*/
public final static void startShutdown() {
if (R66ShutdownHook.isInShutdown()) {
return;
}
Thread thread = new Thread(new ChannelUtils(), "R66 Shutdown Thread");
thread.setDaemon(false);
thread.start();
}
}