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

org.jdiameter.client.impl.transport.tcp.TCPClientConnection Maven / Gradle / Ivy

There is a newer version: 2.0.5
Show newest version
/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2016, TeleStax Inc. and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see 
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 *   JBoss, Home of Professional Open Source
 *   Copyright 2007-2011, Red Hat, Inc. and individual contributors
 *   by the @authors tag. See the copyright.txt in the distribution for a
 *   full listing of individual contributors.
 *
 *   This is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU Lesser General Public License as
 *   published by the Free Software Foundation; either version 2.1 of
 *   the License, or (at your option) any later version.
 *
 *   This software 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
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this software; if not, write to the Free
 *   Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 *   02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jdiameter.client.impl.transport.tcp;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.Configuration;
import org.jdiameter.api.InternalException;
import org.jdiameter.api.OverloadException;
import org.jdiameter.client.api.IMessage;
import org.jdiameter.client.api.io.IConnection;
import org.jdiameter.client.api.io.IConnectionListener;
import org.jdiameter.client.api.io.TransportError;
import org.jdiameter.client.api.io.TransportException;
import org.jdiameter.client.api.parser.IMessageParser;
import org.jdiameter.client.impl.parser.MessageParser;
import org.jdiameter.common.api.concurrent.IConcurrentFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author [email protected]
 * @author  Bartosz Baranowski 
 * @author  Alexandre Mendonca 
 */
@SuppressWarnings("all") //3rd party lib
public class TCPClientConnection implements IConnection {

    private static Logger logger = LoggerFactory.getLogger(TCPClientConnection.class);

    private final long createdTime;
    private TCPTransportClient client;
    //FIXME : requires JDK6 : protected LinkedBlockingDeque buffer = new LinkedBlockingDeque(64);
    private LinkedBlockingQueue buffer = new LinkedBlockingQueue(64);
    private IMessageParser parser;
    private Lock lock = new ReentrantLock();
    private ConcurrentLinkedQueue listeners = new ConcurrentLinkedQueue();

    // Cached value for connection key
    private String cachedKey = null;

    protected TCPClientConnection(IConcurrentFactory concurrentFactory, IMessageParser parser) {
        this.createdTime = System.currentTimeMillis();
        this.parser = parser;
        client = new TCPTransportClient(concurrentFactory, this);
    }

    public TCPClientConnection(Configuration config, IConcurrentFactory concurrentFactory, Socket socket,
            IMessageParser parser, String ref) throws Exception {
        this(concurrentFactory, parser);
        client = new TCPTransportClient(concurrentFactory, this);
        client.initialize(socket);
        client.start();
    }

    public TCPClientConnection(Configuration config, IConcurrentFactory concurrentFactory, InetAddress remoteAddress,
            int remotePort, InetAddress localAddress, int localPort, IMessageParser parser, String ref) {
        this(concurrentFactory, parser);
        client.setDestAddress(new InetSocketAddress(remoteAddress, remotePort));
        client.setOrigAddress(new InetSocketAddress(localAddress, localPort));
    }

    public TCPClientConnection(Configuration config, IConcurrentFactory concurrentFactory, InetAddress remoteAddress,
            int remotePort, InetAddress localAddress, int localPort, IConnectionListener listener,
            IMessageParser parser, String ref) {
        this(concurrentFactory, parser);
        client.setDestAddress(new InetSocketAddress(remoteAddress, remotePort));
        client.setOrigAddress(new InetSocketAddress(localAddress, localPort));
        listeners.add(listener);
    }

    @Override
    public long getCreatedTime() {
        return createdTime;
    }

    @Override
    public void connect() throws TransportException {
        try {
            getClient().initialize();
            getClient().start();
        } catch (IOException e) {
            throw new TransportException("Cannot init transport: ", TransportError.NetWorkError, e);
        } catch (Exception e) {
            throw new TransportException("Cannot init transport: ", TransportError.Internal, e);
        }
    }

    @Override
    public void disconnect() throws InternalError {
        //PCB added logging
        logger.debug("In disconnect for [{}]", this.getKey());
        try {
            if (getClient() != null) {
                getClient().stop();
            }
        } catch (Exception e) {
            throw new InternalError("Error while stopping transport: " + e.getMessage());
        }
    }

    @Override
    public void release() throws IOException {
        //PCB added logging
        logger.debug("In release for [{}]", this.getKey());
        try {
            if (getClient() != null) {
                getClient().release();
            }
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        } finally {
            parser = null;
            buffer.clear();
            remAllConnectionListener();
        }
    }

    @Override
    public void sendMessage(IMessage message) throws TransportException, OverloadException {
        try {
            if (getClient() != null) {
                //PCB added logging
                //Long receivedAt = timerMap.remove(message.getEndToEndIdentifier() + "_"+ message.getHopByHopIdentifier());
                //if (receivedAt != null) {
                //  long millis = System.currentTimeMillis() - receivedAt;
                //  if (millis >= 200) {
                //    logger.warn("Diameter Message processing took [{}]ms", millis);
                //  }
                //}
                getClient().sendMessage(parser.encodeMessage(message));
                //PCB added logging
                //if (receivedAt != null) {
                //  long millis = System.currentTimeMillis() - receivedAt;
                //  if (millis >= 200) {
                //    logger.warn("Diameter Message processing and sending took [{}]ms", millis);
                //  }
                //}
            }
        } catch (Exception e) {
            throw new TransportException("Cannot send message: ", TransportError.FailedSendMessage, e);
        }
    }

    protected TCPTransportClient getClient() {
        return client;
    }

    @Override
    public boolean isNetworkInitiated() {
        return false;
    }

    @Override
    public boolean isConnected() {
        return getClient() != null && getClient().isConnected();
    }

    @Override
    public InetAddress getRemoteAddress() {
        return getClient().getDestAddress().getAddress();
    }

    @Override
    public int getRemotePort() {
        return getClient().getDestAddress().getPort();
    }

    @Override
    public void addConnectionListener(IConnectionListener listener) {
        lock.lock();
        try {
            listeners.add(listener);
            if (buffer.size() != 0) {
                for (Event e : buffer) {
                    try {
                        //PCB added logging
                        logger.debug("Processing event from buffer");
                        onEvent(e);
                    } catch (AvpDataException e1) {
                        // ignore
                    }
                }
                buffer.clear();
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void remAllConnectionListener() {
        //PCB added logging
        logger.debug("Waiting to get lock in order to remove all listeners");
        lock.lock();
        try {
            //PCB added logging
            logger.debug("Removing all listeners on [{}]", this.getKey());
            listeners.clear();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void remConnectionListener(IConnectionListener listener) {
        lock.lock();
        try {
            //PCB added logging
            logger.debug("Removing listener [{}] on [{}]", listener.getClass().getName(), this.getKey());
            listeners.remove(listener);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean isWrapperFor(Class aClass) throws InternalException {
        return false;
    }

    @Override
    public  T unwrap(Class aClass) throws InternalException {
        return null;
    }

    @Override
    public String getKey() {
        if (this.cachedKey == null) {
            this.cachedKey = new StringBuffer("aaa://").append(getRemoteAddress().getHostName()).append(":")
                    .append(getRemotePort()).toString();
        }

        return this.cachedKey;
    }

    protected void onDisconnect() throws AvpDataException {
        onEvent(new Event(EventType.DISCONNECTED));
    }

    protected void onMessageReceived(ByteBuffer message) throws AvpDataException {
        if (logger.isDebugEnabled()) {
            if (logger.isTraceEnabled()) {
                String hex = MessageParser.byteArrayToHexString(message.array());
                logger.trace("Received message of size [{}]\n{}", new Object[] { message.array().length, hex });
            } else {
                logger.debug("Received message of size [{}]", message.array().length);
            }
        }
        onEvent(new Event(EventType.MESSAGE_RECEIVED, message));
    }

    protected void onAvpDataException(AvpDataException e) {
        try {
            onEvent(new Event(EventType.DATA_EXCEPTION, e));
        } catch (AvpDataException e1) {
            // ignore
        }
    }

    protected void onConnected() {
        try {
            onEvent(new Event(EventType.CONNECTED));
        } catch (AvpDataException e1) {
            // ignore
        }
    }

    protected void onEvent(Event event) throws AvpDataException {
        //PCB added logging
        logger.debug("In onEvent for connection [{}]. Getting lock", this.getKey());
        lock.lock();
        //PCB added logging
        logger.debug("Got lock");
        try {
            if (processBufferedMessages(event)) {
                for (IConnectionListener listener : listeners) {
                    //PCB added logging
                    if (logger.isDebugEnabled()) {
                        logger.debug("Passing event to listener. Event type is [{}]", event.type.toString());
                    }
                    switch (event.type) {
                        case CONNECTED:
                            listener.connectionOpened(getKey());
                            break;
                        case DISCONNECTED:
                            listener.connectionClosed(getKey(), null);
                            break;
                        case MESSAGE_RECEIVED:
                            //PCB added
                            IMessage msg = parser.createMessage(event.message);
                            //timerMap.put(msg.getEndToEndIdentifier() + "_"+ msg.getHopByHopIdentifier(), System.currentTimeMillis());
                            listener.messageReceived(getKey(), msg);
                            break;
                        case DATA_EXCEPTION:
                            listener.internalError(getKey(), null, new TransportException("Avp Data Exception:",
                                    TransportError.ReceivedBrokenMessage, event.exception));
                            break;
                    }
                }
            }
        } finally {
            logger.debug("Releasing lock and finished onEvent for connection [{}]", this.getKey());
            lock.unlock();
        }
    }

    //private static final Map timerMap = new ConcurrentHashMap();

    protected boolean processBufferedMessages(Event event) throws AvpDataException {
        if (listeners.size() == 0) {
            //PCB added logging
            logger.debug("listeners.size() == 0 on connection [{}]", this.getKey());
            try {
                buffer.add(event);
            } catch (IllegalStateException e) {
                logger.debug("Got IllegalStateException in processBufferedMessages");
                // FIXME : requires JDK6 : buffer.removeLast();
                Event[] tempBuffer = buffer.toArray(new Event[buffer.size()]);
                buffer.remove(tempBuffer[tempBuffer.length - 1]);
                buffer.add(event);
            }
            //PCB added logging
            logger.debug("processBufferedMessages is returning false");
            return false;
        } else {
            logger.debug("processBufferedMessages is returning true on connection [{}] as there are listeners", getKey());
            return true;
        }
    }

    //------------------ helper classes ------------------------
    private enum EventType {
        CONNECTED,
        DISCONNECTED,
        MESSAGE_RECEIVED,
        DATA_EXCEPTION
    }

    private static class Event {
        EventType type;
        ByteBuffer message;
        Exception exception;

        Event(EventType type) {
            this.type = type;
        }

        Event(EventType type, Exception exception) {
            this(type);
            this.exception = exception;
        }

        Event(EventType type, ByteBuffer message) {
            this(type);
            this.message = message;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy