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

com.swiftmq.amqp.v100.client.Connection Maven / Gradle / Ivy

/*
 * Copyright 2019 IIT Software GmbH
 *
 * IIT Software GmbH 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 com.swiftmq.amqp.v100.client;

import com.swiftmq.amqp.AMQPContext;
import com.swiftmq.amqp.OutboundHandler;
import com.swiftmq.amqp.Writable;
import com.swiftmq.amqp.integration.Tracer;
import com.swiftmq.amqp.v100.client.po.POAuthenticate;
import com.swiftmq.amqp.v100.client.po.POOpen;
import com.swiftmq.amqp.v100.client.po.POProtocolRequest;
import com.swiftmq.amqp.v100.client.po.POSendClose;
import com.swiftmq.amqp.v100.transport.AMQPFrame;
import com.swiftmq.amqp.v100.types.AMQPString;
import com.swiftmq.amqp.v100.types.AMQPSymbol;
import com.swiftmq.amqp.v100.types.Util;
import com.swiftmq.net.PlainSocketFactory;
import com.swiftmq.net.SocketFactory;
import com.swiftmq.net.SocketFactory2;
import com.swiftmq.net.client.BlockingConnection;
import com.swiftmq.net.client.ExceptionHandler;
import com.swiftmq.net.protocol.ProtocolInputHandler;
import com.swiftmq.net.protocol.ProtocolOutputHandler;
import com.swiftmq.net.protocol.raw.RawOutputHandler;
import com.swiftmq.swiftlet.threadpool.AsyncTask;
import com.swiftmq.swiftlet.threadpool.ThreadPool;
import com.swiftmq.tools.collection.ArrayListTool;
import com.swiftmq.tools.concurrent.Semaphore;
import com.swiftmq.tools.pipeline.POObject;
import com.swiftmq.tools.queue.SingleProcessorQueue;
import com.swiftmq.tools.util.DataStreamOutputStream;

import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Representation of an AMQP connection.
 * 

* The actual connect is done by calling the "connect()" method. Any attribute change, e.g. buffer size, must be done before * this method is called. * * @author IIT Software GmbH, Bremen/Germany, (c) 2011, All Rights Reserved */ public class Connection implements ExceptionHandler { AMQPContext ctx = null; Tracer fTracer = null; String hostname = null; String openHostname = System.getProperty("swiftmq.amqp.open.hostname"); int port; String mechanism = "PLAIN"; String userName = null; String password = null; long idleTimeout = Integer.MAX_VALUE; long maxFrameSize = Integer.MAX_VALUE; com.swiftmq.net.client.Connection networkConnection = null; ConnectionDispatcher connectionDispatcher = null; ThreadPool connectionPool = null; ConnectionQueue connectionQueue = null; ConnectionTask connectionTask = null; DataStreamOutputStream dos = null; volatile boolean closed = false; ArrayList localChannels = new ArrayList(); ArrayList remoteChannels = new ArrayList(); Lock lock = new ReentrantLock(); boolean doAuth = true; String containerId = null; boolean containerIdSet = false; boolean connected = false; int inputBufferSize = 131072; int inputBufferExtendSize = 65536; int outputBufferSize = 131072; int outputBufferExtendSize = 65536; SocketFactory socketFactory = new PlainSocketFactory(); ExceptionListener exceptionListener = null; /** * Creates a Connection and uses SASL for authentication. * * @param ctx AMQP context * @param hostname Hostname. * @param port Port * @param userName Username * @param password Password */ public Connection(AMQPContext ctx, String hostname, int port, String userName, String password) { this.ctx = ctx; this.hostname = hostname; this.port = port; this.userName = userName; this.password = password; String myHostname = "unknown"; try { myHostname = InetAddress.getLocalHost().getHostName(); } catch (Exception e) { } containerId = UUID.randomUUID().toString() + "@" + myHostname; fTracer = ctx.getFrameTracer(); } /** * Creates a Connection and either does an anonymous login via SASL (doAuth is true) or * avoids SASL and starts directly with the AMQP protocol (doAuth is false). * * @param ctx AMQP context * @param hostname Hostname * @param port Port * @param doAuth do authentication as anonymous via SASL or do not use SASL at all */ public Connection(AMQPContext ctx, String hostname, int port, boolean doAuth) { this(ctx, hostname, port, null, null); this.doAuth = doAuth; } private void verifyState() throws ConnectionClosedException { if (closed) throw new ConnectionClosedException("Connection is closed"); } /** * Returns the exception listener * * @return exception listener */ public ExceptionListener getExceptionListener() { return exceptionListener; } /** * Sets the exception listener. * * @param exceptionListener exception listener */ public void setExceptionListener(ExceptionListener exceptionListener) { this.exceptionListener = exceptionListener; } /** * Returns the maximum frame size * * @return max frame size */ public long getMaxFrameSize() { return maxFrameSize; } /** * Sets the maximum frame size. Default is Integer.MAX_VALUE. * * @param maxFrameSize max frame size */ public void setMaxFrameSize(long maxFrameSize) { this.maxFrameSize = maxFrameSize; } /** * Sets the SASL mechanisms to use for authentication. If SwiftMQ is the remote server, * it supports PLAIN, CRAM-MD5, Digest-MD5. For ANONYMOUS (which SwiftMQ supports as well) * please use the resp. constructor of this connection. * * @param mechanism the SASL mechanisms to use */ public void setMechanism(String mechanism) { this.mechanism = mechanism; } /** * Returns the container id * * @return container id */ public String getContainerId() { return containerId; } /** * Sets the container id. A container id is automatically and randomly generated if it is not set. * For SwiftMQ the container id is used to identify durable subscribers. It is the same * as the client id in JMS. * * @param containerId container id */ public void setContainerId(String containerId) { this.containerId = containerId; containerIdSet = true; } /** * Returns the hostname set in the open frame. * * @return hostname */ public String getOpenHostname() { return openHostname; } /** * Sets (overwrites) the hostname that will be set in the open frame. Some AMQP brokers * may use this field to select a backend service and use this field as a "virtual host". * * @param openHostname open hostname */ public void setOpenHostname(String openHostname) { this.openHostname = openHostname; } /** * Sets the idle timeout. Default is Long.MAX_VALUE. *

* If a connection does not send/receive data within this interval, the connection is closed. The SwiftMQ client * sends heart beat messages every idletimeout/2 millisecond. * * @param idleTimeout idle timeout in milliseconds */ public void setIdleTimeout(long idleTimeout) { this.idleTimeout = idleTimeout; } /** * Returns the input buffer size * * @return input buffer size */ public int getInputBufferSize() { return inputBufferSize; } /** * Sets the input buffer size. This is the size of the network buffer SwiftMQ uses to receive frames. Default is 131072 bytes. * * @param inputBufferSize input buffer size */ public void setInputBufferSize(int inputBufferSize) { this.inputBufferSize = inputBufferSize; } /** * Returns the input buffer extend size * * @return input buffer extend size */ public int getInputBufferExtendSize() { return inputBufferExtendSize; } /** * Sets the input buffer extend size. This is the size on which the input buffer is extended if its size isn't sufficient to receive a frame. * Default is 65536 bytes. * * @param inputBufferExtendSize input buffer extend size */ public void setInputBufferExtendSize(int inputBufferExtendSize) { this.inputBufferExtendSize = inputBufferExtendSize; } /** * Returns the output buffer size * * @return output buffer size */ public int getOutputBufferSize() { return outputBufferSize; } /** * Sets the output buffer size. This is the size of the network buffer SwiftMQ uses to send frames. Default is 131072 bytes. * * @param outputBufferSize output buffer size */ public void setOutputBufferSize(int outputBufferSize) { this.outputBufferSize = outputBufferSize; } /** * Returns the output buffer extend size * * @return output buffer extend size */ public int getOutputBufferExtendSize() { return outputBufferExtendSize; } /** * Sets the output buffer extend size. This is the size on which the output buffer is extended if its size isn't sufficient to send a frame. * Default is 65536 bytes. * * @param outputBufferExtendSize output buffer extend size */ public void setOutputBufferExtendSize(int outputBufferExtendSize) { this.outputBufferExtendSize = outputBufferExtendSize; } /** * Returns the exception listener * * @return exception listener */ public SocketFactory getSocketFactory() { return socketFactory; } /** * Sets the socket factory. Default is com.swiftmq.net.PlainSocketFactory. To use SSL/TLS, specify * com.swiftmq.net.JSSESocketFactory. * * @param socketFactory socket factory */ public void setSocketFactory(SocketFactory socketFactory) { this.socketFactory = socketFactory; } /** * Returns the user name * * @return user name */ public String getUserName() { return userName; } /** * Performs the actual connect to the remote host, negotiates the protocol and authenticates the user * * @throws IOException if an IOExcption occurs * @throws UnsupportedProtocolVersionException if the AMQP/SASL protocol version is not supported by the remote host * @throws AuthenticationException if the user cannot be authenticated * @throws ConnectionClosedException if the connection was closed */ public void connect() throws IOException, UnsupportedProtocolVersionException, AuthenticationException, ConnectionClosedException { verifyState(); connectionDispatcher = new ConnectionDispatcher(ctx, hostname); if (socketFactory instanceof SocketFactory2) ((SocketFactory2) socketFactory).setReceiveBufferSize(inputBufferSize); networkConnection = new BlockingConnection(socketFactory.createSocket(hostname, port), connectionDispatcher, this) { protected ProtocolOutputHandler createOutputHandler(int outputBufferSize, int outputExtendSize) { return new RawOutputHandler(outputBufferSize, outputExtendSize) { public void flush() throws IOException { super.flush(); invokeOutputListener(); } }; } protected ProtocolInputHandler createInputHandler() { return connectionDispatcher.getProtocolHandler(); } }; connectionDispatcher.setMyConnection(this); networkConnection.start(); dos = new DataStreamOutputStream(networkConnection.getOutputStream()); connectionPool = ctx.getConnectionPool(); connectionTask = new ConnectionTask(); connectionQueue = new ConnectionQueue(); connectionDispatcher.setOutboundHandler(connectionQueue); connectionQueue.startQueue(); // SASL if (doAuth) { connectionDispatcher.setSaslActive(true); Semaphore sem = new Semaphore(); POObject po = new POProtocolRequest(sem, Util.SASL_INIT); connectionDispatcher.dispatch(po); sem.waitHere(); sem.reset(); if (!po.isSuccess()) { cancel(); throw new UnsupportedProtocolVersionException(po.getException()); } po = new POAuthenticate(sem, mechanism, userName, password); connectionDispatcher.dispatch(po); sem.waitHere(); sem.reset(); if (!po.isSuccess()) { cancel(); throw new AuthenticationException(po.getException()); } } // AMQP Semaphore sem = new Semaphore(); POObject po = new POProtocolRequest(sem, Util.AMQP_INIT); connectionDispatcher.dispatch(po); sem.waitHere(); sem.reset(); if (!po.isSuccess()) { cancel(); throw new UnsupportedProtocolVersionException(po.getException()); } // Open po = new POOpen(sem, containerId, maxFrameSize, 255, idleTimeout); connectionDispatcher.dispatch(po); sem.waitHere(); sem.reset(); if (!po.isSuccess()) { cancel(); throw new IOException(po.getException()); } connected = true; } /** * Internal use. * * @param e IOException */ public void onException(final IOException e) { new Thread() { public void run() { cancel(); if (exceptionListener != null) exceptionListener.onException(new ConnectionClosedException(e.toString())); } }.start(); } private Session mapSessionToLocalChannel(long incomingWindowSize, long outgoingWindowSize) { try { lock.lock(); Session session = new Session(ctx, this, incomingWindowSize, outgoingWindowSize); session.setChannel(ArrayListTool.setFirstFreeOrExpand(localChannels, session)); return session; } finally { lock.unlock(); } } /** * Creates a new Session. * * @param incomingWindowSize Incoming Window Size (max number of unsettled incoming transfers) * @param outgoingWindowSize Outgoing Window Size (max number of unsettled outgoing transfers) * @return Session * @throws SessionHandshakeException An error occurred during handshake * @throws ConnectionClosedException The connection was closed */ public Session createSession(long incomingWindowSize, long outgoingWindowSize) throws SessionHandshakeException, ConnectionClosedException { verifyState(); if (!connected) throw new SessionHandshakeException("Connection is not connected, call 'connect()'"); Session session = mapSessionToLocalChannel(incomingWindowSize, outgoingWindowSize); session.finishHandshake(); return session; } protected void removeSession(Session session) { try { lock.lock(); localChannels.set(session.getChannel(), null); } finally { lock.unlock(); } } protected Session getSessionForLocalChannel(int localChannel) { try { lock.lock(); if (localChannel >= 0 && localChannel < localChannels.size()) return (Session) localChannels.get(localChannel); return null; } finally { lock.unlock(); } } protected void mapSessionToRemoteChannel(Session session, int remoteChannel) { try { lock.lock(); Util.ensureSize(remoteChannels, remoteChannel + 1); remoteChannels.set(remoteChannel, session); } finally { lock.unlock(); } } protected void unmapSessionFromRemoteChannel(int remoteChannel) { try { lock.lock(); Util.ensureSize(remoteChannels, remoteChannel + 1); remoteChannels.set(remoteChannel, null); } finally { lock.unlock(); } } protected Session getSessionForRemoteChannel(int remoteChannel) { try { lock.lock(); if (remoteChannel >= 0 && remoteChannel < remoteChannels.size()) return (Session) remoteChannels.get(remoteChannel); return null; } finally { lock.unlock(); } } protected OutboundHandler getOutboundHandler() { return connectionQueue; } /** * Internal use only */ public void cancel() { if (closed) return; List cloned = null; try { lock.lock(); if (closed) return; cloned = (List) ((ArrayList) localChannels).clone(); } finally { lock.unlock(); } for (int i = 0; i < cloned.size(); i++) { Session session = (Session) cloned.get(i); if (session != null) session.cancel(); } if (connectionDispatcher != null) connectionDispatcher.close(); if (connectionQueue != null) connectionQueue.stopQueue(); if (networkConnection != null) networkConnection.close(); closed = true; } /** * Close this connection and all sessions created from it. */ public void close() { close(null, null); } protected void close(String condition, String description) { if (closed) return; if (condition == null) { Semaphore sem = new Semaphore(); POSendClose po = new POSendClose(sem, condition == null ? null : new AMQPSymbol(condition), description == null ? null : new AMQPString(description)); connectionDispatcher.dispatch(po); sem.waitHere(); } else { POSendClose po = new POSendClose(null, condition == null ? null : new AMQPSymbol(condition), description == null ? null : new AMQPString(description)); connectionDispatcher.dispatch(po); } cancel(); if (exceptionListener != null && condition != null && description != null) exceptionListener.onException(new ConnectionClosedException(condition + " / " + description)); } private class ConnectionQueue extends SingleProcessorQueue implements OutboundHandler { public ConnectionQueue() { super(100); } protected void startProcessor() { if (!closed) connectionPool.dispatchTask(connectionTask); } public void send(Writable writable) { enqueue(writable); } protected void process(Object[] bulk, int n) { try { for (int i = 0; i < n; i++) { ((Writable) bulk[i]).writeContent(dos); if (fTracer.isEnabled()) { if (bulk[i] instanceof AMQPFrame) fTracer.trace("amqp", "SND[" + ((AMQPFrame) bulk[i]).getChannel() + "] (size=" + ((AMQPFrame) bulk[i]).getPredictedSize() + "): " + bulk[i]); else fTracer.trace("amqp", "SND: " + bulk[i]); } } dos.flush(); } catch (Exception e) { cancel(); } finally { for (int i = 0; i < n; i++) { Writable w = (Writable) bulk[i]; if (w.getSemaphore() != null) w.getSemaphore().notifySingleWaiter(); else if (w.getCallback() != null) w.getCallback().done(true); } } } } private class ConnectionTask implements AsyncTask { public boolean isValid() { return !closed; } public String getDispatchToken() { return "connectiontask"; } public String getDescription() { return "Connection/ConnectionTask"; } public void run() { if (!closed && connectionQueue.dequeue()) connectionPool.dispatchTask(this); } public void stop() { } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy