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

com.microsoft.azure.servicebus.primitives.RequestResponseLinkCache Maven / Gradle / Ivy

Go to download

Java library for Azure Service Bus. Please note, a newer package com.azure:azure-messaging-servicebus for Azure Service Bus is available as of December 2020. While this package will continue to receive critical bug fixes, we strongly encourage you to upgrade. Read the migration guide at https://aka.ms/azsdk/java/migrate/sb for more details.

There is a newer version: 3.6.7
Show newest version
package com.microsoft.azure.servicebus.primitives;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import org.apache.qpid.proton.amqp.Symbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class RequestResponseLinkCache
{
    private static final Logger TRACE_LOGGER = LoggerFactory.getLogger(RequestResponseLinkCache.class);

    private Object lock = new Object();
    private final MessagingFactory underlyingFactory;
    private HashMap pathToRRLinkMap;

    public RequestResponseLinkCache(MessagingFactory underlyingFactory)
    {
        this.underlyingFactory = underlyingFactory;
        this.pathToRRLinkMap = new HashMap<>();
    }

    public CompletableFuture obtainRequestResponseLinkAsync(String entityPath, String transferEntityPath, MessagingEntityType entityType)
    {
        RequestResponseLinkWrapper wrapper;
        String mapKey;
        if (transferEntityPath != null)
        {
            mapKey = entityPath + ":" + transferEntityPath;
        }
        else
        {
            mapKey = entityPath;
        }

        synchronized (lock)
        {
            wrapper = this.pathToRRLinkMap.get(mapKey);
            if(wrapper == null)
            {
                wrapper = new RequestResponseLinkWrapper(this.underlyingFactory, entityPath, transferEntityPath, entityType);
                this.pathToRRLinkMap.put(mapKey, wrapper);
            }
        }
        return wrapper.acquireReferenceAsync();
    }

    public void releaseRequestResponseLink(String entityPath, String transferEntityPath)
    {
        String mapKey;
        if (transferEntityPath != null)
        {
            mapKey = entityPath + ":" + transferEntityPath;
        }
        else
        {
            mapKey = entityPath;
        }

        RequestResponseLinkWrapper wrapper;
        synchronized (lock)
        {
            wrapper = this.pathToRRLinkMap.get(mapKey);
        }
        if(wrapper != null)
        {
            wrapper.releaseReference();
        }
    }

    public CompletableFuture freeAsync()
    {
        TRACE_LOGGER.info("Closing all cached request-response links");
        ArrayList> closeFutures = new ArrayList<>();
        for(RequestResponseLinkWrapper wrapper : this.pathToRRLinkMap.values())
        {
            closeFutures.add(wrapper.forceCloseAsync());
        }

        this.pathToRRLinkMap.clear();
        return CompletableFuture.allOf(closeFutures.toArray(new CompletableFuture[0]));
    }

    private void removeWrapperFromCache(String entityPath)
    {
        synchronized (lock)
        {
            this.pathToRRLinkMap.remove(entityPath);
        }
    }

    private class RequestResponseLinkWrapper
    {
        private Object lock = new Object();
        private final MessagingFactory underlyingFactory;
        private final String entityPath;
        private final String transferEntityPath;
        private final MessagingEntityType entityType;
        private RequestResponseLink requestResponseLink;
        private int referenceCount;
        private ArrayList> waiters;
        private boolean isClosed;

        public RequestResponseLinkWrapper(MessagingFactory underlyingFactory, String entityPath, String transferEntityPath, MessagingEntityType entityType)
        {
            this.underlyingFactory = underlyingFactory;
            this.entityPath = entityPath;
            this.transferEntityPath = transferEntityPath;
            this.entityType = entityType;
            this.requestResponseLink = null;
            this.referenceCount = 0;
            this.waiters = new ArrayList<>();
            this.isClosed = false;
            this.createRequestResponseLinkAsync();
        }

        private void createRequestResponseLinkAsync()
        {
            String requestResponseLinkPath = RequestResponseLink.getManagementNodeLinkPath(this.entityPath);
            String sasTokenAudienceURI = String.format(ClientConstants.SAS_TOKEN_AUDIENCE_FORMAT, this.underlyingFactory.getHostName(), this.entityPath);

            String transferDestinationSasTokenAudienceURI = null;
            Map additionalProperties = null;
            if (this.transferEntityPath != null) {
                transferDestinationSasTokenAudienceURI = String.format(ClientConstants.SAS_TOKEN_AUDIENCE_FORMAT, this.underlyingFactory.getHostName(), this.transferEntityPath);
                additionalProperties = new HashMap<>();
                additionalProperties.put(ClientConstants.LINK_TRANSFER_DESTINATION_PROPERTY, this.transferEntityPath);
            }

            TRACE_LOGGER.debug("Creating requestresponselink to '{}'", requestResponseLinkPath);
            RequestResponseLink.createAsync(
                    this.underlyingFactory,
                    StringUtil.getShortRandomString() + "-RequestResponse",
                    requestResponseLinkPath,
                    sasTokenAudienceURI,
                    transferDestinationSasTokenAudienceURI,
                    additionalProperties,
                    this.entityType).handleAsync((rrlink, ex) ->
            {
                synchronized (this.lock)
                {
                    if(ex == null)
                    {
                        TRACE_LOGGER.info("Created requestresponselink to '{}'", requestResponseLinkPath);
                        if(this.isClosed)
                        {
                        	// Factory is likely closed. Close the link too
                        	rrlink.closeAsync();
                        }
                        else
                        {
                        	this.requestResponseLink = rrlink;
                            this.completeWaiters(null);
                        }
                    }
                    else
                    {
                        Throwable cause = ExceptionUtil.extractAsyncCompletionCause(ex);
                        TRACE_LOGGER.error("Creating requestresponselink to '{}' failed.", requestResponseLinkPath, cause);
                        RequestResponseLinkCache.this.removeWrapperFromCache(this.entityPath);
                        this.completeWaiters(cause);
                    }
                }
                
                return null;
            }, MessagingFactory.INTERNAL_THREAD_POOL);
        }
        
        private void completeWaiters(Throwable exception)
        {
        	for(CompletableFuture waiter : this.waiters)
            {
        		if(exception == null)
        		{
        			this.referenceCount++;
        			AsyncUtil.completeFuture(waiter, this.requestResponseLink);
        		}
        		else
        		{
        			AsyncUtil.completeFutureExceptionally(waiter, exception);
        		}
            }
        	
        	this.waiters.clear();
        }

        public CompletableFuture acquireReferenceAsync()
        {
            synchronized (this.lock)
            {
                if(this.requestResponseLink == null)
                {
                    CompletableFuture waiter = new CompletableFuture<>();
                    this.waiters.add(waiter);
                    return waiter;
                }
                else
                {
                    this.referenceCount++;
                    return CompletableFuture.completedFuture(this.requestResponseLink);
                }
            }
        }

        public void releaseReference()
        {
            synchronized (this.lock)
            {
                if(--this.referenceCount == 0)
                {
                    RequestResponseLinkCache.this.removeWrapperFromCache(this.entityPath);
                    TRACE_LOGGER.info("Closing requestresponselink to '{}'", this.requestResponseLink.getLinkPath());
                    this.requestResponseLink.closeAsync();
                }
            }
        }

        public CompletableFuture forceCloseAsync()
        {
            TRACE_LOGGER.info("Force closing requestresponselink to '{}'", this.requestResponseLink.getLinkPath());
            this.isClosed = true;
            if(this.waiters.size() > 0)
            {
            	this.completeWaiters(new ServiceBusException(false, "MessagingFactory closed."));
            }
            
            if(this.requestResponseLink != null)
            {
            	return this.requestResponseLink.closeAsync();
            }
            else
            {
            	return CompletableFuture.completedFuture(null);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy