All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alachisoft.ncache.client.internal.communication.Connection Maven / Gradle / Ivy

package com.alachisoft.ncache.client.internal.communication;

import Alachisoft.NCache.Common.Communication.Secure.SecureStream;
import Alachisoft.NCache.Common.Net.Address;
import Alachisoft.NCache.Common.StopWatch;
import Alachisoft.NCache.Common.Streams.BufferedStream;
import Alachisoft.NCache.Common.Threading.Latch;
import Alachisoft.NCache.Common.Threading.Monitor;
import Alachisoft.NCache.Management.Statistics.StatisticsCounter;
import Util.ResponseHelper;

import com.alachisoft.ncache.client.internal.command.Command;
import com.alachisoft.ncache.client.internal.command.CommandOptions;
import com.alachisoft.ncache.client.internal.command.CommandResponse;
import com.alachisoft.ncache.client.internal.util.Logs;
import com.alachisoft.ncache.common.protobuf.ResponseProtocol;
import com.alachisoft.ncache.runtime.exceptions.CacheException;
import com.alachisoft.ncache.runtime.exceptions.CommandException;
import com.alachisoft.ncache.runtime.exceptions.ConnectionException;
import com.alachisoft.ncache.runtime.exceptions.OperationFailedException;
import com.alachisoft.ncache.runtime.util.HelperFxn;
import com.alachisoft.ncache.runtime.util.TimeSpan;
import com.google.common.base.Stopwatch;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

//  Copyright (c) 2020 Alachisoft
//  
//  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

//C# TO JAVA CONVERTER NOTE: There is no Java equivalent to C# namespace aliases:
//using Exception = System.Exception;

public final class Connection {

    public static final int CmdSizeHolderBytesCount = 10;
    public static final int ValSizeHolderBytesCount = 10;
    public static final int ValTypeHolderBytesCount = 4;
    public static final int TotSizeHolderBytesCount = ValSizeHolderBytesCount + CmdSizeHolderBytesCount;

    //10 bytes for the message size...
    private static final int MessageHeader = 10;

    //Threshold for maximum number of commands in a request.
    private static final int MaxCmdsThreshold = 100;
    public static long s_receiveBufferSize = 2048000;
    public static boolean WriteRequestIdInResponse = true;
    private static SocketAddress _bindIP;
    private final CommandQueue _queue = new CommandQueue();

    AtomicInteger inWriteQueue = new AtomicInteger(0);
    private ServerLostListener _serverLost = null;
    private boolean _isConnected = true;
    private Socket _primaryClient = null;
    private Socket _secondaryClient = null;
    private InetAddress _address;
    private String _ipAddress = "";
    private String _intendedRecipientIPAddress = "";
    private int _port = 0;
    private Object _connectionMutex = new Object();
    private Latch _connectionStatusLatch = new Latch(ConnectionStatus.Disconnected);
    private long _processID = ProcessHandle.current().pid();
    private String _cacheId;
    private Thread _primaryReceiveThread = null;
    private Thread _secondaryReceiveThread = null;
    private boolean _notificationsRegistered = false;
    private boolean _isReconnecting = false;
    private boolean _forcedDisconnect = false;
    private boolean _nagglingEnabled = false;
    private long _nagglingSize = 5 * 100 * 1024; //500k
    private NagglingManager _priNagglingMgr;
    private NagglingManager _secNagglingMgr;
    private Alachisoft.NCache.Common.DataStructures.Queue _msgQueue;
    private boolean _supportDualSocket = false;
    private Object _syncLock = new Object();
    private Logs _logger;
    private ResponseIntegrator _responseIntegrator;
    private Address _serverAddress;
    private Broker _container;
    private StatisticsCounter _perfStatsColl = null;
    private Object _socketSelectionMutex = new Object();
    private boolean _usePrimary = true;
    private boolean _requestLoggingEnabled;
    private boolean _optimized = false;
    private int _hostPort;
    private boolean _isIdle = false;
    private String _targetHost;
    private boolean _provideCert;
    private String[] tlsProtocols = {"TLSv1", "SSLv3"};
    private SecureStream privatePrimarySecureStream;
    private SecureStream privateSecondarySecureStream;
    private boolean privateIsSecured;
    private BufferedStream _bufferedStream;

    private AtomicInteger activeWriters = new AtomicInteger(0);

    private InetAddress _localEndpoint;
    private RequestInformation _requestInformation;
    private java.util.LinkedList _retrySendQueue = new java.util.LinkedList();
    private java.util.LinkedList _outstandingQueue = new java.util.LinkedList();

    public Connection(Broker container, Logs logs, StatisticsCounter perfStatsCollector, ResponseIntegrator rspIntegraotr, String bindIP, String cacheName) {
        _connectionStatusLatch = new Latch(ConnectionStatus.Disconnected);
        Initialize(container, logs, perfStatsCollector, rspIntegraotr, bindIP, cacheName);
    }

    public Connection(Broker broker) {
        this(broker, broker.getLogger(), broker._perfStatsColl, broker.getResponseIntegrator(), broker.getClientConfig().getBindIP(), broker.getCache().getName());
    }

    private void Initialize(Broker container, Logs logs, StatisticsCounter perfStatsCollector, ResponseIntegrator rspIntegraotr, String bindIP, String cacheName) {
        _serverLost = container;
        _isConnected = true;
        _primaryClient = null;
        _secondaryClient = null;
        _ipAddress = "";
        _intendedRecipientIPAddress = "";
        _port = 0;
        _connectionMutex = new Object();
        s_receiveBufferSize = 2048000;
        _processID = ProcessHandle.current().pid();
        _primaryReceiveThread = null;
        _secondaryReceiveThread = null;
        _notificationsRegistered = false;
        _isReconnecting = false;
        _forcedDisconnect = false;
        _nagglingEnabled = false;
        _nagglingSize = 5 * 100 * 1024; //500k
        _supportDualSocket = false;
        _syncLock = new Object();
        _perfStatsColl = null;
        _socketSelectionMutex = new Object();
        _usePrimary = true;
        _optimized = false;
        _isIdle = false;
        _container = container;
        _logger = logs;
        _responseIntegrator = rspIntegraotr;
        _cacheId = cacheName;
        _perfStatsColl = perfStatsCollector;
        _requestInformation = new RequestInformation();
        SetBindIP(bindIP);
        if (System.getProperty("EnableNaggling") != null) {
            _nagglingEnabled = Boolean.parseBoolean(System.getProperty("EnableNaggling"));
        }

        if (System.getProperty("NagglingSize") != null) {
            _nagglingSize = 1024 * Long.parseLong(System.getProperty("NagglingSize"));
        }

        if (System.getProperty("EnableDualSockets") != null) {
            _supportDualSocket = Boolean.parseBoolean(System.getProperty("EnableDualSockets"));
        }
    }


    public boolean getIsSecured() {
        return privateIsSecured;
    }

    public void setIsSecured(boolean value) {
        privateIsSecured = value;
    }

    public SecureStream getPrimarySecureStream() {
        return privatePrimarySecureStream;
    }

    public void setPrimarySecureStream(SecureStream value) {
        privatePrimarySecureStream = value;
    }

    public SecureStream getSecondarySecureStream() {
        return privateSecondarySecureStream;
    }

    public void setSecondarySecureStream(SecureStream value) {
        privateSecondarySecureStream = value;
    }

    public void UpdateBulkThreshold() {
        if (_requestInformation != null) {
            _requestInformation.UpdateBulkThreshold();
        }
    }

    public AtomicInteger getInWriteQueue() {
        return inWriteQueue;
    }


    public int GetBulkThreshold() {
        if (_requestInformation != null && _requestInformation.getBulkingEnabled()) {
            return _requestInformation.getBulkThreshold();
        }

        return 1;
    }

    public String getClientLocalIP() {
        String ip = "";
        if (getPrimaryClientSocket() != null) {
            if (getIsConnected()) {
                InetAddress add = getPrimaryClientSocket().getInetAddress();
                ip = add.getAddress().toString();
            }
        }
        return ip;
    }

    public boolean HasMinimumCommands() {
        return _queue.Count() >= GetBulkThreshold();
    }

    public boolean getOptimized() {
        return _optimized;
    }

    public void setOptimized(boolean value) {
        _optimized = value;
    }

    private boolean getDoNaggling() {
        return (_nagglingEnabled && _priNagglingMgr != null);
    }

    public boolean getSupportDualSocket() {
        return _supportDualSocket;
    }

    public Socket getPrimaryClientSocket() {
        return _primaryClient;
    }

    public Socket getSecondaryClientSocket() {
        return _secondaryClient;
    }

    public Latch getStatusLatch() {
        return _connectionStatusLatch;
    }

    /**
     * Checks if request logging is enabled on this server or not.
     */
    public boolean getRequestInquiryEnabled() {
        return _requestLoggingEnabled;
    }

    public void setRequestInquiryEnabled(boolean value) {
        _requestLoggingEnabled = value;
    }

    public boolean getIsConnected() {
        return _connectionStatusLatch.IsAnyBitsSet(ConnectionStatus.Connected);
    }

    /**
     * Get ip address of machine to which connection is made
     */
    public String getIpAddress() {
        return this._ipAddress;
    }

    public InetAddress getAddress() {
        return this._address;
    }

    public Address getServerAddress() {
        return this._serverAddress;
    }

    public void setServerAddress(Address value) {
        _serverAddress = value;
    }

    public String getIntendedRecipientIPAddress() {
        return this._intendedRecipientIPAddress;
    }

    public void setIntendedRecipientIPAddress(String value) {
        this._intendedRecipientIPAddress = value;
    }

    /**
     * Get port on which connection is made
     */
    public int getPort() {
        return this._port;
    }

    private void setPort(int value) {
        _port = value;
    }

    public boolean getNotifRegistered() {
        return this._notificationsRegistered;
    }

    public void setNotifRegistered(boolean value) {
        this._notificationsRegistered = value;
    }

    public boolean getIsReconnecting() {
        return this._isReconnecting;
    }

    public void setIsReconnecting(boolean value) {
        this._isReconnecting = value;
    }

    private Socket getCommunicationSocket() {
        Socket selectedSocket = _primaryClient;
        if (getSupportDualSocket()) {
            selectedSocket = _primaryClient;
            synchronized (_socketSelectionMutex) {
                if (!_usePrimary) {
                    selectedSocket = _secondaryClient;
                }
                _usePrimary = !_usePrimary;
            }
        }
        return selectedSocket;
    }

    public boolean getIsIdle() {
        synchronized (_connectionMutex) {
            return _isIdle;
        }
    }

    public void setIsIdle(boolean value) {
        synchronized (_connectionMutex) {
            _isIdle = value;
        }
    }

    //function that sets string provided to bindIP
    public void SetBindIP(String value) {

        if (value != null && !value.equals("")) {
            try {
                _bindIP = new InetSocketAddress(InetAddress.getByName(value.trim()), this._port);
            } catch (RuntimeException | UnknownHostException ex) {
            }
        }

    }

    @Override
    public boolean equals(Object obj) {
        Connection connection = (Connection) ((obj instanceof Connection) ? obj : null);
        return (connection != null && this.getIpAddress().equals(connection.getIpAddress()));
    }

    public void dispose() {
        if (_msgQueue != null && !_msgQueue.getClosed()) {
            _msgQueue.close(true);
            _msgQueue = null;
        }

        try {
            if (_priNagglingMgr != null && _priNagglingMgr.isAlive()) {
                _priNagglingMgr.interrupt();
            }

            if (_secNagglingMgr != null && _secNagglingMgr.isAlive()) {
                _secNagglingMgr.interrupt();
            }
        } catch (RuntimeException e) {
        }
    }

    public boolean connect(InetAddress ipAddress, int port) {

        int retry = 0;
        setOptimized(false);
        _ipAddress = ipAddress.toString();
        _address = ipAddress;
        _port = port;
        _serverAddress = new Address(ipAddress, port);
        synchronized (_connectionMutex) {
            _primaryClient = PrepareToConnect(_primaryClient);

            SocketAddress endPoint = null;
            endPoint = new InetSocketAddress(ipAddress, port);
            while (retry < 3) {
                try {
                    _primaryClient.connect(endPoint);
                    _bufferedStream = new BufferedStream(_primaryClient, false);

                    _localEndpoint = (_primaryClient.getInetAddress() instanceof InetAddress) ? _primaryClient.getLocalAddress() : null;
                    if (_logger != null && _logger.getIsErrorLogsEnabled()) {
						_logger.getNCacheLog().CriticalInfo("Connection.Connect", String.format("established TCP connection with %s:%d. Local endpoint: {%s}",_ipAddress,_port,_localEndpoint.getHostAddress()));

                    }


                    return true;
                } catch (RuntimeException | IOException e) {
                    if (_logger != null && _logger.getIsErrorLogsEnabled()) {
                        _logger.getNCacheLog().Error("Connection.Connect", " can not connect to " + ipAddress + ":" + port + ". error: " + e.toString());
                    }
                    if (e.getMessage().contains("A connection attempt failed because the connected party did not properly respond after a period of time")) {
                        retry++;
                    } else {
                        return false;
                    }
                }
            }
        }
        return false;
    }
    public void Init() {
        StartThread();
    }

    private Socket PrepareToConnect(Socket client) {
        client = new Socket();
        try {
            client.setTcpNoDelay(false);
        } catch (SocketException e) {
        }


        if (_bindIP != null) {
            try {
                client.bind(_bindIP);
            } catch (RuntimeException | IOException e) {
                throw new RuntimeException("Invalid bind-ip-address specified in client configuration");
            }
        }

        _forcedDisconnect = false;
        return client;
    }

    public void Secure(String targetHost, boolean provideCert) {
        synchronized (_connectionMutex) {
            if (!getIsSecured()) {
                setPrimarySecureStream(new SecureStream(_primaryClient));
                getPrimarySecureStream().InitializeAsClient(_targetHost = targetHost, _provideCert = provideCert);
                setIsSecured(true);
            }
        }
    }

    public void ConnectSecondarySocket(InetAddress address, int port) {
        _secondaryClient = PrepareToConnect(_secondaryClient);
        SocketAddress socketAddress = new InetSocketAddress(address, port);

        try {
            _secondaryClient.connect(socketAddress);
        } catch (RuntimeException | IOException e) {
            if (_logger.getIsErrorLogsEnabled()) {
                _logger.getNCacheLog().Error("Connection.Connect", " can not connect to " + address + ":" + port + ". error: " + e.toString());
            }
        }

        synchronized (_connectionMutex) {
            if (getIsSecured()) {
                setSecondarySecureStream(new SecureStream(_secondaryClient));
                getSecondarySecureStream().InitializeAsClient(_targetHost, _provideCert);
            }
        }
    }

    /**
     * it transfers the existing connection to a new connection without changing the object container
     *
     * @param container
     * @param perfStatsCollector
     * @param rspIntegraotr
     * @param bindIP
     * @param cacheName
     * @param ipAddress
     * @param cachePort
     * @return
     */
    public boolean SwitchTo(Broker container, Logs logs, StatisticsCounter perfStatsCollector, ResponseIntegrator rspIntegraotr, String bindIP, String cacheName, InetAddress ipAddress, int cachePort) {
        int oldPort = getPort();

        Initialize(container, logs, perfStatsCollector, rspIntegraotr, bindIP, cacheName);
        if (this.connect(getAddress(), cachePort)) {
            _hostPort = cachePort;
            this.setPort(oldPort);
            this._serverAddress = new Address(ipAddress, oldPort);
            return true;
        }

        this.setPort(oldPort);
        this._serverAddress = new Address(ipAddress, oldPort);

        return false;
    }

    public void Disconnect() {
        Disconnect(true);
    }

    public void Disconnect(boolean changeStatus) {
        _forcedDisconnect = true;
        if (changeStatus) {
            this._connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
        }

        if (_primaryReceiveThread != null && _primaryReceiveThread.getState() != Thread.State.TERMINATED) {
            _primaryReceiveThread.interrupt();
            _primaryReceiveThread = null;
        }

        if (_primaryClient != null && _primaryClient.isConnected()) {
            try {
                _primaryClient.shutdownInput();
                _primaryClient.shutdownOutput();
            } catch (SocketException e3) {
            } catch (IOException e) {
            }finally {
                try {
                    _primaryClient.close();
                } catch (IOException e) {
                }
            }
        }

        //dispose the secondary socket
        if (_secondaryReceiveThread != null && _secondaryReceiveThread.getState() != Thread.State.TERMINATED) {
            _secondaryReceiveThread.interrupt();
            _secondaryReceiveThread = null;
        }

        if (_secondaryClient != null && _secondaryClient.isConnected()) {
            try {
                _secondaryClient.shutdownInput();
                _secondaryClient.shutdownOutput();
            } catch (SocketException e3) {
            } catch (IOException e) {
            }finally {
                try {
                    _secondaryClient.close();
                } catch (IOException e) {
                }
            }
        }

        setIsSecured(false);
        setSecondarySecureStream(null);
        setPrimarySecureStream(null);
    }

    public CommandResponse RecieveCommandResponse(boolean _usingSecondary) throws ConnectionException {
        if (getIsSecured()) {
            if (_usingSecondary) {
                return SecureRecieveCommandResponse(getSecondarySecureStream());
            }

            return SecureRecieveCommandResponse(getPrimarySecureStream());
        }

        if (_usingSecondary) {
            return RecieveCommandResponse(_secondaryClient);
        }

        return RecieveCommandResponse(_primaryClient);
    }

    private CommandResponse RecieveCommandResponse(Socket client) throws ConnectionException {

        CommandResponse cmdRespose = null;
        try {
            byte[] value = AssureRecieve(client, getOptimized());

            ResponseProtocol.Response response = ResponseProtocol.Response.parseFrom(value);

            if (response != null && response.getResponseType() == ResponseProtocol.Response.Type.RESPONSE_FRAGMENT) {
                response = _responseIntegrator.AddResponseFragment(this._serverAddress, response.getGetResponseFragment());
            }

            if (response != null) {
                cmdRespose = new CommandResponse(false, new Address());
                cmdRespose.setCacheId(this._cacheId);
                cmdRespose.setResult(response);
            }
        } catch (IOException | CacheException e) {
            throw new ConnectionException(e.getMessage(), this._serverAddress.getIpAddress(), this._serverAddress.getPort());
        }
        return cmdRespose;
    }

    private CommandResponse SecureRecieveCommandResponse(SecureStream client) throws ConnectionException {
        byte[] value = null;
        CommandResponse cmdRespose = null;
        try {
            value = AssureSecureRecieve(client, getOptimized());

            ResponseProtocol.Response response = ResponseProtocol.Response.parseFrom(value);

            if (response != null && response.getResponseType() == ResponseProtocol.Response.Type.RESPONSE_FRAGMENT) {
                response = _responseIntegrator.AddResponseFragment(this._serverAddress, response.getGetResponseFragment());
            }

            if (response != null) {
                cmdRespose = new CommandResponse(false, new Address());
                cmdRespose.setCacheId(this._cacheId);
                cmdRespose.setResult(response);
            }
        } catch (IOException | CacheException e) {
            throw new ConnectionException(e.getMessage(), this._serverAddress.getIpAddress(), this._serverAddress.getPort());
        }
        return cmdRespose;
    }

    public void AssureSendDirect(byte[] buffer, Socket client, boolean checkConnected) throws IOException, ConnectionException {
        int dataSent = 0, dataLeft = buffer.length;
        synchronized (_connectionMutex) {
            if (checkConnected && _connectionStatusLatch.IsAnyBitsSet((byte) (ConnectionStatus.Disconnected | ConnectionStatus.Connecting))) {
                throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());
            }
            synchronized (client.getOutputStream()) {
                try {
                    client.getOutputStream().write(buffer);
                    client.getOutputStream().flush();
                } catch (IOException se) {
                    if (_logger.getIsErrorLogsEnabled()) {
                        _logger.getNCacheLog().Error("Connection.AssureSendDirect() ", se.toString());
                    }
                    _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
                    throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());

                }
            }
        }
    }

    public void AssureSendToBufferedStream(byte[] buffer, Socket client, boolean checkConnected) throws ConnectionException {
        int dataSent = 0, dataLeft = buffer.length;
        if (checkConnected && _connectionStatusLatch.IsAnyBitsSet((byte) (ConnectionStatus.Disconnected | ConnectionStatus.Connecting))) {
            throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());
        }

        try {
            _bufferedStream.write(buffer, dataSent, dataLeft);
            setIsIdle(false);
        } catch (IOException se) {
            if (_logger.getIsErrorLogsEnabled()) {
                _logger.getNCacheLog().Error("Connection.AssureSend() ", se.toString());
            }
            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
            throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());

        }
    }

    public void AssureSendNaggledData(Socket client, byte[] buffer, int bytesToSent, boolean checkConnected) throws ConnectionException {
        int dataSent = 0, dataLeft = bytesToSent;
        synchronized (_connectionMutex) {

            if (checkConnected && _connectionStatusLatch.IsAnyBitsSet((byte) (ConnectionStatus.Disconnected | ConnectionStatus.Connecting))) {
                throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());
            }

            while (dataSent < bytesToSent) {
                try {
                    dataLeft = bytesToSent - dataSent;
                    client.getOutputStream().write(buffer, dataSent, dataLeft);
                } catch (IOException se) {
                    if (_logger.getIsErrorLogsEnabled()) {
                        _logger.getNCacheLog().Error("Connection.AssureSendDirect() ", se.toString());
                    }
                    _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
                    throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());
                }
            }
            setIsIdle(false);
        }
    }

    private byte[] AssureRecieve(Socket client, boolean optimized) throws IOException, ConnectionException {
        byte[] buffer = new byte[CmdSizeHolderBytesCount + (optimized ? CmdSizeHolderBytesCount : 0)];

        AssureRecieve(buffer, client);

        String s = new String(buffer, 0 + (optimized ? CmdSizeHolderBytesCount : 0), CmdSizeHolderBytesCount);

        int commandSize = 0;
        try
        {
            commandSize = Integer.parseInt(s.trim());
        }
        catch (NumberFormatException ex){ }

        if (commandSize == 0) {
            return new byte[0];
        }

        buffer = new byte[commandSize];
        AssureRecieve(buffer, client);
        return buffer;
    }

    private void AssureRecieve(byte[] buffer, Socket client) throws ConnectionException {
        int bytesRecieved = 0;
        try
        {
            synchronized (client.getInputStream())
            {
                do
                {
                    bytesRecieved += client.getInputStream().read(buffer, bytesRecieved, (buffer.length - bytesRecieved));
                }
                while (bytesRecieved != -1 &&  bytesRecieved < buffer.length);
            }
        }
        catch (Exception e)
        {
            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
            throw new ConnectionException("Disconnected: can not receive." + e.toString() );
        }
        setIsIdle(false);
    }

    public void AssureSendSecure(byte[] buffer, SecureStream stream, boolean checkConnected) throws ConnectionException {
        synchronized (_connectionMutex) {
            if (checkConnected && _connectionStatusLatch.IsAnyBitsSet((byte) (ConnectionStatus.Disconnected | ConnectionStatus.Connecting))) {
                throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());
            }
            try {
                stream.write(buffer);
            } catch (IOException se) {

                if (_logger.getIsErrorLogsEnabled()) {
                    _logger.getNCacheLog().Error("Connection.AssureSendSecure() ", se.toString());
                }
                _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
                throw new ConnectionException(_serverAddress.getIpAddress(), _serverAddress.getPort());

            }
            setIsIdle(false);
        }
    }

    private byte[] AssureSecureRecieve(SecureStream client, boolean optimized) throws IOException, ConnectionException {
        byte[] buffer = new byte[CmdSizeHolderBytesCount + (optimized ? CmdSizeHolderBytesCount : 0)];

        AssureSecureRecieve(buffer, client);

        String s = new String(buffer, 0 + (optimized ? CmdSizeHolderBytesCount : 0), CmdSizeHolderBytesCount);

        int commandSize = 0;
        try
        {
            commandSize = Integer.parseInt(s.trim());
        }
        catch (NumberFormatException ex){ }

        if (commandSize == 0) {
            return new byte[0];
        }

        buffer = new byte[commandSize];
        AssureSecureRecieve(buffer, client);
        return buffer;
    }

    private void AssureSecureRecieve(byte[] buffer, SecureStream client) throws IOException, ConnectionException {
        int bytesRecieved = 0;
        try
        {
            synchronized (client.getInputStream())
            {
                do
                {
                    bytesRecieved += client.getInputStream().read(buffer, bytesRecieved, (buffer.length - bytesRecieved));
                }
                while (bytesRecieved != -1 &&  bytesRecieved < buffer.length);
            }
        }
        catch (Exception e)
        {
            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
            throw new ConnectionException("Disconnected: can not receive." + e.toString() );
        }
        setIsIdle(false);
    }

    public void SendCommand(byte[] commandBytes, boolean checkConnected) throws ConnectionException {
        if (_perfStatsColl.getIsEnabled()) {
            _perfStatsColl.IncrementClientRequestsPerSecStats(1);
        }

        byte[] dataWithSize = new byte[commandBytes.length + MessageHeader];
        byte[] lengthBytes = HelperFxn.toBytes(commandBytes.length);

        System.arraycopy(lengthBytes, 0, dataWithSize, 0, lengthBytes.length);
        System.arraycopy(commandBytes, 0, dataWithSize, MessageHeader, commandBytes.length);

        if (getDoNaggling()) {
            _msgQueue.add(commandBytes);
        } else {
            if (getIsSecured()) {
                if (getSupportDualSocket()) {
                    SecureStream selectedStream = getPrimarySecureStream();
                    synchronized (_socketSelectionMutex) {
                        if (!_usePrimary) {
                            selectedStream = getSecondarySecureStream();
                        }
                        _usePrimary = !_usePrimary;
                    }
                    AssureSendSecure(dataWithSize, selectedStream, checkConnected);
                } else {
                    AssureSendSecure(dataWithSize, getPrimarySecureStream(), checkConnected);
                }
            } else {
                if (getSupportDualSocket()) {
                    Socket selectedSocket = _primaryClient;
                    synchronized (_socketSelectionMutex) {
                        if (!_usePrimary) {
                            selectedSocket = _secondaryClient;
                        }
                        _usePrimary = !_usePrimary;
                    }
                    AssureSendToBufferedStream(dataWithSize, selectedSocket, checkConnected);
                } else {
                    AssureSendToBufferedStream(dataWithSize, _primaryClient, checkConnected);
                }
            }
        }
    }

    public void StartThread() {

        _primaryReceiveThread = new Thread(() -> {
            Socket client = getIsSecured() ? getPrimarySecureStream() : _primaryClient;
            RecieveThread(client);
        });
        _primaryReceiveThread.setPriority(5);
        _primaryReceiveThread.setDaemon(true); //Taimoor:Now application can exit without calling dispose()
        _primaryReceiveThread.start();

        if (getSupportDualSocket()) {


            _secondaryReceiveThread = new Thread(() -> {
                Socket client = getIsSecured() ? getSecondarySecureStream() : _secondaryClient;
                RecieveThread(client);
            });
            _secondaryReceiveThread.setPriority(5);
            _secondaryReceiveThread.setDaemon(true); //Taimoor:Now application can exit without calling dispose()
            _secondaryReceiveThread.start();
        }
    }

    private void RecieveThread(Socket client) {
        Socket clientSocket = (client instanceof Socket) ? client : null;
        SecureStream stream = (SecureStream) ((client instanceof SecureStream) ? client : null);
        int count;
        while (true) {
            try {
                count = 0;
                byte[] cmdBytes = clientSocket != null ? AssureRecieve(clientSocket, false) : AssureSecureRecieve(stream, false);
                 InputStream targetStream = new ByteArrayInputStream(cmdBytes);
                try {
                    while (targetStream.available() > 0) {
                        ProcessResponse(targetStream);
                        count++;
                    }
                } finally {
                    targetStream.close();
                }
                //      Broker.AverageCounterRecieve.IncrementBy(count);

            }
            catch(SocketException se)
            {
                OnConnectionBroken(se,ExType.Socket);
                break;
            }
            catch (IOException ie) {
                //System.IOException is going to thrown in the case SslStream when the connection gets forcibly closed.
                //Thus we are going to handle it as a SocketException....
                OnConnectionBroken(ie, ExType.Socket);
                break;
            }
            catch(ConnectionException ce)
            {
                OnConnectionBroken(ce,ExType.Connection);
                break;
            }
            catch (Exception e) {
                OnConnectionBroken(e, ExType.General);
                break;
            }
        }
    }

    private void OnConnectionBroken(Exception e, ExType exType) {

            if (_logger != null) {
                switch (exType) {
                    case Socket:
                    case Connection:
                        if (_forcedDisconnect) {
                            if (_logger.getIsErrorLogsEnabled()) {
                                _logger.getNCacheLog().Error("Connection.ReceivedThread", "Connection with server lost gracefully");
                            }
                        } else {
                            if (_logger.getIsErrorLogsEnabled()) {
                                _logger.getNCacheLog().Error("Connection.ReceivedThread", "An established connection with the server " + _serverAddress + " is lost. Error:" + e.toString());
                            }
                        }

                        if (!_forcedDisconnect) {
                            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
                        }
                        _primaryReceiveThread = null;
                        _serverLost.OnServerLost(_serverAddress, _forcedDisconnect);
                        break;
                    case Interrupt:
                    case Abort:
                        if (_forcedDisconnect) {
                            if (_logger.getIsErrorLogsEnabled()) {
                                _logger.getNCacheLog().Error("Connection.ReceivedThread", "Connection with server lost gracefully");
                                _logger.getNCacheLog().Flush();
                            }
                        }
                        if (!_forcedDisconnect) {
                            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
                        }
                        _serverLost.OnServerLost(_serverAddress, _forcedDisconnect);
                        break;
                    case General:
                        if (_logger.getIsErrorLogsEnabled()) {
                            _logger.getNCacheLog().Error("Connection.ReceivedThread", e.toString());
                            _logger.getNCacheLog().Flush();
                        }
                        if (!_forcedDisconnect) {
                            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
                        }
                        _serverLost.OnServerLost(_serverAddress, _forcedDisconnect);
                        break;
                }
            }
            setIsSecured(false);

    }

    /**
     * Reads a single single response from the stream and processes it.
     *
     * @param stream
     */
    private void ProcessResponse(InputStream stream) throws IOException, OperationFailedException {
        ResponseProtocol.Response response;
        long requestId = 0;

        if (WriteRequestIdInResponse) {
            byte[] requestIdBytes = new byte[CmdSizeHolderBytesCount];
            stream.read(requestIdBytes, 0, requestIdBytes.length);
            String s = new String(requestIdBytes, 0, requestIdBytes.length);
            requestId = Long.parseLong(s.trim());
        }

        byte[] responseTypeBytes = new byte[ValTypeHolderBytesCount];
        stream.read(responseTypeBytes, 0, responseTypeBytes.length);
        String responseTypeString = new String(responseTypeBytes, 0, responseTypeBytes.length);
        int requestType = Integer.parseInt(responseTypeString.trim());
        ResponseProtocol.Response.Type responseType = ResponseProtocol.Response.Type.valueOf(requestType);

        byte[] cmdSzBytes = new byte[CmdSizeHolderBytesCount];
        stream.read(cmdSzBytes, 0, CmdSizeHolderBytesCount);
        String s = new String(cmdSzBytes, 0, cmdSzBytes.length);
        int commandSize = Integer.parseInt(s.trim());

        byte[] cmdBytes = new byte[commandSize];
        stream.read(cmdBytes, 0, commandSize);

        CommandResponse cmdRespose = new CommandResponse(false, new Address());
        cmdRespose.setCacheId(_cacheId);
        cmdRespose.setSourceAddress(this.getServerAddress());

        if (WriteRequestIdInResponse) {
            cmdRespose.setNeedsDeserialization(true);
            cmdRespose.setRequestId(requestId);
            cmdRespose.setRawResult(cmdBytes);
            cmdRespose.setType(responseType);
        } else {
            InputStream targetStream = new ByteArrayInputStream(cmdBytes);
            try {
                response = ResponseHelper.deserializeResponse(responseType, targetStream);
            } finally {
                targetStream.close();
            }

            cmdRespose.setNeedsDeserialization(false);
            if (response != null) {
                cmdRespose.setResult(response);
            }
        }

        if (_perfStatsColl.getIsEnabled()) {
            _perfStatsColl.incrementClientResponsesPerSecStats(1);
        }

        if (cmdRespose != null) {
            _container.ProcessResponse(cmdRespose, _serverAddress);
        }
    }

    public boolean TryEnqueue(Command command, boolean checkConnected) {
        if (_requestInformation != null) {
            _requestInformation.AddRequest();
        }

        boolean reqWrite = _queue.Push(command);

        if (reqWrite) {
            _container.getSocketManagerHandler().RequestWrite(this, false);
        }
        return true;
    }

    public WriteResult WriteQueue(int maxWork) {
        boolean weAreWriter = false;

        try {

            weAreWriter = activeWriters.compareAndExchange(0, 1) == 0;
            if (!weAreWriter) {
                return WriteResult.CompetingWriter;
            }

            if (_bufferedStream == null) {
                return WriteResult.NoConnection;
            }

            int count = 0;
            while (true) {

                Command command = _queue.Dequeue();

                if (command == null) {
                    if (count == 0) {
                        Flush();
                        //if (_container.getLogger().getIsErrorLogsEnabled()) {
                          //  _container.getLogger().getNCacheLog().Info("Connection.WriteQueue", "Flushed");
                        //}
                        return WriteResult.NothingToDo;
                    }
                    return WriteResult.QueueEmptyAfterWrite;
                }

                command.setFinalDestinationAddress(_serverAddress);
                if (command.getPulseOnSend()) {
                    _retrySendQueue.offer(command);
                } else {
                    _outstandingQueue.offer(command);
                }

                long prevWriteCount = _bufferedStream.getWriteCount();

                if (!WriteMessageDirect(command)) {
                    return WriteResult.NoConnection;
                }

                long currentWriteCount = _bufferedStream.getWriteCount();

              //  if (_container.getLogger().getIsErrorLogsEnabled()) {
                  //  _container.getLogger().getNCacheLog().Error("Connection.WriteQueue", "RequestID :" + command.getRequestId() + " " + command.getCommandName() + " with Previous Count:"+prevWriteCount+" and current write count:"+currentWriteCount);
               // }
                if (currentWriteCount > prevWriteCount) {
                    OnWriteSuccess();
                }

                count++;
                if (maxWork > 0 && count >= maxWork) {
                    Flush();
                    break;
                }
            }
        } catch (IOException ce) {

            if (_logger.getIsErrorLogsEnabled())
                _logger.getNCacheLog().Error("Connection.AssureSend().IOException ", ce.toString());

            _connectionStatusLatch.SetStatusBit(ConnectionStatus.Disconnected, ConnectionStatus.Connected);
            SendError sendError = new SendError(ErrorType.ConnectionException, new ConnectionException(this._serverAddress.getIpAddress(), this._serverAddress.getPort()));
            OnWriteFailure(sendError);

        } catch (ConnectionException exs) {
            SendError sendError = new SendError(ErrorType.ConnectionException, exs);
            OnWriteFailure(sendError);
        } catch (Exception ex) {
            SendError sendError = new SendError(ErrorType.Exception, ex);
            OnWriteFailure(sendError);
        } finally {
            if (weAreWriter) {
                activeWriters.set(0);
            }
        }

        return _queue.Any() ? WriteResult.MoreWork : WriteResult.QueueEmptyAfterWrite;
    }

    private void OnWriteSuccess() {
        PulseRetrySendCommands(null);
        _outstandingQueue.clear();
    }

    private void OnWriteFailure(SendError sendError) {
        PulseRetrySendCommands(sendError);
        SendFailureResponse(sendError);
    }

    public void Flush() throws IOException {
        _bufferedStream.flush();
        OnWriteSuccess();
    }

    private void PulseRetrySendCommands(SendError sendError) {
        while (_retrySendQueue.size() != 0) {
            Command command = _retrySendQueue.poll();
            synchronized (command) {
                command.setSendError(sendError);
                command.setSentOverWire(true);
                Monitor.pulse(command);
            }
        }
    }

    private void SendFailureResponse(SendError sendError) {
        while (_outstandingQueue.size() != 0) {
            Command command = _outstandingQueue.poll();
            command.setSendError(sendError);
            Request request = _container.GetRequest(command.getRequestId());
            if (request == null) {
                continue;
            }
            synchronized (request) {
                request.InitializeFailedSendResponse(this._serverAddress, command);
                Monitor.pulse(request);
            }
        }
    }

    public boolean ConfirmRemoveFromWriteQueue() {
        synchronized (_queue.getSyncLock()) {
            if (_queue.Count() == 0) {
                inWriteQueue.set(0);
                return true;
            }
        }
        return false;
    }

    private boolean WriteMessageDirect(Command command) throws CommandException, ConnectionException, IOException {
        SendCommand(command.toByte(command.getAcknowledgmentId(), getRequestInquiryEnabled()), true);
        return true;
    }

    public void WaitUntillPipelineFilled() {
        int iterations = 10;
        int timeout = Extensions.getTimeout();

        StopWatch watch = new StopWatch();
        watch.start();

        long elapsedTime = 0;
        while (timeout > elapsedTime && !HasMinimumCommands()) {
            int spinWaitCount = 0;
            while (spinWaitCount++ < iterations) {
                Thread.onSpinWait();
            }
            elapsedTime = watch.getElapsedTime();
            if (elapsedTime < 10) {
                iterations *= 2;
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy