private void setupCredit() { synchronized (creditsLock) { this.credits = sender.getCredit() - pending.get(); if (credits < 0) { credits = 0; } } }
@Override public void processFlowUpdates(AmqpConnection connection) throws IOException { LOG.trace("Sender {} flow update, credit = {}", getEndpoint().getCredit()); doCreditInspection(); }
@Override public boolean hasCredits(ServerConsumer consumer) { ProtonServerSenderContext plugSender = (ProtonServerSenderContext) consumer.getProtocolContext(); if (plugSender != null && plugSender.getSender().getCredit() > 0) { return true; } else { return false; } }
@Override public ErrorContext getContext() { final boolean isLinkOpened = this.linkFirstOpen != null && this.linkFirstOpen.isDone(); final String referenceId = this.sendLink != null && this.sendLink.getRemoteProperties() != null && this.sendLink.getRemoteProperties().containsKey(ClientConstants.TRACKING_ID_PROPERTY) ? this.sendLink.getRemoteProperties().get(ClientConstants.TRACKING_ID_PROPERTY).toString() : ((this.sendLink != null) ? this.sendLink.getName() : null); SenderErrorContext errorContext = new SenderErrorContext( this.underlyingFactory!=null ? this.underlyingFactory.getHostName() : null, this.sendPath, referenceId, isLinkOpened && this.sendLink != null ? this.sendLink.getCredit() : null); return errorContext; }
@Override public ErrorContext getContext() { final Sender link; synchronized (this.errorConditionLock) { link = this.sendLink; } final boolean isLinkOpened = this.linkFirstOpen != null && this.linkFirstOpen.isDone(); final String referenceId = link != null && link.getRemoteProperties() != null && link.getRemoteProperties().containsKey(ClientConstants.TRACKING_ID_PROPERTY) ? link.getRemoteProperties().get(ClientConstants.TRACKING_ID_PROPERTY).toString() : ((link != null) ? link.getName() : null); final SenderContext errorContext = new SenderContext( this.underlyingFactory != null ? this.underlyingFactory.getHostName() : null, this.sendPath, referenceId, isLinkOpened && link != null ? link.getCredit() : null); return errorContext; }
@Override public void inspectCredit(Sender sender) { int count = counter.incrementAndGet(); switch (count) { case 1: assertEquals("Unexpected initial credit", AmqpSupport.AMQP_CREDITS_DEFAULT, sender.getCredit()); initialCredit.countDown(); break; case 2: assertEquals("Unexpected replenished credit", AmqpSupport.AMQP_CREDITS_DEFAULT, sender.getCredit()); refreshedCredit.countDown(); break; default: throw new IllegalStateException("Unexpected additional flow: " + count); } } });
if (snd.getCredit() > 0)
@Override public void onLinkFlow(Event event) { if (this.isFirstFlow) { synchronized (this.firstFlow) { if (this.isFirstFlow) { this.msgSender.onOpenComplete(null); this.isFirstFlow = false; } } } Sender sender = event.getSender(); this.msgSender.onFlow(sender.getRemoteCredit()); if (TRACE_LOGGER.isDebugEnabled()) { TRACE_LOGGER.debug("onLinkFlow linkName[" + sender.getName() + "], unsettled[" + sender.getUnsettled() + "], credit[" + sender.getCredit() + "]"); } } }
@Override public void processFlowUpdates(AmqpProvider provider) throws IOException { if (!blocked.isEmpty() && getEndpoint().getCredit() > 0) { Iterator<InFlightSend> blockedSends = blocked.values().iterator(); while (getEndpoint().getCredit() > 0 && blockedSends.hasNext()) { LOG.trace("Dispatching previously held send"); InFlightSend held = blockedSends.next(); try { // If the transaction has failed due to remote termination etc then we just indicate // the send has succeeded until the a new transaction is started. if (session.isTransacted() && session.isTransactionFailed()) { held.onSuccess(); return; } doSend(held.getEnvelope(), held); } catch (JMSException e) { throw IOExceptionSupport.create(e); } finally { blockedSends.remove(); } } } // If a drain was requested, we just sent what we had so respond with drained if (getEndpoint().getDrain()) { getEndpoint().drained(); } super.processFlowUpdates(provider); }
@Override public void processFlowUpdates(AmqpProvider provider) throws IOException { if (!blocked.isEmpty() && getEndpoint().getCredit() > 0) { Iterator<InFlightSend> blockedSends = blocked.values().iterator(); while (getEndpoint().getCredit() > 0 && blockedSends.hasNext()) { LOG.trace("Dispatching previously held send"); InFlightSend held = blockedSends.next(); try { // If the transaction has failed due to remote termination etc then we just indicate // the send has succeeded until the a new transaction is started. if (session.isTransacted() && session.isTransactionFailed()) { held.onSuccess(); return; } doSend(held.getEnvelope(), held); } catch (JMSException e) { throw IOExceptionSupport.create(e); } finally { blockedSends.remove(); } } } // If a drain was requested, we just sent what we had so respond with drained if (getEndpoint().getDrain()) { getEndpoint().drained(); } super.processFlowUpdates(provider); }
@Override public void onLinkFlow(Event event) { if (this.isFirstFlow) { synchronized (this.firstFlow) { if (this.isFirstFlow) { this.msgSender.onOpenComplete(null); this.isFirstFlow = false; } } } Sender sender = event.getSender(); this.msgSender.onFlow(sender.getRemoteCredit()); TRACE_LOGGER.debug("onLinkFlow: linkName:{}, unsettled:{}, credit:{}", sender.getName(), sender.getUnsettled(), sender.getCredit()); } }
while(true) { while( currentBuffer !=null ) { if( sender.getCredit() > 0 ) { int sent = sender.send(currentBuffer.data, currentBuffer.offset, currentBuffer.length); currentBuffer.moveHead(sent);
@Test(timeout = 60000) public void testCreditsAreAllocatedOnceOnLinkCreated() throws Exception { AmqpClient client = createAmqpClient(new URI(singleCreditAcceptorURI)); AmqpConnection connection = addConnection(client.connect()); try { AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender(getQueueName()); assertEquals("Should only be issued one credit", 1, sender.getSender().getCredit()); } finally { connection.close(); } }
LOG.trace("Sender:[{}] msgId={} draining={}, drain={}, credit={}, remoteCredit={}, queued={}", getEndpoint().getName(), jms.getJMSMessageID(), draining, getEndpoint().getDrain(), getEndpoint().getCredit(), getEndpoint().getRemoteCredit(), getEndpoint().getQueued()); if (draining && getEndpoint().getCredit() == 0) { LOG.trace("Sender:[{}] browse complete.", getEndpoint().getName()); getEndpoint().drained();
@Override public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException { if (isClosed()) { request.onFailure(new IllegalStateException("The MessageProducer is closed")); } if (!delayedDeliverySupported && envelope.getMessage().getFacade().isDeliveryTimeTransmitted()) { // Don't allow sends with delay if the remote has not said it can handle them request.onFailure(new JMSException("Remote does not support delayed message delivery")); } else if (getEndpoint().getCredit() <= 0) { LOG.trace("Holding Message send until credit is available."); InFlightSend send = new InFlightSend(envelope, request); if (getSendTimeout() > JmsConnectionInfo.INFINITE) { send.requestTimeout = getParent().getProvider().scheduleRequestTimeout(send, getSendTimeout(), send); } blocked.put(envelope.getMessageId(), send); getParent().getProvider().pumpToProtonTransport(request); } else { // If the transaction has failed due to remote termination etc then we just indicate // the send has succeeded until the a new transaction is started. if (session.isTransacted() && session.isTransactionFailed()) { request.onSuccess(); return; } doSend(envelope, new InFlightSend(envelope, request)); } }
@Override public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException { if (isClosed()) { request.onFailure(new IllegalStateException("The MessageProducer is closed")); } if (!delayedDeliverySupported && envelope.getMessage().getFacade().isDeliveryTimeTransmitted()) { // Don't allow sends with delay if the remote has not said it can handle them request.onFailure(new JMSException("Remote does not support delayed message delivery")); } else if (getEndpoint().getCredit() <= 0) { LOG.trace("Holding Message send until credit is available."); InFlightSend send = new InFlightSend(envelope, request); if (getSendTimeout() > JmsConnectionInfo.INFINITE) { send.requestTimeout = getParent().getProvider().scheduleRequestTimeout(send, getSendTimeout(), send); } blocked.put(envelope.getMessageId(), send); getParent().getProvider().pumpToProtonTransport(request); } else { // If the transaction has failed due to remote termination etc then we just indicate // the send has succeeded until the a new transaction is started. if (session.isTransacted() && session.isTransactionFailed()) { request.onSuccess(); return; } doSend(envelope, new InFlightSend(envelope, request)); } }
@Test(timeout = 60000) public void testCreditsAreRefreshedWhenAddressIsUnblocked() throws Exception { fillAddress(getQueueName()); AmqpClient client = createAmqpClient(); AmqpConnection connection = addConnection(client.connect()); try { AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender(getQueueName()); // Wait for a potential flow frame. Thread.sleep(500); assertEquals(0, sender.getSender().getCredit()); // Empty Address except for 1 message used later. AmqpReceiver receiver = session.createReceiver(getQueueName()); receiver.flow(100); AmqpMessage m; for (int i = 0; i < messagesSent - 1; i++) { m = receiver.receive(5000, TimeUnit.MILLISECONDS); m.accept(); } // Wait for address to unblock and flow frame to arrive Thread.sleep(500); assertTrue(sender.getSender().getCredit() >= 0); } finally { connection.close(); } }
@Test(timeout = 60000) public void testNewLinkAttachAreNotAllocatedCreditsWhenAddressIsBlocked() throws Exception { fillAddress(getQueueName()); AmqpClient client = createAmqpClient(); AmqpConnection connection = addConnection(client.connect()); try { AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender(getQueueName()); // Wait for a potential flow frame. Thread.sleep(1000); assertEquals(0, sender.getSender().getCredit()); } finally { connection.close(); } }
@Test(timeout = 60000) public void testCreditsAreNotAllocatedWhenAddressIsFull() throws Exception { AmqpClient client = createAmqpClient(new URI(singleCreditAcceptorURI)); AmqpConnection connection = addConnection(client.connect()); try { AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender(getQueueName()); // Use blocking send to ensure buffered messages do not interfere with credit. sender.setSendTimeout(-1); sendUntilFull(sender); // This should be -1. A single message is buffered in the client, and 0 credit has been allocated. assertTrue(sender.getSender().getCredit() == -1); long addressSize = server.getPagingManager().getPageStore(new SimpleString(getQueueName())).getAddressSize(); assertTrue(addressSize >= MAX_SIZE_BYTES && addressSize <= MAX_SIZE_BYTES_REJECT_THRESHOLD); } finally { connection.close(); } }