
org.apache.qpid.jms.provider.amqp.AmqpProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qpid-jms-client Show documentation
Show all versions of qpid-jms-client Show documentation
The core JMS Client implementation
/*
* 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.jms.provider.amqp;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.security.Principal;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jms.JMSException;
import javax.jms.JMSSecurityException;
import org.apache.qpid.jms.JmsTemporaryDestination;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
import org.apache.qpid.jms.message.JmsMessageFactory;
import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
import org.apache.qpid.jms.meta.JmsConnectionInfo;
import org.apache.qpid.jms.meta.JmsConsumerId;
import org.apache.qpid.jms.meta.JmsConsumerInfo;
import org.apache.qpid.jms.meta.JmsDefaultResourceVisitor;
import org.apache.qpid.jms.meta.JmsProducerId;
import org.apache.qpid.jms.meta.JmsProducerInfo;
import org.apache.qpid.jms.meta.JmsResource;
import org.apache.qpid.jms.meta.JmsResourceVistor;
import org.apache.qpid.jms.meta.JmsSessionId;
import org.apache.qpid.jms.meta.JmsSessionInfo;
import org.apache.qpid.jms.meta.JmsTransactionInfo;
import org.apache.qpid.jms.provider.AsyncResult;
import org.apache.qpid.jms.provider.NoOpAsyncResult;
import org.apache.qpid.jms.provider.Provider;
import org.apache.qpid.jms.provider.ProviderClosedException;
import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
import org.apache.qpid.jms.provider.ProviderFuture;
import org.apache.qpid.jms.provider.ProviderListener;
import org.apache.qpid.jms.provider.amqp.builders.AmqpConnectionBuilder;
import org.apache.qpid.jms.transports.SSLTransport;
import org.apache.qpid.jms.transports.TransportFactory;
import org.apache.qpid.jms.transports.TransportListener;
import org.apache.qpid.jms.util.IOExceptionSupport;
import org.apache.qpid.proton.engine.Collector;
import org.apache.qpid.proton.engine.Connection;
import org.apache.qpid.proton.engine.EndpointState;
import org.apache.qpid.proton.engine.Event;
import org.apache.qpid.proton.engine.Event.Type;
import org.apache.qpid.proton.engine.Sasl;
import org.apache.qpid.proton.engine.Transport;
import org.apache.qpid.proton.engine.impl.CollectorImpl;
import org.apache.qpid.proton.engine.impl.ProtocolTracer;
import org.apache.qpid.proton.engine.impl.TransportImpl;
import org.apache.qpid.proton.framing.TransportFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An AMQP v1.0 Provider.
*
* The AMQP Provider is bonded to a single remote broker instance. The provider will attempt
* to connect to only that instance and once failed can not be recovered. For clients that
* wish to implement failover type connections a new AMQP Provider instance must be created
* and state replayed from the JMS layer using the standard recovery process defined in the
* JMS Provider API.
*
* All work within this Provider is serialized to a single Thread. Any asynchronous exceptions
* will be dispatched from that Thread and all in-bound requests are handled there as well.
*/
public class AmqpProvider implements Provider, TransportListener , AmqpResourceParent {
private static final Logger LOG = LoggerFactory.getLogger(AmqpProvider.class);
private static final Logger TRACE_BYTES = LoggerFactory.getLogger(AmqpConnection.class.getPackage().getName() + ".BYTES");
private static final Logger TRACE_FRAMES = LoggerFactory.getLogger(AmqpConnection.class.getPackage().getName() + ".FRAMES");
private static final int DEFAULT_MAX_FRAME_SIZE = 1024 * 1024 * 1;
// NOTE: Limit default channel max to signed short range to deal with
// brokers that don't currently handle the unsigned range well.
private static final int DEFAULT_CHANNEL_MAX = 32767;
private static final AtomicInteger PROVIDER_SEQUENCE = new AtomicInteger();
private static final NoOpAsyncResult NOOP_REQUEST = new NoOpAsyncResult();
private ProviderListener listener;
private AmqpConnection connection;
private AmqpSaslAuthenticator authenticator;
private org.apache.qpid.jms.transports.Transport transport;
private String transportType = AmqpProviderFactory.DEFAULT_TRANSPORT_TYPE;
private String vhost;
private boolean traceFrames;
private boolean traceBytes;
private boolean saslLayer = true;
private String[] saslMechanisms;
private boolean presettleConsumers;
private boolean presettleProducers;
private long connectTimeout = JmsConnectionInfo.DEFAULT_CONNECT_TIMEOUT;
private long closeTimeout = JmsConnectionInfo.DEFAULT_CLOSE_TIMEOUT;
private int channelMax = DEFAULT_CHANNEL_MAX;
private int idleTimeout = 60000;
private long sessionOutoingWindow = -1; //Use proton default
private int maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
private final URI remoteURI;
private final AtomicBoolean closed = new AtomicBoolean();
private final ScheduledExecutorService serializer;
private final Transport protonTransport = Transport.Factory.create();
private final Collector protonCollector = new CollectorImpl();
private final Connection protonConnection = Connection.Factory.create();
private AsyncResult connectionOpenRequest;
private ScheduledFuture> nextIdleTimeoutCheck;
/**
* Create a new instance of an AmqpProvider bonded to the given remote URI.
*
* @param remoteURI
* The URI of the AMQP broker this Provider instance will connect to.
*/
public AmqpProvider(URI remoteURI) {
this.remoteURI = remoteURI;
this.serializer = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable runner) {
Thread serial = new Thread(runner);
serial.setDaemon(true);
serial.setName(AmqpProvider.this.getClass().getSimpleName() + ":(" +
PROVIDER_SEQUENCE.incrementAndGet() + "):[" +
getRemoteURI() + "]");
return serial;
}
});
updateTracer();
}
@Override
public void connect() throws IOException {
checkClosed();
try {
transport = TransportFactory.create(getTransportType(), getRemoteURI());
} catch (Exception e) {
throw IOExceptionSupport.create(e);
}
transport.setTransportListener(this);
transport.connect();
}
@Override
public void start() throws IOException, IllegalStateException {
checkClosed();
if (listener == null) {
throw new IllegalStateException("No ProviderListener registered.");
}
}
@Override
public void close() {
if (closed.compareAndSet(false, true)) {
final ProviderFuture request = new ProviderFuture() {
@Override
public void onFailure(Throwable result) {
// During close it is fine if the close call fails
// this in unrecoverable so we just log the event.
onSuccess();
}
};
serializer.execute(new Runnable() {
@Override
public void run() {
try {
// If we are not connected then there is nothing we can do now
// just signal success.
if (transport == null || !transport.isConnected()) {
request.onSuccess();
}
if (connection != null) {
connection.close(request);
pumpToProtonTransport(request);
} else {
request.onSuccess();
}
} catch (Exception e) {
LOG.debug("Caught exception while closing proton connection: {}", e.getMessage());
} finally {
if (nextIdleTimeoutCheck != null) {
LOG.trace("Cancelling scheduled IdleTimeoutCheck");
nextIdleTimeoutCheck.cancel(false);
nextIdleTimeoutCheck = null;
}
}
}
});
try {
if (closeTimeout < 0) {
request.sync();
} else {
request.sync(closeTimeout, TimeUnit.MILLISECONDS);
}
} catch (IOException e) {
LOG.warn("Error caught while closing Provider: {}", e.getMessage() != null ? e.getMessage() : "");
} finally {
if (transport != null) {
try {
transport.close();
} catch (Exception e) {
LOG.debug("Caught exception while closing down Transport: {}", e.getMessage());
}
}
serializer.shutdown();
}
}
}
@Override
public void create(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
resource.visit(new JmsResourceVistor() {
@Override
public void processSessionInfo(JmsSessionInfo sessionInfo) throws Exception {
connection.createSession(sessionInfo, request);
}
@Override
public void processProducerInfo(JmsProducerInfo producerInfo) throws Exception {
AmqpSession session = connection.getSession(producerInfo.getParentId());
session.createProducer(producerInfo, request);
}
@Override
public void processConsumerInfo(JmsConsumerInfo consumerInfo) throws Exception {
AmqpSession session = connection.getSession(consumerInfo.getParentId());
session.createConsumer(consumerInfo, request);
}
@Override
public void processConnectionInfo(JmsConnectionInfo connectionInfo) throws Exception {
closeTimeout = connectionInfo.getCloseTimeout();
connectTimeout = connectionInfo.getConnectTimeout();
protonTransport.setEmitFlowEventOnSend(false);
if (getMaxFrameSize() > 0) {
protonTransport.setMaxFrameSize(getMaxFrameSize());
}
protonTransport.setChannelMax(getChannelMax());
protonTransport.setIdleTimeout(idleTimeout);
protonTransport.bind(protonConnection);
protonConnection.collect(protonCollector);
if (saslLayer) {
Sasl sasl = protonTransport.sasl();
sasl.client();
String hostname = getVhost();
if (hostname == null) {
hostname = remoteURI.getHost();
} else if (hostname.isEmpty()) {
hostname = null;
}
sasl.setRemoteHostname(hostname);
authenticator = new AmqpSaslAuthenticator(sasl, connectionInfo, getLocalPrincipal(), saslMechanisms);
}
AmqpConnectionBuilder builder = new AmqpConnectionBuilder(AmqpProvider.this, connectionInfo);
AsyncResult wrappedOpenRequest = new AsyncResult() {
@Override
public void onSuccess() {
fireConnectionEstablished();
request.onSuccess();
}
@Override
public void onFailure(Throwable result) {
request.onFailure(result);
}
@Override
public boolean isComplete() {
return request.isComplete();
}
};
connectionOpenRequest = wrappedOpenRequest;
builder.buildResource(wrappedOpenRequest);
}
@Override
public void processDestination(JmsTemporaryDestination destination) throws Exception {
if (destination.isTemporary()) {
connection.createTemporaryDestination(destination, request);
} else {
request.onSuccess();
}
}
@Override
public void processTransactionInfo(JmsTransactionInfo transactionInfo) throws Exception {
AmqpSession session = connection.getSession(transactionInfo.getSessionId());
session.begin(transactionInfo.getId(), request);
}
});
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void start(final JmsResource resource, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
resource.visit(new JmsDefaultResourceVisitor() {
@Override
public void processConsumerInfo(JmsConsumerInfo consumerInfo) throws Exception {
AmqpSession session = connection.getSession(consumerInfo.getParentId());
AmqpConsumer consumer = session.getConsumer(consumerInfo);
consumer.start(request);
}
});
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void stop(final JmsResource resource, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
resource.visit(new JmsDefaultResourceVisitor() {
@Override
public void processConsumerInfo(JmsConsumerInfo consumerInfo) throws Exception {
AmqpSession session = connection.getSession(consumerInfo.getParentId());
AmqpConsumer consumer = session.getConsumer(consumerInfo);
consumer.stop(request);
}
});
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void destroy(final JmsResource resource, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
resource.visit(new JmsDefaultResourceVisitor() {
@Override
public void processSessionInfo(JmsSessionInfo sessionInfo) throws Exception {
AmqpSession session = connection.getSession(sessionInfo.getId());
session.close(request);
}
@Override
public void processProducerInfo(JmsProducerInfo producerInfo) throws Exception {
AmqpSession session = connection.getSession(producerInfo.getParentId());
AmqpProducer producer = session.getProducer(producerInfo);
producer.close(request);
}
@Override
public void processConsumerInfo(JmsConsumerInfo consumerInfo) throws Exception {
AmqpSession session = connection.getSession(consumerInfo.getParentId());
AmqpConsumer consumer = session.getConsumer(consumerInfo);
consumer.close(request);
}
@Override
public void processConnectionInfo(JmsConnectionInfo connectionInfo) throws Exception {
connection.close(request);
}
@Override
public void processDestination(JmsTemporaryDestination destination) throws Exception {
AmqpTemporaryDestination temporary = connection.getTemporaryDestination(destination);
if (temporary != null) {
temporary.close(request);
} else {
LOG.debug("Could not find temporary destination {} to delete.", destination);
request.onSuccess();
}
}
});
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void send(final JmsOutboundMessageDispatch envelope, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
JmsProducerId producerId = envelope.getProducerId();
AmqpProducer producer = null;
if (producerId.getProviderHint() instanceof AmqpFixedProducer) {
producer = (AmqpFixedProducer) producerId.getProviderHint();
} else {
AmqpSession session = connection.getSession(producerId.getParentId());
producer = session.getProducer(producerId);
}
boolean couldSend = producer.send(envelope, request);
pumpToProtonTransport(request);
if (couldSend && envelope.isSendAsync()) {
request.onSuccess();
}
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
AmqpSession amqpSession = connection.getSession(sessionId);
amqpSession.acknowledge(ackType);
pumpToProtonTransport(request);
request.onSuccess();
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
JmsConsumerId consumerId = envelope.getConsumerId();
AmqpConsumer consumer = null;
if (consumerId.getProviderHint() instanceof AmqpConsumer) {
consumer = (AmqpConsumer) consumerId.getProviderHint();
} else {
AmqpSession session = connection.getSession(consumerId.getParentId());
consumer = session.getConsumer(consumerId);
}
consumer.acknowledge(envelope, ackType);
if (consumer.getSession().isAsyncAck()) {
request.onSuccess();
pumpToProtonTransport(request);
} else {
pumpToProtonTransport(request);
request.onSuccess();
}
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void commit(final JmsTransactionInfo transactionInfo, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
AmqpSession session = connection.getSession(transactionInfo.getSessionId());
session.commit(transactionInfo, request);
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void rollback(final JmsTransactionInfo transactionInfo, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
AmqpSession session = connection.getSession(transactionInfo.getSessionId());
session.rollback(transactionInfo, request);
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void recover(final JmsSessionId sessionId, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
AmqpSession session = connection.getSession(sessionId);
session.recover();
pumpToProtonTransport(request);
request.onSuccess();
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void unsubscribe(final String subscription, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
connection.unsubscribe(subscription, request);
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
@Override
public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws IOException {
checkClosed();
serializer.execute(new Runnable() {
@Override
public void run() {
try {
checkClosed();
AmqpConsumer consumer = null;
if (consumerId.getProviderHint() instanceof AmqpConsumer) {
consumer = (AmqpConsumer) consumerId.getProviderHint();
} else {
AmqpSession session = connection.getSession(consumerId.getParentId());
consumer = session.getConsumer(consumerId);
}
consumer.pull(timeout, request);
pumpToProtonTransport(request);
} catch (Exception error) {
request.onFailure(error);
}
}
});
}
//---------- Event handlers and Utility methods -------------------------//
private void updateTracer() {
if (isTraceFrames()) {
((TransportImpl) protonTransport).setProtocolTracer(new ProtocolTracer() {
@Override
public void receivedFrame(TransportFrame transportFrame) {
TRACE_FRAMES.trace("RECV: {}", transportFrame.getBody());
}
@Override
public void sentFrame(TransportFrame transportFrame) {
TRACE_FRAMES.trace("SENT: {}", transportFrame.getBody());
}
});
}
}
@Override
public void onData(final ByteBuf input) {
// We need to retain until the serializer gets around to processing it.
ReferenceCountUtil.retain(input);
serializer.execute(new Runnable() {
@Override
public void run() {
if (isTraceBytes()) {
TRACE_BYTES.info("Received: {}", ByteBufUtil.hexDump(input));
}
ByteBuffer source = input.nioBuffer();
do {
ByteBuffer buffer = protonTransport.getInputBuffer();
int limit = Math.min(buffer.remaining(), source.remaining());
ByteBuffer duplicate = source.duplicate();
duplicate.limit(source.position() + limit);
buffer.put(duplicate);
protonTransport.processInput();
source.position(source.position() + limit);
} while (source.hasRemaining());
ReferenceCountUtil.release(input);
// Process the state changes from the latest data and then answer back
// any pending updates to the Broker.
processUpdates();
pumpToProtonTransport();
}
});
}
/**
* Callback method for the Transport to report connection errors. When called
* the method will queue a new task to fire the failure error back to the listener.
*
* @param error
* the error that causes the transport to fail.
*/
@Override
public void onTransportError(final Throwable error) {
if (!serializer.isShutdown()) {
serializer.execute(new Runnable() {
@Override
public void run() {
LOG.info("Transport failed: {}", error.getMessage());
if (!closed.get()) {
// We can't send any more output, so close the transport
protonTransport.close_head();
fireProviderException(error);
if (connection != null) {
connection.resourceClosed();
}
}
}
});
}
}
/**
* Callback method for the Transport to report that the underlying connection
* has closed. When called this method will queue a new task that will check for
* the closed state on this transport and if not closed then an exception is raised
* to the registered ProviderListener to indicate connection loss.
*/
@Override
public void onTransportClosed() {
if (!serializer.isShutdown()) {
serializer.execute(new Runnable() {
@Override
public void run() {
LOG.debug("Transport connection remotely closed");
if (!closed.get()) {
// We can't send any more output, so close the transport
protonTransport.close_head();
fireProviderException(new IOException("Transport connection remotely closed."));
if (connection != null) {
connection.resourceClosed();
}
}
}
});
}
}
private void processUpdates() {
try {
Event protonEvent = null;
while ((protonEvent = protonCollector.peek()) != null) {
if (!protonEvent.getType().equals(Type.TRANSPORT)) {
LOG.trace("New Proton Event: {}", protonEvent.getType());
}
AmqpEventSink amqpEventSink = null;
switch (protonEvent.getType()) {
case CONNECTION_REMOTE_CLOSE:
amqpEventSink = (AmqpEventSink) protonEvent.getConnection().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteClose(this);
}
break;
case CONNECTION_REMOTE_OPEN:
amqpEventSink = (AmqpEventSink) protonEvent.getConnection().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteOpen(this);
}
break;
case SESSION_REMOTE_CLOSE:
amqpEventSink = (AmqpEventSink) protonEvent.getSession().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteClose(this);
}
break;
case SESSION_REMOTE_OPEN:
amqpEventSink = (AmqpEventSink) protonEvent.getSession().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteOpen(this);
}
break;
case LINK_REMOTE_CLOSE:
amqpEventSink = (AmqpEventSink) protonEvent.getLink().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteClose(this);
}
break;
case LINK_REMOTE_DETACH:
amqpEventSink = (AmqpEventSink) protonEvent.getLink().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteDetach(this);
}
break;
case LINK_REMOTE_OPEN:
amqpEventSink = (AmqpEventSink) protonEvent.getLink().getContext();
if (amqpEventSink != null) {
amqpEventSink.processRemoteOpen(this);
}
break;
case LINK_FLOW:
amqpEventSink = (AmqpEventSink) protonEvent.getLink().getContext();
if (amqpEventSink != null) {
amqpEventSink.processFlowUpdates(this);
}
break;
case DELIVERY:
amqpEventSink = (AmqpEventSink) protonEvent.getLink().getContext();
if (amqpEventSink != null) {
amqpEventSink.processDeliveryUpdates(this);
}
break;
default:
break;
}
protonCollector.pop();
}
// We have to do this to pump SASL bytes in as SASL is not event driven yet.
processSaslAuthentication();
} catch (Exception ex) {
LOG.warn("Caught Exception during update processing: {}", ex.getMessage(), ex);
fireProviderException(ex);
}
}
private void processSaslAuthentication() {
if (authenticator == null) {
return;
}
try {
if (authenticator.authenticate()) {
authenticator = null;
}
} catch (JMSSecurityException ex) {
try {
org.apache.qpid.proton.engine.Transport t = protonConnection.getTransport();
t.close_head();
} finally {
fireProviderException(ex);
}
}
}
protected boolean pumpToProtonTransport() {
return pumpToProtonTransport(NOOP_REQUEST);
}
protected boolean pumpToProtonTransport(AsyncResult request) {
try {
boolean done = false;
while (!done) {
ByteBuffer toWrite = protonTransport.getOutputBuffer();
if (toWrite != null && toWrite.hasRemaining()) {
ByteBuf outbound = transport.allocateSendBuffer(toWrite.remaining());
outbound.writeBytes(toWrite);
if (isTraceBytes()) {
TRACE_BYTES.info("Sending: {}", ByteBufUtil.hexDump(outbound));
}
transport.send(outbound);
protonTransport.outputConsumed();
} else {
done = true;
}
}
} catch (IOException e) {
fireProviderException(e);
request.onFailure(e);
return false;
}
return true;
}
void fireConnectionEstablished() {
// The request onSuccess calls this method
connectionOpenRequest = null;
// Using nano time since it is not related to the wall clock, which may change
long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
long deadline = protonTransport.tick(now);
if (deadline > 0) {
long delay = deadline - now;
LOG.trace("IdleTimeoutCheck being initiated, initial delay: {}", delay);
nextIdleTimeoutCheck = serializer.schedule(new IdleTimeoutCheck(), delay, TimeUnit.MILLISECONDS);
}
ProviderListener listener = this.listener;
if (listener != null) {
listener.onConnectionEstablished(remoteURI);
}
}
void fireNonFatalProviderException(Exception ex) {
ProviderListener listener = this.listener;
if (listener != null) {
listener.onProviderException(ex);
}
}
void fireProviderException(Throwable ex) {
if (connectionOpenRequest != null) {
connectionOpenRequest.onFailure(ex);
connectionOpenRequest = null;
}
ProviderListener listener = this.listener;
if (listener != null) {
listener.onConnectionFailure(IOExceptionSupport.create(ex));
}
}
void fireResourceRemotelyClosed(JmsResource resource, Exception ex) {
ProviderListener listener = this.listener;
if (listener != null) {
listener.onResourceRemotelyClosed(resource, ex);
}
}
@Override
public void addChildResource(AmqpResource resource) {
if (resource instanceof AmqpConnection) {
this.connection = (AmqpConnection) resource;
}
}
@Override
public void removeChildResource(AmqpResource resource) {
// No need to remove resources
}
//---------- Property Setters and Getters --------------------------------//
@Override
public JmsMessageFactory getMessageFactory() {
if (connection == null) {
throw new RuntimeException("Message Factory is not accessible when not connected.");
}
return connection.getAmqpMessageFactory();
}
public void setTraceFrames(boolean trace) {
this.traceFrames = trace;
updateTracer();
}
public boolean isTraceFrames() {
return this.traceFrames;
}
public void setTraceBytes(boolean trace) {
this.traceBytes = trace;
}
public boolean isTraceBytes() {
return this.traceBytes;
}
public boolean isSaslLayer() {
return saslLayer;
}
/**
* Sets whether a sasl layer is used for the connection or not.
*
* @param saslLayer true to enable the sasl layer, false to disable it.
*/
public void setSaslLayer(boolean saslLayer) {
this.saslLayer = saslLayer;
}
public String[] getSaslMechanisms() {
return saslMechanisms;
}
/**
* Sets a selection of mechanisms to restrict the choice to, enabling only
* a subset of the servers offered mechanisms to be selectable.
*
* @param saslMechanisms the mechanisms to restrict choice to, or null not to restrict.
*/
public void setSaslMechanisms(String[] saslMechanisms) {
this.saslMechanisms = saslMechanisms;
}
public String getVhost() {
return vhost;
}
/**
* Sets the hostname to be used in the AMQP SASL Init and Open frames.
*
* If set null, the host provided in the remoteURI will be used. If set to
* the empty string, the hostname field of the frames will be cleared.
*
* @param vhost the hostname to include in SASL Init and Open frames.
*/
public void setVhost(String vhost) {
this.vhost = vhost;
}
public int getIdleTimeout() {
return idleTimeout;
}
/**
* Sets the idle timeout (in milliseconds) after which the connection will
* be closed if the peer has not send any data. The provided value will be
* halved before being transmitted as our advertised idle-timeout in the
* AMQP Open frame.
*
* @param idleTimeout the timeout in milliseconds.
*/
public void setIdleTimeout(int idleTimeout) {
this.idleTimeout = idleTimeout;
}
public int getMaxFrameSize() {
return maxFrameSize;
}
/**
* Sets the max frame size (in bytes).
*
* Values of -1 indicates to use the proton default.
*
* @param maxFrameSize the frame size in bytes.
*/
public void setMaxFrameSize(int maxFrameSize) {
this.maxFrameSize = maxFrameSize;
}
public long getSessionOutgoingWindow() {
return sessionOutoingWindow;
}
/**
* Sets the outgoing window size for the AMQP session. Values may
* be between -1 and 2^32-1, where -1 indicates to use the default.
*
* @param sessionOutoingWindow the outgoing window size
*/
public void setSessionOutgoingWindow(long sessionOutoingWindow) {
this.sessionOutoingWindow = sessionOutoingWindow;
}
public long getCloseTimeout() {
return this.closeTimeout;
}
public long getConnectTimeout() {
return connectTimeout;
}
public long getRequestTimeout() {
return connection != null ? connection.getResourceInfo().getRequestTimeout() : JmsConnectionInfo.DEFAULT_REQUEST_TIMEOUT;
}
public long getSendTimeout() {
return connection != null ? connection.getResourceInfo().getSendTimeout() : JmsConnectionInfo.DEFAULT_SEND_TIMEOUT;
}
public void setPresettle(boolean presettle) {
setPresettleConsumers(presettle);
setPresettleProducers(presettle);
}
public boolean isPresettleConsumers() {
return this.presettleConsumers;
}
public void setPresettleConsumers(boolean presettle) {
this.presettleConsumers = presettle;
}
public boolean isPresettleProducers() {
return this.presettleProducers;
}
public void setPresettleProducers(boolean presettle) {
this.presettleProducers = presettle;
}
@Override
public String toString() {
return "AmqpProvider: " + getRemoteURI().getHost() + ":" + getRemoteURI().getPort();
}
public int getChannelMax() {
return channelMax;
}
public void setChannelMax(int channelMax) {
this.channelMax = channelMax;
}
String getTransportType() {
return transportType;
}
void setTransportType(String transportType) {
this.transportType = transportType;
}
@Override
public void setProviderListener(ProviderListener listener) {
this.listener = listener;
}
@Override
public ProviderListener getProviderListener() {
return listener;
}
@Override
public URI getRemoteURI() {
return remoteURI;
}
public Transport getProtonTransport() {
return protonTransport;
}
public Connection getProtonConnection() {
return protonConnection;
}
ScheduledExecutorService getScheduler() {
return this.serializer;
}
@Override
public AmqpProvider getProvider() {
return this;
}
/**
* Allows a resource to request that its parent resource schedule a future
* cancellation of a request and return it a {@link Future} instance that
* can be used to cancel the scheduled automatic failure of the request.
*
* @param request
* The request that should be marked as failed based on configuration.
* @param timeout
* The time to wait before marking the request as failed.
* @param error
* The error to use when failing the pending request.
*
* @return a {@link ScheduledFuture} that can be stored by the caller.
*/
public ScheduledFuture> scheduleRequestTimeout(final AsyncResult request, long timeout, final Exception error) {
if (timeout != JmsConnectionInfo.INFINITE) {
return serializer.schedule(new Runnable() {
@Override
public void run() {
request.onFailure(error);
pumpToProtonTransport();
}
}, timeout, TimeUnit.MILLISECONDS);
}
return null;
}
Principal getLocalPrincipal() {
if (transport instanceof SSLTransport) {
return ((SSLTransport) transport).getLocalPrincipal();
}
return null;
}
//----- Internal implementation ------------------------------------------//
private void checkClosed() throws ProviderClosedException {
if (closed.get()) {
throw new ProviderClosedException("This Provider is already closed");
}
}
private final class IdleTimeoutCheck implements Runnable {
@Override
public void run() {
boolean checkScheduled = false;
if (connection.getLocalState() == EndpointState.ACTIVE) {
// Using nano time since it is not related to the wall clock, which may change
long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
long deadline = protonTransport.tick(now);
boolean pumpSucceeded = pumpToProtonTransport();
if (protonTransport.isClosed()) {
LOG.info("IdleTimeoutCheck closed the transport due to the peer exceeding our requested idle-timeout.");
if (pumpSucceeded) {
fireProviderException(new IOException("Transport closed due to the peer exceeding our requested idle-timeout"));
}
} else {
if (deadline > 0) {
long delay = deadline - now;
checkScheduled = true;
LOG.trace("IdleTimeoutCheck rescheduling with delay: {}", delay);
nextIdleTimeoutCheck = serializer.schedule(this, delay, TimeUnit.MILLISECONDS);
}
}
} else {
LOG.trace("IdleTimeoutCheck skipping check, connection is not active.");
}
if (!checkScheduled) {
nextIdleTimeoutCheck = null;
LOG.trace("IdleTimeoutCheck exiting");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy