/** * 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(); } }