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

edu.utexas.tacc.tapis.sharedq.QueueManager Maven / Gradle / Ivy

package edu.utexas.tacc.tapis.sharedq;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Envelope;

import edu.utexas.tacc.tapis.shared.exceptions.TapisException;
import edu.utexas.tacc.tapis.shared.exceptions.runtime.TapisRuntimeException;
import edu.utexas.tacc.tapis.shared.i18n.MsgUtils;
import edu.utexas.tacc.tapis.sharedq.exceptions.TapisQueueException;

public final class QueueManager 
  extends QueueManagerNames
{
  /* ********************************************************************** */
  /*                               Constants                                */
  /* ********************************************************************** */
  // Tracing.
  private static final Logger _log = LoggerFactory.getLogger(QueueManager.class);
    
  // Binding key to use with fanout exchanges.
  public static final String DEFAULT_BINDING_KEY = "";
  
  // Default timeout in milliseconds to close a connection.
  public static final int DEFAULT_CONN_CLOSE_TIMEOUT_MS = 1000;
  
  /* ********************************************************************** */
  /*                                 Fields                                 */
  /* ********************************************************************** */
  // Singleton instance of this class.
  private static QueueManager     _instance;
  
  // Configuration parameters.
  private final QueueManagerParms _parms;
  
  // Fields that get initialized once and tend not to change.
  private ConnectionFactory       _factory;
  private Connection              _outConnection;
  private Connection              _inConnection;

  /* ********************************************************************** */
  /*                             Constructors                               */
  /* ********************************************************************** */
  /* ---------------------------------------------------------------------- */
  /* constructor:                                                           */
  /* ---------------------------------------------------------------------- */
  /** This constructor creates all job worker queues and exchanges by querying 
   * the job_queues table.  It also creates each active tenant's command and 
   * event topics and their exchanges by accessing data in the tenants table.
   * Without the ability to connect to the database, not much is going to get
   * done even though we don't throw exceptions.
 * @throws TapisException 
   */
  private QueueManager(QueueManagerParms parms) 
   throws TapisException
  {
      // Make sure we have a parameter object.
      if (parms == null) {
          String msg = MsgUtils.getMsg("TAPIS_NULL_PARAMETER", "QueueManager", 
                                       "parms");
          _log.error(msg);
          throw new TapisException(msg);
      }
      
      // Validate the parameters.
      parms.validate();
      
      // Set the parms for the singleton.
      _parms = parms;
      
      // Create the multi-tenant queues.
      try {createStandardMultiTenantQueues();}
      catch (Exception e) {
          String msg = MsgUtils.getMsg("TAPIS_QMGR_INIT_ERROR", ALL_TENANTS_NAME);
          _log.error(msg, e);
      }
  }
  
  /* ********************************************************************** */
  /*                             Public Methods                             */
  /* ********************************************************************** */
  /* ---------------------------------------------------------------------- */
  /* getInstance:                                                           */
  /* ---------------------------------------------------------------------- */
  /** Get the singleton instance of this class, creating it if necessary.
   * If the singleton already exists, the parameters are ignored.
   * 
   * @return the new or existing singleton.
 * @throws TapisException 
   */
  public static QueueManager getInstance(QueueManagerParms parms) 
   throws TapisException
  {
    // Create the singleton instance if it's null without
    // setting up a synchronized block in the common case.
    if (_instance == null) {
      synchronized (QueueManager.class) {
        if (_instance == null) _instance = new QueueManager(parms);
      }
    }
    return _instance;
  }
  
  /* ---------------------------------------------------------------------- */
  /* getInstance:                                                           */
  /* ---------------------------------------------------------------------- */
  /** Get the singleton instance of this class.  Calling this method before
   * the singleton exists causes a runtime error.
   * 
   * @return the existing singleton
   */
  public static QueueManager getInstance()
  {
    // The singleton instance must have been created before
    // this method is called.
    if (_instance == null) {
        String msg = MsgUtils.getMsg("QMGR_UNINITIALIZED_ERROR");
        _log.error(msg);
        throw new TapisRuntimeException(msg);
    }
    return _instance;
  }
  
  /* ---------------------------------------------------------------------- */
  /* getNewOutChannel:                                                      */
  /* ---------------------------------------------------------------------- */
  /** Return a new outbound channel on the existing queuing system connection.
   * 
   * @return the new channel
   * @throws JobSchedulerException on error
   */
  public Channel getNewOutChannel()
    throws TapisQueueException
  {
      // Create a new channel in this phase's connection.
      Channel channel = null;
      try {channel = getOutConnection().createChannel();} 
       catch (IOException e) {
           String msg = MsgUtils.getMsg("QMGR_CHANNEL_CREATE_ERROR", 
                                        getOutConnectionName(), e.getMessage());
           _log.error(msg, e);
           throw new TapisQueueException(msg, e);
       }
      
      // Tracing.
      if (_log.isInfoEnabled()) 
          _log.info("Created channel number " + channel.getChannelNumber() + 
                    " on " + getOutConnectionName() + ".");
       
      return channel;
  }
  
  /* ---------------------------------------------------------------------- */
  /* getNewInChannel:                                                       */
  /* ---------------------------------------------------------------------- */
  /** Return a new inbound channel on the existing queuing system connection.
   * 
   * @return the new channel
   * @throws JobSchedulerException on error
   */
  public Channel getNewInChannel()
    throws TapisQueueException
  {
      // Create a new channel in this phase's connection.
      Channel channel = null;
      try {channel = getInConnection().createChannel();} 
       catch (IOException e) {
           String msg = MsgUtils.getMsg("QMGR_CHANNEL_CREATE_ERROR", 
                                        getOutConnectionName(), e.getMessage());
           _log.error(msg, e);
           throw new TapisQueueException(msg, e);
       }
      
      // Tracing.
      if (_log.isInfoEnabled()) 
          _log.info("Created channel number " + channel.getChannelNumber() + 
                    " on " + getInConnectionName() + ".");
       
      return channel;
  }
  
  /* ---------------------------------------------------------------------- */
  /* postDeadLetterQueue:                                                   */
  /* ---------------------------------------------------------------------- */
  /** Write a json message to the named tenant queue.  The queue name is used 
   * as the routing key on the direct exchange.
   * 
   * @param queueName the target queue name
   * @param message a json string
   */
  public void postDeadLetterQueue(String message)
    throws TapisQueueException
  {
    // Get the exchange and queuenames.
    String queueName    = getAllTenantDeadLetterQueueName();
    String exchangeName = getAllTenantDeadLetterExchangeName(); 
    
    // Create a temporary channel.
    Channel channel = null;
    boolean abortChannel = false;
    try {
      // Create a temporary channel.
      try {channel = getNewOutChannel();}
        catch (Exception e) {
          String msg = MsgUtils.getMsg("QMGR_CHANNEL_TENANT_ERROR", ALL_TENANTS_NAME);
          _log.error(msg, e);
          throw e;
        }
    
      // Publish the message to the queue.
      try {
        // Write the job to the tenant recovery queue.
        channel.basicPublish(exchangeName, DEFAULT_BINDING_KEY, QueueManager.PERSISTENT_TEXT, 
                             message.getBytes("UTF-8"));
        
        // Tracing.
        if (_log.isDebugEnabled()) {
            String msg = MsgUtils.getMsg("QMGR_POST", ALL_TENANTS_NAME, exchangeName, queueName);
            _log.debug(msg);
        }
      }
        catch (Exception e) {
          String msg = MsgUtils.getMsg("QMGR_PUBLISH_ERROR", exchangeName, 
                                       getOutConnectionName(), channel.getChannelNumber(), 
                                       e.getMessage());
          _log.error(msg, e);
          throw new TapisQueueException(msg, e);
        }
    } 
    catch (Exception e) {
      // Affect the way we close the channel and then rethrow exception.
      abortChannel = true;
      throw e;
    }
    finally {
      // Channel clean up
      if (channel != null) {
        try {
          // Close the channel one way or the other.
          if (abortChannel) channel.abort();
            else channel.close();
        } 
          catch (Exception e) {
            String msg = MsgUtils.getMsg("QMGR_CHANNEL_CLOSE_ERROR", channel.getChannelNumber(), 
                                         e.getMessage());
            _log.error(msg, e);
          }
      }
    }
  }

  /* ---------------------------------------------------------------------- */
  /* getTenantExchangeArgs:                                                 */
  /* ---------------------------------------------------------------------- */
  /** Get the configuration arguments for the standard exchanges created for
   * tenants.  Using these arguments both senders and receivers can create the
   * exchange since it will be created in exactly the same way in both cases.  
   * 
   * @param tenantId the tenant who is creating an exchange
   * @return the exchange configuration arguments
   */
  public Map getTenantExchangeArgs(String exchangeName, String tenantId)
  {
      // Create the argument mapping.
      HashMap args = new HashMap<>();
      
      // Special case AllTenants.
      if (ALL_TENANTS_NAME.equals(tenantId)) {
          if (exchangeName.endsWith(MULTI_TENANT_ALT_EXCHANGE_SUFFIX))
              args.put("x-dead-letter-exchange", getAllTenantDeadLetterExchangeName());
          return args;
      }
      
      args.put("x-dead-letter-exchange", getAllTenantDeadLetterExchangeName());
      args.put("alternate-exchange", getAllTenantAltExchangeName());
      return args;
  }
  
  /* ---------------------------------------------------------------------- */
  /* createAndBindJobSpecificTopic:                                         */
  /* ---------------------------------------------------------------------- */
  /** Create and bind a job-specific topic to the tenant's command exchange.
   * 
   * @param tenantId the job's tenant
   * @param jobUuid the job's uuid
   * @throws JobQueueException
   */
  public void createAndBindSpecificTopic(Channel channel, String exchangeName, 
                                            String topicName, String bindingKey)
   throws TapisQueueException
  {
      // Set the options for a transient, job-specific topic.
      boolean durable    = false;
      boolean exclusive  = false;
      boolean autoDelete = true;

      // Create the topic.
      createAndBindQueue(channel, exchangeName, topicName,
                         bindingKey, durable, exclusive, autoDelete);
  }
 
  /* ---------------------------------------------------------------------- */
  /* cancelConsumer:                                                        */
  /* ---------------------------------------------------------------------- */
  /** Cancel the specified consumer on the channel.  If the queue being read
   * is configured with autoDelete, then the queue should be deleted as a 
   * result of this operation if there are no more subscribers reading the
   * queue.  
   * 
   * In general, this method should only be used when there's a need at
   * some later time to reuse the channel.  Otherwise, closing the channel will
   * release more resources and also cause autoDelete processing.
   * 
   * @param channel the channel on which basicConsume has been called
   * @param consumerTag the consumer's id
   */
  public void cancelConsumer(Channel channel, String consumerTag, String queueName)
   throws TapisQueueException
  {
      // Don't blow up.
      if (channel == null || consumerTag == null) return;
      
      // Cancel the consumer on the provided channel.
      try {channel.basicCancel(consumerTag);}
          catch (Exception e) {
            String msg = MsgUtils.getMsg("QMGR_CANCEL_TOPIC_CONSUMER", 
                                         channel.getChannelNumber(), consumerTag,
                                         queueName, e.getMessage());
            _log.error(msg, e);
            throw new TapisQueueException(msg, e);
          }
  }
  
  /* ---------------------------------------------------------------------- */
  /* dumpMessageInfo:                                                       */
  /* ---------------------------------------------------------------------- */
  /** Construct a delivery message and threading information.  This method
   * should only be called after checking that debugging is enabled.
   * 
   * @param consumerTag the tag associated with the receiving consumer
   * @param envelope the message envelope
   * @param properties the message properties
   * @param body the message
   */
  public String dumpMessageInfo(String consumerTag, Envelope envelope, 
                                AMQP.BasicProperties properties, byte[] body)
  {
      // We assume all input parameters are non-null.
      Thread curthd = Thread.currentThread();
      ThreadGroup curgrp = curthd.getThreadGroup();
      int bodyLen = body == null ? 0 : body.length;
      String msg = "\n------------------------- Bytes Received: " + bodyLen + "\n";
      msg += "Consumer tag: " + consumerTag + "\n";
      msg += "Thread(name=" +curthd.getName() + ", isDaemon=" + curthd.isDaemon() + ")\n";
      msg += "ThreadGroup(name=" + curgrp.getName() + ", parentGroup=" + curgrp.getParent().getName() +
                  ", activeGroupCount=" + curgrp.activeGroupCount() + ", activeThreadCount=" + 
                  curgrp.activeCount() + ", isDaemon=" + curgrp.isDaemon() + ")\n";
      
      // Output is truncated at array size.
      Thread[] thdArray = new Thread[200];
      int thdArrayLen = curgrp.enumerate(thdArray, false); // non-recursive 
      msg += "ThreadArray(length=" + thdArrayLen + ", names=";
      for (int i = 0; i < thdArrayLen; i++) msg += thdArray[i].getName() + ((i < thdArrayLen-1) ? ", " : "");
      msg += ")\n";
      
      // Output is truncated at array size.
      ThreadGroup[] grpArray = new ThreadGroup[200];
      int grpArrayLen = curgrp.enumerate(grpArray, false); // non-recursive 
      msg += "ThreadGroupArray(length=" + grpArrayLen + ", names=";
      for (int i = 0; i < grpArrayLen; i++) msg += grpArray[i].getName() + ((i < grpArrayLen-1) ? ", " : "");
      msg += ")\n";
      
      // Avoid blowing up.
      if (envelope != null) msg += envelope.toString() + "\n";
      if (properties != null) {
          StringBuilder buf = new StringBuilder(512);
          properties.appendPropertyDebugStringTo(buf);
          msg += "Properties" + buf.toString() + "\n";
      }
      msg += "-------------------------------------------------\n";
      return msg;
  }

  /* ---------------------------------------------------------------------- */
  /* closeConnections:                                                      */
  /* ---------------------------------------------------------------------- */
  /** Close all connections managed by this queue manager.  Use -1 for 
   * unlimited wait time.
   * 
   * @param timeoutMs milliseconds to wait before forcing connection closed
   * */
  public void closeConnections(int timeoutMs)
  {
      // Close each connection.
      if (_inConnection != null) 
          try {_inConnection.close(timeoutMs);}
          catch (Exception e) {
              String msg = MsgUtils.getMsg("QMGR_CLOSE_CONN_ERROR", "inbound", e.getMessage());
              _log.error(msg, e);
          }
      if (_outConnection != null) 
          try {_outConnection.close(timeoutMs);}
          catch (Exception e) {
              String msg = MsgUtils.getMsg("QMGR_CLOSE_CONN_ERROR", "outbound", e.getMessage());
              _log.error(msg, e);
          }
  }
  
  /* ********************************************************************** */
  /*                             Private Methods                            */
  /* ********************************************************************** */
  /* ---------------------------------------------------------------------- */
  /* getOutConnection:                                                      */
  /* ---------------------------------------------------------------------- */
  /** Return a outbound connection to the queuing subsystem, creating the 
   * connection if necessary.
   * 
   * @return the connection
   * @throws JobQueueException on error.
   */
  private Connection getOutConnection()
   throws TapisQueueException
  {
      // Create the connection if necessary.
      if (_outConnection == null)
      {
        // Only allow one thread at a time to create a shared connection.
        synchronized(QueueManager.class) {
          // Don't do anything if another thread beat us to the punch.
          if (_outConnection == null)
            try {_outConnection = getConnectionFactory().newConnection(getOutConnectionName());}
            catch (IOException e) {
              String msg = MsgUtils.getMsg("QMGR_CONNECTION_CREATE_ERROR", e.getMessage());
              _log.error(msg, e);
              throw new TapisQueueException(msg, e);
            } 
            catch (TimeoutException e) {
              String msg = MsgUtils.getMsg("QMGR_CONNECTION_TIMEOUT_ERROR", e.getMessage());
              _log.error(msg, e);
              throw new TapisQueueException(msg, e);
            }
        } // synchronized
      }
      
      return _outConnection;
  }
  
  /* ---------------------------------------------------------------------- */
  /* getInConnection:                                                       */
  /* ---------------------------------------------------------------------- */
  /** Return a inbound connection to the queuing subsystem, creating the 
   * connection if necessary.
   * 
   * @return the connection
   * @throws JobQueueException on error.
   */
  private Connection getInConnection()
   throws TapisQueueException
  {
    // Create the connection if necessary.
    if (_inConnection == null)
    {
      // Only allow one thread at a time to create a shared connection.
      synchronized(QueueManager.class) {
        // Don't do anything if another thread beat us to the punch.
        if (_inConnection == null)
          try {_inConnection = getConnectionFactory().newConnection(getInConnectionName());}
          catch (IOException e) {
            String msg = MsgUtils.getMsg("QMGR_CONNECTION_CREATE_ERROR", e.getMessage());
            _log.error(msg, e);
            throw new TapisQueueException(msg, e);
          } 
          catch (TimeoutException e) {
            String msg = MsgUtils.getMsg("QMGR_CONNECTION_TIMEOUT_ERROR", e.getMessage());
            _log.error(msg, e);
            throw new TapisQueueException(msg, e);
          }
      } // synchronized
    }
    
    return _inConnection;
  }
  
  /* ---------------------------------------------------------------------- */
  /* getConnectionFactory:                                                  */
  /* ---------------------------------------------------------------------- */
  /** Return a connection factory, creating it if necessary.  The calling
   * method is required to handle multithread synchronization issues.  This
   * method assumes it is executed by only one thread at a time.s
   * 
   * @return this scheduler's queue connection factory.
   */
  private ConnectionFactory getConnectionFactory()
  {
      // Create the factory if necessary.
      if (_factory == null) 
      {
          // Get a rabbitmq connection factory.
          _factory = new ConnectionFactory();
          
          // Set the factory parameters.
          // TODO: generalize w/auth & network info & heartbeat
          _factory.setHost(_parms.getQueueHost());
          _factory.setPort(_parms.getQueuePort());
          _factory.setUsername(_parms.getQueueUser());
          _factory.setPassword(_parms.getQueuePassword());
          _factory.setAutomaticRecoveryEnabled(_parms.isQueueAutoRecoveryEnabled());
      }
      
      return _factory;
  }

  /* ---------------------------------------------------------------------- */
  /* createStandardMultiTenantQueue:                                        */
  /* ---------------------------------------------------------------------- */
  /** Create the exchanges and queues service all tenants in an installation.
   * These queue objects use the pseudo-tenant id "AllTenants", which should
   * never name an actual tenant and we treat as a reserved name.  
   * 
   * Note: We don't currently check for a tenant named "AllTenants".
   * 
   * @throws AloeException
   */
  @SuppressWarnings("unchecked")
  private void createStandardMultiTenantQueues()
   throws TapisQueueException
  {
      String allTenantId = QueueManager.ALL_TENANTS_NAME;
      Channel channel = null;
      try {
          // Create a temporary channel.
          try {channel = getNewInChannel();}
            catch (Exception e) {
              String msg = MsgUtils.getMsg("JOBS_QMGR_CHANNEL_TENANT_ERROR", allTenantId);
              _log.error(msg, e);
              throw e;
            }
          
          // Create the dead letter exchange and queue and bind them together.
          createExchangeAndQueue(channel, allTenantId, 
                                 getAllTenantDeadLetterExchangeName(), BuiltinExchangeType.FANOUT, 
                                 getAllTenantDeadLetterQueueName(), DEFAULT_BINDING_KEY, null);
          
          // Create the alternate exchange and queue and bind them together.  
          // Configure the dead letter queue on this exchange.
          HashMap exchangeArgs = new HashMap<>();
          exchangeArgs.put("x-dead-letter-exchange", getAllTenantDeadLetterExchangeName());
          createExchangeAndQueue(channel, allTenantId, 
                                 getAllTenantAltExchangeName(), BuiltinExchangeType.FANOUT, 
                                 getAllTenantAltQueueName(), DEFAULT_BINDING_KEY, 
                                 (HashMap) exchangeArgs.clone());
      }
      finally {
          // Close the channel if it exists and hasn't already been aborted.
          if (channel != null)
            try {channel.close();} 
                catch (Exception e1){
                String msg = MsgUtils.getMsg("QMGR_CHANNEL_CLOSE_ERROR", 
                                             channel.getChannelNumber(), e1.getMessage());
                _log.warn(msg, e1);
            }
        }
  }
  
  /* ---------------------------------------------------------------------- */
  /* createExchangeAndQueue:                                                */
  /* ---------------------------------------------------------------------- */
  /** Create the named exchange, the named queue, and bind them.
   * 
   * @param channel the communication channel
   * @param tenantId the tenant whose components are being configured
   * @param exchangeName the exchange to create
   * @param exchangeType the type of exchange
   * @param queueName the queue to create or null if no queue should be created
   * @param bindingKey the key to use when binding the queue to the exchange
   * @throws JobQueueException on error
   */
  private void createExchangeAndQueue(Channel channel, String tenantId, 
                                      String exchangeName, BuiltinExchangeType exchangeType,
                                      String queueName, String bindingKey, 
                                      Map exchangeArgs) 
   throws TapisQueueException
  {
      // Establish a durable exchange for the tenant.
      // Create the durable, non-autodelete topic exchange.
      boolean durable = true;
      boolean autodelete = false;
      try {channel.exchangeDeclare(exchangeName, exchangeType, durable, autodelete, exchangeArgs);}
          catch (IOException e) {
              String msg = MsgUtils.getMsg("QMGR_XCHG_TENANT_ERROR", tenantId, 
                                            getInConnectionName(), channel.getChannelNumber(), 
                                            e.getMessage());
              _log.error(msg, e);
              throw new TapisQueueException(msg, e);
          }
      
      // Worker processes create and bind their own queues using their runtime 
      // name parameter.  In these cases, there's no more work to do.
      if (queueName == null) return;
      
      // Create the durable queue or topic with a well-known name.
      durable = true;
      boolean exclusive = false;
      boolean autoDelete = false;
      try {channel.queueDeclare(queueName, durable, exclusive, autoDelete, null);}
          catch (IOException e) {
              String msg = MsgUtils.getMsg("QMGR_Q_DECLARE_ERROR", exchangeType.getType(), 
                                           queueName, getInConnectionName(), 
                                           channel.getChannelNumber(), e.getMessage());
              _log.error(msg, e);
              throw new TapisQueueException(msg, e);
          }
      
      // Bind the queue/topic to the exchange with the binding key.
      try {channel.queueBind(queueName, exchangeName, bindingKey);}
          catch (IOException e) {
              String msg = MsgUtils.getMsg("QMGR_Q_BIND_ERROR", exchangeType.getType(), 
                                           bindingKey, queueName, getInConnectionName(), 
                                           channel.getChannelNumber(), e.getMessage());
              _log.error(msg, e);
              throw new TapisQueueException(msg, e);
          }
  }
  
  /* ---------------------------------------------------------------------- */
  /* createAndBindQueue:                                                    */
  /* ---------------------------------------------------------------------- */
  /** Create a tenant queue and bind it to the specified direct exchange.
   * The binding key is the queue name. 
   * 
   * @param channel the channel used to post to the queue
   * @param exchangeName the target exchange
   * @param queueName the queue to be posted
   * @throws JobQueueException on error
   */
  private void createAndBindQueue(Channel channel, String exchangeName, String queueName)
    throws TapisQueueException
  {
      // Set the standard queue/topic options.
      boolean durable = true;
      boolean exclusive = false;
      boolean autoDelete = false;
      
      // Create the queue with the configured name.
      createAndBindQueue(channel, exchangeName, queueName, null, durable, exclusive, autoDelete);
  }
  
  /* ---------------------------------------------------------------------- */
  /* createAndBindQueue:                                                    */
  /* ---------------------------------------------------------------------- */
  /** Create a tenant queue and bind it to the specified direct exchange.
   * The binding key is the queue name. 
   * 
   * @param channel the channel used to post to the queue
   * @param exchangeName the target exchange
   * @param queueName the queue to be posted
   * @param bindingKey the key to bind the queue to the exchange or null to use queueName
   * @param durable whether the queue will survive a broker restart
   * @param exclusive whether used by only one connection and the queue will be deleted when that connection closes
   * @param autoDelete whether queue that has had at least one consumer is deleted when last consumer unsubscribes
   * @throws JobQueueException on error
   */
  private void createAndBindQueue(Channel channel, String exchangeName, String queueName, String bindingKey, 
                                  boolean durable, boolean exclusive, boolean autoDelete)
    throws TapisQueueException
  {
    // Create the queue with the configured name.
    try {channel.queueDeclare(queueName, durable, exclusive, autoDelete, null);}
        catch (IOException e) {
            String msg = MsgUtils.getMsg("QMGR_Q_DECLARE_ERROR", "queue", 
                                         queueName, getOutConnectionName(), 
                                         channel.getChannelNumber(), e.getMessage());
            _log.error(msg, e);
            throw new TapisQueueException(msg, e);
        }
   
    // Bind the queue to the exchange using the queue name as the 
    // binding key if the caller hasn't specified a key.
    if (bindingKey == null) bindingKey = queueName;
    try {channel.queueBind(queueName, exchangeName, bindingKey);}
        catch (IOException e) {
            String msg = MsgUtils.getMsg("QMGR_Q_BIND_ERROR", "queue", queueName, 
                                         bindingKey, getOutConnectionName(), 
                                         channel.getChannelNumber(), e.getMessage());
            _log.error(msg, e);
            throw new TapisQueueException(msg, e);
        }
  }
  
  /* ---------------------------------------------------------------------- */
  /* getOutConnectionName:                                                  */
  /* ---------------------------------------------------------------------- */
  public String getOutConnectionName()
  {return getOutConnectionName(_parms.getInstanceName());}
  
  /* ---------------------------------------------------------------------- */
  /* getInConnectionName:                                                   */
  /* ---------------------------------------------------------------------- */
  public String getInConnectionName()
  {return getInConnectionName(_parms.getInstanceName());}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy