Package org.apache.qpid.proton.driver.impl

Source Code of org.apache.qpid.proton.driver.impl.ConnectorImpl

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.qpid.proton.driver.impl;

import static org.apache.qpid.proton.driver.impl.ConnectorImpl.ConnectorState.UNINITIALIZED;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.qpid.proton.driver.Connector;
import org.apache.qpid.proton.driver.Listener;
import org.apache.qpid.proton.engine.Connection;
import org.apache.qpid.proton.engine.Sasl;
import org.apache.qpid.proton.engine.Sasl.SaslState;
import org.apache.qpid.proton.engine.Transport;
import org.apache.qpid.proton.engine.impl.TransportFactory;

class ConnectorImpl<C> implements Connector<C>
{
    public static int END_OF_STREAM = -1;
    private static int DEFAULT_BUFFER_SIZE = 64 * 1024;
    private static int readBufferSize = Integer.getInteger
        ("pn.receive_buffer_size", DEFAULT_BUFFER_SIZE);
    private static int writeBufferSize = Integer.getInteger
        ("pn.send_buffer_size", DEFAULT_BUFFER_SIZE);

    enum ConnectorState {UNINITIALIZED, OPENED, EOS, CLOSED};

    private final Sasl _sasl;
    private final DriverImpl _driver;
    private final Listener<C> _listener;
    private final SocketChannel _channel;
    private final Logger _logger = Logger.getLogger("proton.driver");
    private C _context;

    private Connection _connection;
    private SelectionKey _key;
    private ConnectorState _state = UNINITIALIZED;

    private ByteBuffer _readBuffer = ByteBuffer.allocate(readBufferSize);
    private int _bytesNotRead = 0;

    private int _bytesNotWritten = 0;
    private ByteBuffer _writeBuffer = ByteBuffer.allocate(writeBufferSize);
    private Transport _transport = null;

    ConnectorImpl(DriverImpl driver, Listener<C> listener, Sasl sasl, SocketChannel c, C context, SelectionKey key)
    {
        _driver = driver;
        _listener = listener;
        _channel = c;
        _sasl = sasl;
        _context = context;
        _key = key;
    }

    public void process()
    {
        if (_channel.isConnectionPending())
        {
            try
            {
                _channel.finishConnect();
            }
            catch (IOException io)
            {
                throw new RuntimeException("Exception will trying to complete connection",io);
            }
        }

        if (!_channel.isOpen())
        {
            _state = ConnectorState.CLOSED;
            return;
        }

        if (_key.isReadable())
        {
            read();
        }
        write();
    }

    void read()
    {
        try
        {
            int  bytesRead = _channel.read(_readBuffer);
            int consumed = 0;
            while (bytesRead > 0)
            {
                consumed = processInput(_readBuffer.array(), 0, bytesRead + _bytesNotRead);
                if (consumed < bytesRead)
                {
                    _readBuffer.compact();
                    _bytesNotRead = bytesRead - consumed;
                }
                else
                {
                    _readBuffer.rewind();
                    _bytesNotRead = 0;
                }
                bytesRead = _channel.read(_readBuffer);
            }
            if (bytesRead == -1)
            {
                _state = ConnectorState.EOS;
            }
        }
        catch (IOException e)
        {
            _logger.log(Level.SEVERE, "Exception when reading from connection",e);
        }
    }

    void write()
    {
        try
        {
            processOutput();
            if (_bytesNotWritten > 0)
            {
                _writeBuffer.limit(_bytesNotWritten);
                int written = _channel.write(_writeBuffer);
                if (_writeBuffer.hasRemaining())
                {
                    _writeBuffer.compact();
                    _bytesNotWritten = _bytesNotWritten - written;
                }
                else
                {
                    _writeBuffer.clear();
                    _bytesNotWritten = 0;
                }
                if (_bytesNotWritten > 0) // couldn't write all the data, need to know when we could write again.
                {
                    _key.interestOps(_key.interestOps() | SelectionKey.OP_WRITE);
                }
                else if ((_key.interestOps() & SelectionKey.OP_WRITE) != 0)
                {
                    _key.interestOps(_key.interestOps() & ~SelectionKey.OP_WRITE);                   
                }
            }
        }
        catch (IOException e)
        {
            _logger.log(Level.SEVERE, "Exception when writing to connection",e);
        }
    }

    int processInput(byte[] bytes, int offset, int size)
    {
        int read = 0;
        while (read < size)
        {
            offset = read;
            switch (_state)
            {
            case UNINITIALIZED:
                read += readSasl(bytes, offset, size - offset);
                if (isSaslDone())
                {
                    _state = _sasl.getState() == SaslState.PN_SASL_PASS ? ConnectorState.OPENED : ConnectorState.CLOSED;
                }
                break;
            case OPENED:
                read += readAMQPCommands(bytes, offset, size - offset);
                break;
            case EOS:
            case CLOSED:
                break;
            }
        }
        return read;
    }

    void processOutput()
    {
        switch (_state)
        {
        case UNINITIALIZED:
            writeSasl();
            if (isSaslDone())
            {
                _state = _sasl.getState() == SaslState.PN_SASL_PASS ? ConnectorState.OPENED : ConnectorState.CLOSED;
            }
            break;
        case OPENED:
            writeAMQPCommands();
            break;
        case EOS:
            writeAMQPCommands();
        case CLOSED:  // not a valid option
            //TODO
            break;
        }
    }

    int readAMQPCommands(byte[] bytes, int offset, int size)
    {
        int consumed = _transport.input(bytes, offset, size);
        if (consumed == END_OF_STREAM)
        {
            return size;
        }
        else
        {
            return consumed;
        }
    }

    void writeAMQPCommands()
    {
        int size = _writeBuffer.array().length - _bytesNotWritten;
        _bytesNotWritten += _transport.output(_writeBuffer.array(),
                _bytesNotWritten, size);
    }

    int readSasl(byte[] bytes, int offset, int size)
    {
        int consumed = _sasl.input(bytes, offset, size);
        if (consumed == END_OF_STREAM)
        {
            return size;
        }
        else
        {
            return consumed;
        }
    }

    void writeSasl()
    {
        int size = _writeBuffer.array().length - _bytesNotWritten;
        _bytesNotWritten += _sasl.output(_writeBuffer.array(),
                _bytesNotWritten, size);
    }

    public Listener<C> listener()
    {
        return _listener;
    }

    public Sasl sasl()
    {
        return _sasl;
    }

    public Connection getConnection()
    {
        return _connection;
    }

    public void setConnection(Connection connection)
    {
        _connection = connection;
        _transport = TransportFactory.getDefaultTransportFactory().transport(_connection);
    }

    public C getContext()
    {
        return _context;
    }

    public void setContext(C context)
    {
        _context = context;
    }

    public void close()
    {
        if (_state == ConnectorState.CLOSED)
        {
            return;
        }

        try
        {
            // If the connection was closed due to authentication error
            // then there might be data available to write on to the wire.
            writeSasl();
            writeAMQPCommands(); // write any closing commands
            _channel.close();
            _state = ConnectorState.CLOSED;
        }
        catch (IOException e)
        {
            _logger.log(Level.SEVERE, "Exception when closing connection",e);
        }
    }

    public boolean isClosed()
    {
        return _state == ConnectorState.EOS || _state == ConnectorState.CLOSED;
    }

    public void destroy()
    {
        close(); // close if not closed already
    }

    private void setState(ConnectorState newState)
    {
        _state = newState;
    }

    private boolean isSaslDone()
    {
        SaslState state = _sasl.getState();
        return state == SaslState.PN_SASL_PASS || state == SaslState.PN_SASL_FAIL;
    }
}
TOP

Related Classes of org.apache.qpid.proton.driver.impl.ConnectorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.