org.waarp.openr66.protocol.localhandler.LocalTransaction.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.openr66.protocol.localhandler.LocalTransaction.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.openr66.protocol.localhandler;

import java.net.SocketAddress;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;

import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.openr66.context.ErrorCode;
import org.waarp.openr66.context.R66FiniteDualStates;
import org.waarp.openr66.context.R66Result;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolRemoteShutdownException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolShutdownException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
import org.waarp.openr66.protocol.localhandler.packet.ConnectionErrorPacket;
import org.waarp.openr66.protocol.localhandler.packet.LocalPacketFactory;
import org.waarp.openr66.protocol.localhandler.packet.StartupPacket;
import org.waarp.openr66.protocol.localhandler.packet.ValidPacket;
import org.waarp.openr66.protocol.networkhandler.NetworkChannelReference;
import org.waarp.openr66.protocol.networkhandler.NetworkServerHandler;
import org.waarp.openr66.protocol.networkhandler.NetworkTransaction;
import org.waarp.openr66.protocol.networkhandler.packet.NetworkPacket;
import org.waarp.openr66.protocol.utils.R66Future;
import org.waarp.openr66.protocol.utils.R66ShutdownHook;

/**
 * This class handles Local Transaction connections
 * 
 * @author frederic bregier
 */
public class LocalTransaction {
    /**
     * Internal Logger
     */
    private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(LocalTransaction.class);

    /**
     * HashMap of LocalChannelReference using LocalChannelId
     */
    private final ConcurrentHashMap<Integer, LocalChannelReference> localChannelHashMap = new ConcurrentHashMap<Integer, LocalChannelReference>();

    /**
     * HashMap of LocalChannelReference using requested_requester_specialId
     */
    private final ConcurrentHashMap<String, LocalChannelReference> localChannelHashMapIdBased = new ConcurrentHashMap<String, LocalChannelReference>();

    private final ServerBootstrap serverBootstrap = new ServerBootstrap();

    private final Channel serverChannel;

    private final LocalAddress socketLocalServerAddress = new LocalAddress("0");

    private final Bootstrap clientBootstrap = new Bootstrap();

    private final ChannelGroup localChannelGroup = new DefaultChannelGroup("LocalChannels",
            Configuration.configuration.getSubTaskGroup().next());

    /**
     * Constructor
     */
    public LocalTransaction() {
        serverBootstrap.channel(LocalServerChannel.class);
        serverBootstrap.group(Configuration.configuration.getLocalBossGroup(),
                Configuration.configuration.getLocalWorkerGroup());
        serverBootstrap.option(ChannelOption.TCP_NODELAY, true);
        serverBootstrap.option(ChannelOption.SO_REUSEADDR, true);
        serverBootstrap.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS,
                (int) Configuration.configuration.getTIMEOUTCON());
        serverBootstrap.childHandler(new LocalServerInitializer());
        try {
            serverChannel = serverBootstrap.bind(socketLocalServerAddress).sync().channel();
        } catch (InterruptedException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
        localChannelGroup.add(serverChannel);

        clientBootstrap.channel(LocalChannel.class);
        // Same Group than Network final handler 
        clientBootstrap.group(Configuration.configuration.getLocalWorkerGroup());
        clientBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
                (int) Configuration.configuration.getTIMEOUTCON());
        clientBootstrap.handler(new LocalClientInitializer());
    }

    public String hashStatus() {
        return "LocalTransaction: [localChannelHashMap: " + localChannelHashMap.size()
                + " localChannelHashMapIdBased: " + localChannelHashMapIdBased.size() + "] ";
    }

    /**
     * Get the corresponding LocalChannelReference and set the remoteId if different
     * 
     * @param remoteId
     * @param localId
     * @return the LocalChannelReference
     * @throws OpenR66ProtocolSystemException
     */
    public LocalChannelReference getClient(Integer remoteId, Integer localId)
            throws OpenR66ProtocolSystemException {
        LocalChannelReference localChannelReference = getFromId(localId);
        if (localChannelReference != null) {
            if (localChannelReference.getRemoteId().compareTo(remoteId) != 0) {
                localChannelReference.setRemoteId(remoteId);
            }
            return localChannelReference;
        }
        throw new OpenR66ProtocolSystemException("Cannot find LocalChannelReference");
    }

    private static class SendLater extends Thread {
        private static Map<Integer, SendLater> sendLaters = new ConcurrentHashMap<Integer, SendLater>();
        final LocalTransaction lt;
        final Channel networkChannel;
        final SocketAddress remoteAddress;
        Integer remoteId;
        final Integer localId;
        final Queue<NetworkPacket> packets = new ConcurrentLinkedQueue<NetworkPacket>();
        int step = 0;

        private SendLater(LocalTransaction lt, Channel nc, Integer remoteId, Integer localId) {
            this.lt = lt;
            this.networkChannel = nc;
            this.remoteId = remoteId;
            this.localId = localId;
            remoteAddress = networkChannel.remoteAddress();
        }

        private void add(NetworkPacket packet) {
            if (packets.isEmpty()) {
                packets.add(packet);
                Configuration.configuration.launchInFixedDelay(this, Configuration.WAITFORNETOP,
                        TimeUnit.MILLISECONDS);
            }
            packets.add(packet);
        }

        public void run() {
            synchronized (sendLaters) {
                LocalChannelReference localChannelReference = lt.localChannelHashMap.get(localId);
                if (localChannelReference != null) {
                    if (localChannelReference.getRemoteId().compareTo(remoteId) != 0) {
                        localChannelReference.setRemoteId(remoteId);
                    }
                    NetworkPacket networkPacket = packets.poll();
                    LocalChannel localChannel = localChannelReference.getLocalChannel();
                    while (networkPacket != null) {
                        localChannel.write(networkPacket.getBuffer());
                        networkPacket = packets.poll();
                    }
                    localChannel.flush();
                    sendLaters.remove(localId);
                } else {
                    step++;
                    if (step > 10000) {
                        if (NetworkTransaction.isShuttingdownNetworkChannel(remoteAddress)
                                || R66ShutdownHook.isShutdownStarting()) {
                            // ignore
                            sendLaters.remove(localId);
                            packets.clear();
                            return;
                        }
                        logger.warn("Cannot get LocalChannel: due to LocalId not found: " + localId);
                        final ConnectionErrorPacket error = new ConnectionErrorPacket(
                                "Cannot get localChannel since localId is not found anymore", "" + localId);
                        NetworkServerHandler.writeError(networkChannel, remoteId, localId, error);
                        sendLaters.remove(localId);
                        packets.clear();
                    } else {
                        Configuration.configuration.launchInFixedDelay(this, Configuration.WAITFORNETOP,
                                TimeUnit.MILLISECONDS);
                    }
                }
            }
        }
    }

    /**
     * Get the corresponding LocalChannelReference and set the remoteId if different
     * 
     * @param remoteId
     * @param localId
     * @throws OpenR66ProtocolSystemException
     */
    public void sendLaterToClient(Channel networkChannel, Integer remoteId, Integer localId, NetworkPacket packet) {
        synchronized (SendLater.sendLaters) {
            SendLater sendLater = SendLater.sendLaters.get(localId);
            if (sendLater == null) {
                sendLater = new SendLater(this, networkChannel, remoteId, localId);
                SendLater.sendLaters.put(localId, sendLater);
                sendLater.setDaemon(true);
            }
            sendLater.remoteId = remoteId;
            sendLater.add(packet);
        }
    }

    /**
     * Create a new Client
     * 
     * @param networkChannelReference
     * @param remoteId
     *            might be set to ChannelUtils.NOCHANNEL (real creation)
     * @param futureRequest
     *            might be null (from NetworkChannel Startup)
     * @return the LocalChannelReference
     * @throws OpenR66ProtocolSystemException
     * @throws OpenR66ProtocolRemoteShutdownException
     * @throws OpenR66ProtocolNoConnectionException
     */
    public LocalChannelReference createNewClient(NetworkChannelReference networkChannelReference, Integer remoteId,
            R66Future futureRequest) throws OpenR66ProtocolSystemException, OpenR66ProtocolRemoteShutdownException,
            OpenR66ProtocolNoConnectionException {
        ChannelFuture channelFuture = null;
        logger.debug("Status LocalChannelServer: {} {}", serverChannel.getClass().getName(),
                serverChannel.config().getConnectTimeoutMillis() + " " + serverChannel.isOpen());
        for (int i = 0; i < Configuration.RETRYNB; i++) {
            if (R66ShutdownHook.isShutdownStarting()) {
                // Do not try since already locally in shutdown
                throw new OpenR66ProtocolNoConnectionException("Cannot connect to local handler: "
                        + socketLocalServerAddress + " " + serverChannel.isOpen() + " " + serverChannel
                        + " since the local server is in shutdown.");
            }
            channelFuture = clientBootstrap.connect(socketLocalServerAddress);
            try {
                channelFuture.await();
                //channelFuture.await(Configuration.configuration.TIMEOUTCON/3);
            } catch (InterruptedException e1) {
                logger.error("LocalChannelServer Interrupted: " + serverChannel.getClass().getName() + " "
                        + serverChannel.config().getConnectTimeoutMillis() + " " + serverChannel.isOpen());
                throw new OpenR66ProtocolSystemException("Interruption - Cannot connect to local handler: "
                        + socketLocalServerAddress + " " + serverChannel.isOpen() + " " + serverChannel, e1);
            }
            if (channelFuture.isSuccess()) {
                final LocalChannel channel = (LocalChannel) channelFuture.channel();
                localChannelGroup.add(channel);
                logger.debug(
                        "Will start localChannelReference and eventually generate a new Db Connection if not-thread-safe");
                final LocalChannelReference localChannelReference = new LocalChannelReference(channel,
                        networkChannelReference, remoteId, futureRequest);
                localChannelHashMap.put(channel.id().hashCode(), localChannelReference);
                logger.debug("Db connection done and Create LocalChannel entry: " + i + " {}",
                        localChannelReference);
                logger.info("Add one localChannel to a Network Channel: " + channel.id());
                // Now send first a Startup message
                StartupPacket startup = new StartupPacket(localChannelReference.getLocalId());
                channel.writeAndFlush(startup);
                return localChannelReference;
            } else {
                logger.error("Can't connect to local server " + i + " (Done: " + channelFuture.isDone() + ")");
            }
            try {
                Thread.sleep(Configuration.RETRYINMS * 10);
            } catch (InterruptedException e) {
                throw new OpenR66ProtocolSystemException("Cannot connect to local handler", e);
            }
        }
        logger.error("LocalChannelServer: " + serverChannel.getClass().getName() + " "
                + serverChannel.config().getConnectTimeoutMillis() + " " + serverChannel.isOpen());
        throw new OpenR66ProtocolSystemException("Cannot connect to local handler: " + socketLocalServerAddress
                + " " + serverChannel.isOpen() + " " + serverChannel, channelFuture.cause());
    }

    /**
     * 
     * @param id
     * @return the LocalChannelReference
     */
    public LocalChannelReference getFromId(Integer id) {
        int maxtry = (int) (Configuration.configuration.getTIMEOUTCON() / Configuration.RETRYINMS) / 2;
        maxtry = 100;
        for (int i = 0; i < maxtry; i++) {
            LocalChannelReference lcr = localChannelHashMap.get(id);
            if (lcr == null) {
                try {
                    Thread.sleep(Configuration.RETRYINMS);
                    Thread.yield();
                } catch (InterruptedException e) {
                }
            } else {
                return lcr;
            }
        }
        return localChannelHashMap.get(id);
    }

    /**
     * Remove one local channel
     * 
     * @param localChannelReference
     */
    protected void remove(LocalChannelReference localChannelReference) {
        logger.debug("DEBUG remove: " + localChannelReference.getLocalId());
        localChannelHashMap.remove(localChannelReference.getLocalId());
        if (localChannelReference.getRequestId() != null) {
            localChannelHashMapIdBased.remove(localChannelReference.getRequestId());
        }
    }

    /**
     * 
     * @param runner
     * @param lcr
     */
    public void setFromId(DbTaskRunner runner, LocalChannelReference lcr) {
        String key = runner.getKey();
        lcr.setRequestId(key);
        localChannelHashMapIdBased.put(key, lcr);
    }

    /**
     * 
     * @param key
     *            as "requested requester specialId"
     * @return the LocalChannelReference
     */
    public LocalChannelReference getFromRequest(String key) {
        return localChannelHashMapIdBased.get(key);
    }

    /**
     * 
     * @param key as "requested requester specialId"
     * @return True if the LocalChannelReference exists
     */
    public boolean contained(String key) {
        return localChannelHashMapIdBased.containsKey(key);
    }

    /**
     * 
     * @return the number of active local channels
     */
    public int getNumberLocalChannel() {
        return localChannelHashMap.size();
    }

    /**
     * Debug function (while shutdown for instance)
     */
    public void debugPrintActiveLocalChannels() {
        Collection<LocalChannelReference> collection = localChannelHashMap.values();
        Iterator<LocalChannelReference> iterator = collection.iterator();
        while (iterator.hasNext()) {
            LocalChannelReference localChannelReference = iterator.next();
            logger.debug("Will close local channel: {}", localChannelReference);
            logger.debug(" Containing: {}",
                    (localChannelReference.getSession() != null ? localChannelReference.getSession()
                            : "no session"));
        }
    }

    /**
     * Informs all remote client that the server is shutting down
     */
    public void shutdownLocalChannels() {
        logger.warn("Will inform LocalChannels of Shutdown: " + localChannelHashMap.size());
        Collection<LocalChannelReference> collection = localChannelHashMap.values();
        Iterator<LocalChannelReference> iterator = collection.iterator();
        ValidPacket packet = new ValidPacket("Shutdown forced", null, LocalPacketFactory.SHUTDOWNPACKET);
        ByteBuf buffer = null;
        while (iterator.hasNext()) {
            LocalChannelReference localChannelReference = iterator.next();
            logger.info("Inform Shutdown {}", localChannelReference);
            packet.setSmiddle(null);
            packet.retain();
            // If a transfer is running, save the current rank and inform remote
            // host
            if (localChannelReference.getSession() != null) {
                R66Session session = localChannelReference.getSession();
                DbTaskRunner runner = session.getRunner();
                if (runner != null && runner.isInTransfer()) {
                    if (!runner.isSender()) {
                        int newrank = runner.getRank();
                        packet.setSmiddle(Integer.toString(newrank));
                    }
                    // Save File status
                    try {
                        runner.saveStatus();
                    } catch (OpenR66RunnerErrorException e) {
                    }
                }
                if (runner != null && !runner.isFinished()) {
                    R66Result result = new R66Result(new OpenR66ProtocolShutdownException(), session, true,
                            ErrorCode.Shutdown, runner);
                    result.setOther(packet);
                    try {
                        buffer = packet.getLocalPacket(localChannelReference);
                    } catch (OpenR66ProtocolPacketException e1) {
                    }
                    localChannelReference.sessionNewState(R66FiniteDualStates.SHUTDOWN);
                    NetworkPacket message = new NetworkPacket(localChannelReference.getLocalId(),
                            localChannelReference.getRemoteId(), packet.getType(), buffer);
                    try {
                        localChannelReference.getNetworkChannel().writeAndFlush(message)
                                .await(Configuration.WAITFORNETOP);
                    } catch (InterruptedException e1) {
                    }
                    try {
                        session.setFinalizeTransfer(false, result);
                    } catch (OpenR66RunnerErrorException e) {
                    } catch (OpenR66ProtocolSystemException e) {
                    }
                }
                localChannelReference.getLocalChannel().close();
                continue;
            }
            try {
                buffer = packet.getLocalPacket(localChannelReference);
            } catch (OpenR66ProtocolPacketException e1) {
            }
            NetworkPacket message = new NetworkPacket(localChannelReference.getLocalId(),
                    localChannelReference.getRemoteId(), packet.getType(), buffer);
            localChannelReference.getNetworkChannel().writeAndFlush(message);
        }
    }

    /**
     * Close All Local Channels
     */
    public void closeAll() {
        logger.debug("close All Local Channels");
        localChannelGroup.close().awaitUninterruptibly();
    }

}