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

com.datastax.oss.pulsar.jms.PulsarJMSProducer Maven / Gradle / Ivy

/*
 * Copyright DataStax, Inc.
 *
 * Licensed 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.datastax.oss.pulsar.jms;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.jms.CompletionListener;
import jakarta.jms.DeliveryMode;
import jakarta.jms.Destination;
import jakarta.jms.InvalidDestinationException;
import jakarta.jms.InvalidDestinationRuntimeException;
import jakarta.jms.JMSException;
import jakarta.jms.JMSProducer;
import jakarta.jms.JMSRuntimeException;
import jakarta.jms.Message;
import jakarta.jms.MessageFormatException;
import jakarta.jms.MessageFormatRuntimeException;
import jakarta.jms.MessageNotWriteableRuntimeException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class PulsarJMSProducer implements JMSProducer {
  private final PulsarJMSContext parent;
  private boolean disableMessageId;
  private boolean disableMessageTimestamp;
  private int deliveryMode = Message.DEFAULT_DELIVERY_MODE;
  private int priority = Message.DEFAULT_PRIORITY;
  private long deliveryDelay = Message.DEFAULT_DELIVERY_DELAY;
  private long timeToLive = Message.DEFAULT_TIME_TO_LIVE;
  private final Map properties = new HashMap<>();
  private CompletionListener completionListener;
  private byte[] correlationID;
  private String jmsType;
  private Destination jmsReplyTo;

  public PulsarJMSProducer(PulsarJMSContext parent) {
    this.parent = parent;
  }

  /**
   * Sends a message to the specified destination, using any send options, message properties and
   * message headers that have been defined on this {@code JMSProducer}.
   *
   * @param destination the destination to send this message to
   * @param message the message to send
   * @return this {@code JMSProducer}
   * @throws MessageFormatRuntimeException if an invalid message is specified.
   * @throws InvalidDestinationRuntimeException if a client uses this method with an invalid
   *     destination.
   * @throws MessageNotWriteableRuntimeException if this {@code JMSProducer} has been configured to
   *     set a message property, but the message's properties are read-only
   * @throws JMSRuntimeException if the JMS provider fails to send the message due to some internal
   *     error.
   */
  @Override
  public JMSProducer send(Destination destination, Message message) {
    Utils.runtimeException(() -> getProducerAndSend(destination, message));
    return this;
  }

  /**
   * Send a {@code TextMessage} with the specified body to the specified destination, using any send
   * options, message properties and message headers that have been defined on this {@code
   * JMSProducer}.
   *
   * @param destination the destination to send this message to
   * @param body the body of the {@code TextMessage} that will be sent. If a null value is specified
   *     then a {@code TextMessage} with no body will be sent.
   * @return this {@code JMSProducer}
   * @throws MessageFormatRuntimeException if an invalid message is specified.
   * @throws InvalidDestinationRuntimeException if a client uses this method with an invalid
   *     destination.
   * @throws JMSRuntimeException if the JMS provider fails to send the message due to some internal
   *     error.
   */
  @Override
  public JMSProducer send(Destination destination, String body) {
    Utils.runtimeException(
        () -> getProducerAndSend(destination, parent.session.createTextMessage(body)));
    return this;
  }

  /**
   * Send a {@code MapMessage} with the specified body to the specified destination, using any send
   * options, message properties and message headers that have been defined on this {@code
   * JMSProducer}.
   *
   * @param destination the destination to send this message to
   * @param body the body of the {@code MapMessage} that will be sent. If a null value is specified
   *     then a {@code MapMessage} with no map entries will be sent.
   * @return this {@code JMSProducer}
   * @throws MessageFormatRuntimeException if an invalid message is specified.
   * @throws InvalidDestinationRuntimeException if a client uses this method with an invalid
   *     destination.
   * @throws JMSRuntimeException if the JMS provider fails to send the message due to some internal
   *     error.
   */
  @Override
  public JMSProducer send(Destination destination, Map body) {
    Utils.runtimeException(
        () -> getProducerAndSend(destination, parent.session.createMapMessage(body)));
    return this;
  }

  /**
   * Send a {@code BytesMessage} with the specified body to the specified destination, using any
   * send options, message properties and message headers that have been defined on this {@code
   * JMSProducer}.
   *
   * @param destination the destination to send this message to
   * @param body the body of the {@code BytesMessage} that will be sent. If a null value is
   *     specified then a {@code BytesMessage} with no body will be sent.
   * @return this {@code JMSProducer}
   * @throws MessageFormatRuntimeException if an invalid message is specified.
   * @throws InvalidDestinationRuntimeException if a client uses this method with an invalid
   *     destination.
   * @throws JMSRuntimeException if the JMS provider fails to send the message due to some internal
   *     error.
   */
  @Override
  public JMSProducer send(Destination destination, byte[] body) {
    Utils.runtimeException(
        () -> getProducerAndSend(destination, parent.session.createBytesMessage().fill(body)));
    return this;
  }

  /**
   * Send an {@code ObjectMessage} with the specified body to the specified destination, using any
   * send options, message properties and message headers that have been defined on this {@code
   * JMSProducer}.
   *
   * @param destination the destination to send this message to
   * @param body the body of the ObjectMessage that will be sent. If a null value is specified then
   *     an {@code ObjectMessage} with no body will be sent.
   * @return this {@code JMSProducer}
   * @throws MessageFormatRuntimeException if an invalid message is specified.
   * @throws InvalidDestinationRuntimeException if a client uses this method with an invalid
   *     destination.
   * @throws JMSRuntimeException if JMS provider fails to send the message due to some internal
   *     error.
   */
  @Override
  public JMSProducer send(Destination destination, Serializable body) {
    Utils.runtimeException(
        () -> getProducerAndSend(destination, parent.session.createObjectMessage(body)));
    return this;
  }

  private void getProducerAndSend(Destination destination, Message message) throws JMSException {
    if (message == null) {
      if (completionListener != null) {
        completionListener.onException(null, new MessageFormatRuntimeException("message is null"));
        return;
      } else {
        throw new MessageFormatException("message is null");
      }
    }
    if (destination == null) {
      if (completionListener != null) {
        completionListener.onException(
            message, new InvalidDestinationRuntimeException("message is null"));
        return;
      } else {
        throw new InvalidDestinationException("destination is null");
      }
    }
    PulsarMessageProducer producer = parent.session.createProducer(null);
    producer.setDisableMessageID(disableMessageId);

    producer.setDisableMessageTimestamp(disableMessageTimestamp);
    producer.setDeliveryMode(deliveryMode);
    producer.setPriority(priority);
    producer.setDeliveryDelay(deliveryDelay);
    producer.setTimeToLive(timeToLive);
    for (Map.Entry prop : properties.entrySet()) {
      message.setObjectProperty(prop.getKey(), prop.getValue());
    }
    message.setJMSPriority(priority);
    if (message.getJMSCorrelationIDAsBytes() == null) {
      message.setJMSCorrelationIDAsBytes(correlationID);
    }
    if (message.getJMSType() == null) {
      message.setJMSType(jmsType);
    }
    if (message.getJMSReplyTo() == null) {
      message.setJMSReplyTo(jmsReplyTo);
    }

    if (completionListener != null) {
      producer.send(destination, message, deliveryMode, priority, timeToLive, completionListener);
    } else {
      producer.send(destination, message, deliveryMode, priority, timeToLive);
    }
  }

  /**
   * Specifies whether message IDs may be disabled for messages that are sent using this {@code
   * JMSProducer}
   *
   * 

Since message IDs take some effort to create and increase a message's size, some JMS * providers may be able to optimise message overhead if they are given a hint that the message ID * is not used by an application. By calling this method, a JMS application enables this potential * optimisation for all messages sent using this {@code JMSProducer}. If the JMS provider accepts * this hint, these messages must have the message ID set to null; if the provider ignores the * hint, the message ID must be set to its normal unique value. * *

Message IDs are enabled by default. * * @param value indicates whether message IDs may be disabled * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set message ID to disabled due to some * internal error. * @see JMSProducer#getDisableMessageID */ @Override public JMSProducer setDisableMessageID(boolean value) { this.disableMessageId = value; return this; } /** * Gets an indication of whether message IDs are disabled. * * @return an indication of whether message IDs are disabled * @throws JMSRuntimeException if the JMS provider fails to determine if message IDs are disabled * due to some internal error. * @see JMSProducer#setDisableMessageID */ @Override public boolean getDisableMessageID() { return disableMessageId; } /** * Specifies whether message timestamps may be disabled for messages that are sent using this * {@code JMSProducer}. * *

Since timestamps take some effort to create and increase a message's size, some JMS * providers may be able to optimise message overhead if they are given a hint that the timestamp * is not used by an application. By calling this method, a JMS application enables this potential * optimisation for all messages sent using this {@code JMSProducer}. If the JMS provider accepts * this hint, these messages must have the timestamp set to zero; if the provider ignores the * hint, the timestamp must be set to its normal value. * *

Message timestamps are enabled by default. * * @param value indicates whether message timestamps may be disabled * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set timestamps to disabled due to some * internal error. * @see JMSProducer#getDisableMessageTimestamp */ @Override public JMSProducer setDisableMessageTimestamp(boolean value) { this.disableMessageTimestamp = value; return this; } /** * Gets an indication of whether message timestamps are disabled. * * @return an indication of whether message timestamps are disabled * @throws JMSRuntimeException if the JMS provider fails to determine if timestamps are disabled * due to some internal error. * @see JMSProducer#setDisableMessageTimestamp */ @Override public boolean getDisableMessageTimestamp() { return disableMessageTimestamp; } /** * Specifies the delivery mode of messages that are sent using this {@code JMSProducer} * *

Delivery mode is set to {@code PERSISTENT} by default. * * @param deliveryMode the message delivery mode to be used; legal values are {@code * DeliveryMode.NON_PERSISTENT} and {@code DeliveryMode.PERSISTENT} * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the delivery mode due to some * internal error. * @see JMSProducer#getDeliveryMode * @see DeliveryMode#NON_PERSISTENT * @see DeliveryMode#PERSISTENT * @see Message#DEFAULT_DELIVERY_MODE */ @Override public JMSProducer setDeliveryMode(int deliveryMode) { switch (deliveryMode) { case DeliveryMode.NON_PERSISTENT: case DeliveryMode.PERSISTENT: break; default: throw new JMSRuntimeException("Invalid deliveryMode " + deliveryMode); } this.deliveryMode = deliveryMode; return this; } /** * Returns the delivery mode of messages that are sent using this {@code JMSProducer} * * @return the message delivery mode * @throws JMSRuntimeException if the JMS provider fails to get the delivery mode due to some * internal error. * @see JMSProducer#setDeliveryMode */ @Override public int getDeliveryMode() { return deliveryMode; } /** * Specifies the priority of messages that are sent using this {@code JMSProducer} * *

The JMS API defines ten levels of priority value, with 0 as the lowest priority and 9 as the * highest. Clients should consider priorities 0-4 as gradations of normal priority and priorities * 5-9 as gradations of expedited priority. Priority is set to 4 by default. * * @param priority the message priority to be used; must be a value between 0 and 9 * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the priority due to some internal * error. * @see JMSProducer#getPriority * @see Message#DEFAULT_PRIORITY */ @Override public JMSProducer setPriority(int priority) { if (priority < 0 || priority > 10) { throw new JMSRuntimeException("Invalid priority " + priority); } this.priority = priority; return this; } /** * Return the priority of messages that are sent using this {@code JMSProducer} * * @return the message priority * @throws JMSRuntimeException if the JMS provider fails to get the priority due to some internal * error. * @see JMSProducer#setPriority */ @Override public int getPriority() { return priority; } /** * Specifies the time to live of messages that are sent using this {@code JMSProducer}. This is * used to determine the expiration time of a message. * *

The expiration time of a message is the sum of the message's time to live and the time it is * sent. For transacted sends, this is the time the client sends the message, not the time the * transaction is committed. * *

Clients should not receive messages that have expired; however, JMS does not guarantee that * this will not happen. * *

A JMS provider should do its best to accurately expire messages; however, JMS does not * define the accuracy provided. It is not acceptable to simply ignore time-to-live. * *

Time to live is set to zero by default, which means a message never expires. * * @param timeToLive the message time to live to be used, in milliseconds; a value of zero means * that a message never expires. * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the time to live due to some * internal error. * @see JMSProducer#getTimeToLive * @see Message#DEFAULT_TIME_TO_LIVE */ @Override public JMSProducer setTimeToLive(long timeToLive) { this.timeToLive = timeToLive; return this; } /** * Returns the time to live of messages that are sent using this {@code JMSProducer}. * * @return the message time to live in milliseconds; a value of zero means that a message never * expires. * @throws JMSRuntimeException if the JMS provider fails to get the time to live due to some * internal error. * @see JMSProducer#setTimeToLive */ @Override public long getTimeToLive() { return timeToLive; } /** * Sets the minimum length of time in milliseconds that must elapse after a message is sent before * the JMS provider may deliver the message to a consumer. * *

For transacted sends, this time starts when the client sends the message, not when the * transaction is committed. * *

deliveryDelay is set to zero by default. * * @param deliveryDelay the delivery delay in milliseconds. * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the delivery delay due to some * internal error. * @see JMSProducer#getDeliveryDelay * @see Message#DEFAULT_DELIVERY_DELAY */ @Override public JMSProducer setDeliveryDelay(long deliveryDelay) { this.deliveryDelay = deliveryDelay; return this; } /** * Gets the minimum length of time in milliseconds that must elapse after a message is sent before * the JMS provider may deliver the message to a consumer. * * @return the delivery delay in milliseconds. * @throws JMSRuntimeException if the JMS provider fails to get the delivery delay due to some * internal error. * @see JMSProducer#setDeliveryDelay */ @Override public long getDeliveryDelay() { return deliveryDelay; } /** * Specifies whether subsequent calls to {@code send} on this {@code JMSProducer} object should be * synchronous or asynchronous. If the specified {@code CompletionListener} is not null then * subsequent calls to {@code send} will be asynchronous. If the specified {@code * CompletionListener} is null then subsequent calls to {@code send} will be synchronous. Calls to * {@code send} are synchronous by default. * *

If a call to {@code send} is asynchronous then part of the work involved in sending the * message will be performed in a separate thread and the specified CompletionListener * will be notified when the operation has completed. * *

When the message has been successfully sent the JMS provider invokes the callback method * onCompletion on the CompletionListener object. Only when that * callback has been invoked can the application be sure that the message has been successfully * sent with the same degree of confidence as if the send had been synchronous. An application * which requires this degree of confidence must therefore wait for the callback to be invoked * before continuing. * *

The following information is intended to give an indication of how an asynchronous send * would typically be implemented. * *

In some JMS providers, a normal synchronous send involves sending the message to a remote * JMS server and then waiting for an acknowledgement to be received before returning. It is * expected that such a provider would implement an asynchronous send by sending the message to * the remote JMS server and then returning without waiting for an acknowledgement. When the * acknowledgement is received, the JMS provider would notify the application by invoking the * onCompletion method on the application-specified CompletionListener * object. If for some reason the acknowledgement is not received the JMS provider would notify * the application by invoking the CompletionListener's onException * method. * *

In those cases where the JMS specification permits a lower level of reliability, a normal * synchronous send might not wait for an acknowledgement. In that case it is expected that an * asynchronous send would be similar to a synchronous send: the JMS provider would send the * message to the remote JMS server and then return without waiting for an acknowledgement. * However the JMS provider would still notify the application that the send had completed by * invoking the onCompletion method on the application-specified * CompletionListener object. * *

It is up to the JMS provider to decide exactly what is performed in the calling thread and * what, if anything, is performed asynchronously, so long as it satisfies the requirements given * below: * *

Quality of service: After the send operation has completed successfully, which means * that the message has been successfully sent with the same degree of confidence as if a normal * synchronous send had been performed, the JMS provider must invoke the CompletionListener * 's onCompletion method. The CompletionListener must not be * invoked earlier than this. * *

Exceptions: If an exception is encountered during the call to the send * method then an appropriate exception should be thrown in the thread that is calling the * send method. In this case the JMS provider must not invoke the CompletionListener * 's onCompletion or onException method. If an exception is * encountered which cannot be thrown in the thread that is calling the send method * then the JMS provider must call the CompletionListener's onException * method. In both cases if an exception occurs it is undefined whether or not the message was * successfully sent. * *

Message order: If the same JMSContext is used to send multiple messages * then JMS message ordering requirements must be satisfied. This applies even if a combination of * synchronous and asynchronous sends has been performed. The application is not required to wait * for an asynchronous send to complete before sending the next message. * *

Close, commit or rollback: If the close method is called on the * JMSContext then the JMS provider must block until any incomplete send operations have * been completed and all {@code CompletionListener} callbacks have returned before closing the * object and returning. If the session is transacted (uses a local transaction) then when the * JMSContext's commit or rollback method is called the JMS * provider must block until any incomplete send operations have been completed and all {@code * CompletionListener} callbacks have returned before performing the commit or rollback. * Incomplete sends should be allowed to complete normally unless an error occurs. * *

A CompletionListener callback method must not call close, * commit or rollback on its own JMSContext. Doing so will cause * the close, commit or rollback to throw an * IllegalStateRuntimeException. * *

Restrictions on usage in Java EE This method must not be used in a Java EE EJB or web * container. Doing so may cause a {@code JMSRuntimeException} to be thrown though this is not * guaranteed. * *

Message headers JMS defines a number of message header fields and message properties * which must be set by the "JMS provider on send". If the send is asynchronous these fields and * properties may be accessed on the sending client only after the CompletionListener * has been invoked. If the CompletionListener's onException method is * called then the state of these message header fields and properties is undefined. * *

Restrictions on threading: Applications that perform an asynchronous send must * confirm to the threading restrictions defined in JMS. This means that the session may be used * by only one thread at a time. * *

Setting a CompletionListener does not cause the session to be dedicated to the * thread of control which calls the CompletionListener. The application thread may * therefore continue to use the session after performing an asynchronous send. However the * CompletionListener's callback methods must not use the session if an application thread * might be using the session at the same time. * *

Use of the CompletionListener by the JMS provider: A session will only * invoke one CompletionListener callback method at a time. For a given * JMSContext, callbacks (both {@code onCompletion} and {@code onException}) will be * performed in the same order as the corresponding calls to the send method. A JMS * provider must not invoke the CompletionListener from the thread that is calling * the send method. * *

Restrictions on the use of the Message object: Applications which perform an * asynchronous send must take account of the restriction that a Message object is * designed to be accessed by one logical thread of control at a time and does not support * concurrent use. * *

After the send method has returned, the application must not attempt to read * the headers, properties or body of the Message object until the * CompletionListener's onCompletion or onException method has * been called. This is because the JMS provider may be modifying the Message object * in another thread during this time. The JMS provider may throw an JMSException if * the application attempts to access or modify the Message object after the * send method has returned and before the CompletionListener has been * invoked. If the JMS provider does not throw an exception then the behaviour is undefined. * * @param completionListener If asynchronous send behaviour is required, this should be set to a * {@code CompletionListener} to be notified when the send has completed. If synchronous send * behaviour is required, this should be set to {@code null}. * @return this {@code JMSProducer} * @throws JMSRuntimeException if an internal error occurs * @see JMSProducer#getAsync * @see CompletionListener */ @Override public JMSProducer setAsync(CompletionListener completionListener) { this.completionListener = completionListener; return this; } /** * If subsequent calls to {@code send} on this {@code JMSProducer} object have been configured to * be asynchronous then this method returns the {@code CompletionListener} that has previously * been configured. If subsequent calls to {@code send} have been configured to be synchronous * then this method returns {@code null}. * * @return the {@code CompletionListener} or {@code null} * @throws JMSRuntimeException if the JMS provider fails to get the required information due to * some internal error. * @see JMSProducer#setAsync */ @Override public CompletionListener getAsync() { return completionListener; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code boolean} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code boolean} value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getBooleanProperty */ @Override public JMSProducer setProperty(String name, boolean value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code byte} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code byte} value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getByteProperty */ @Override public JMSProducer setProperty(String name, byte value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code short} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code short} property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getShortProperty */ @Override public JMSProducer setProperty(String name, short value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code int} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code int} property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getIntProperty */ @Override public JMSProducer setProperty(String name, int value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code long} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code long} property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getLongProperty */ @Override public JMSProducer setProperty(String name, long value) { setPropertyInternal(name, value); return this; } private void setPropertyInternal(String name, Object value) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("Invalid empty property name"); } Utils.runtimeException(() -> PulsarMessage.validateWritableObject(value)); this.properties.put(name, value); } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code float} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code float} property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getFloatProperty */ @Override public JMSProducer setProperty(String name, float value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code double} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code double} property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getDoubleProperty */ @Override public JMSProducer setProperty(String name, double value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified {@code String} value. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the {@code String} property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @see JMSProducer#getStringProperty */ @Override public JMSProducer setProperty(String name, String value) { setPropertyInternal(name, value); return this; } /** * Specifies that messages sent using this {@code JMSProducer} will have the specified property * set to the specified Java object value. * *

Note that this method works only for the objectified primitive object types ({@code * Integer}, {@code Double}, {@code Long} ...) and {@code String} objects. * *

This will replace any property of the same name that is already set on the message being * sent. * * @param name the name of the property * @param value the Java object property value to set * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the property due to some internal * error. * @throws IllegalArgumentException if the name is null or if the name is an empty string. * @throws MessageFormatRuntimeException if the object is invalid * @see JMSProducer#getObjectProperty */ @Override public JMSProducer setProperty(String name, Object value) { setPropertyInternal(name, value); return this; } /** * Clears any message properties set on this {@code JMSProducer} * * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to clear the message properties due to * some internal error. */ @Override public JMSProducer clearProperties() { this.properties.clear(); return this; } /** * Indicates whether a message property with the specified name has been set on this {@code * JMSProducer} * * @param name the name of the property * @return true whether the property exists * @throws JMSRuntimeException if the JMS provider fails to determine whether the property exists * due to some internal error. */ @Override public boolean propertyExists(String name) { return properties.containsKey(name); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code boolean}. * * @param name the name of the property * @return the property value, converted to a {@code boolean} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, boolean) */ @Override public boolean getBooleanProperty(String name) { switch (properties.getOrDefault(name, "false").toString()) { case "true": return true; case "false": return false; } throw new MessageFormatRuntimeException("Invalid value for boolean"); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code String}. * * @param name the name of the property * @return the property value, converted to a {@code byte} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, byte) */ @Override public byte getByteProperty(String name) { if (!properties.containsKey(name)) { throw new NumberFormatException(); } final Object currentValue = properties.getOrDefault(name, "0"); if (currentValue instanceof Number) { if (!(currentValue instanceof Byte)) { throw new MessageFormatRuntimeException("unsupported conversion"); } else { return ((Number) currentValue).byteValue(); } } return Utils.runtimeException(() -> Byte.parseByte(currentValue.toString())); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code short}. * * @param name the name of the property * @return the property value, converted to a {@code short} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, short) */ @Override public short getShortProperty(String name) { if (!properties.containsKey(name)) { throw new NumberFormatException(); } final Object currentValue = properties.getOrDefault(name, "0"); if (currentValue instanceof Number) { if (!(currentValue instanceof Short) && !(currentValue instanceof Byte)) { throw new MessageFormatRuntimeException("unsupported conversion"); } else { return ((Number) currentValue).shortValue(); } } return Utils.runtimeException(() -> Short.parseShort(currentValue.toString())); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code int}. * * @param name the name of the property * @return the property value, converted to a {@code int} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, int) */ @Override public int getIntProperty(String name) { if (!properties.containsKey(name)) { throw new NumberFormatException(); } final Object currentValue = properties.getOrDefault(name, "0"); if (currentValue instanceof Number) { if (!(currentValue instanceof Short) && !(currentValue instanceof Integer) && !(currentValue instanceof Byte)) { throw new MessageFormatRuntimeException("unsupported conversion"); } else { return ((Number) currentValue).intValue(); } } return Utils.runtimeException(() -> Integer.parseInt(currentValue.toString())); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code long}. * * @param name the name of the property * @return the property value, converted to a {@code long} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, long) */ @Override public long getLongProperty(String name) { if (!properties.containsKey(name)) { throw new NumberFormatException(); } final Object currentValue = properties.getOrDefault(name, "0"); if (currentValue instanceof Number) { if (!(currentValue instanceof Short) && !(currentValue instanceof Integer) && !(currentValue instanceof Byte) && !(currentValue instanceof Long)) { throw new MessageFormatRuntimeException("unsupported conversion"); } else { return ((Number) currentValue).longValue(); } } return Utils.runtimeException(() -> Long.parseLong(currentValue.toString())); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code float}. * * @param name the name of the property * @return the property value, converted to a {@code float} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, float) */ @Override public float getFloatProperty(String name) { if (!properties.containsKey(name)) { throw new NullPointerException(); } final Object currentValue = properties.getOrDefault(name, "0"); if (currentValue instanceof Number) { if (!(currentValue instanceof Float)) { throw new MessageFormatRuntimeException("unsupported conversion"); } else { return (Float) currentValue; } } return Utils.runtimeException(() -> Float.parseFloat(currentValue.toString())); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code double}. * * @param name the name of the property * @return the property value, converted to a {@code double} * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, double) */ @Override public double getDoubleProperty(String name) { if (!properties.containsKey(name)) { throw new NullPointerException(); } final Object currentValue = properties.getOrDefault(name, "0"); if (currentValue instanceof Number) { if (!(currentValue instanceof Double) && !(currentValue instanceof Float)) { throw new MessageFormatRuntimeException("unsupported conversion"); } else { return ((Number) currentValue).doubleValue(); } } return Utils.runtimeException(() -> Double.parseDouble(currentValue.toString())); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to a {@code String}. * * @param name the name of the property * @return the property value, converted to a {@code boolean}; if there is no property by this * name, a null value is returned * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @throws MessageFormatRuntimeException if this type conversion is invalid. * @see JMSProducer#setProperty(String, String) */ @Override public String getStringProperty(String name) { return Utils.runtimeException(() -> properties.getOrDefault(name, "").toString()); } /** * Returns the message property with the specified name that has been set on this {@code * JMSProducer}, converted to objectified format. * *

This method can be used to return, in objectified format, an object that has been stored as * a property in the message with the equivalent {@code setObjectProperty} method call, or its * equivalent primitive settypeProperty method. * * @param name the name of the property * @return the Java object property value with the specified name, in objectified format (for * example, if the property was set as an {@code int}, an {@code Integer} is returned); if * there is no property by this name, a null value is returned * @throws JMSRuntimeException if the JMS provider fails to get the property value due to some * internal error. * @see JMSProducer#setProperty(String, Object) */ @Override public Object getObjectProperty(String name) { return Utils.runtimeException(() -> properties.getOrDefault(name, null)); } /** * Returns an unmodifiable {@code Set} view of the names of all the message properties that have * been set on this JMSProducer. * *

Note that JMS standard header fields are not considered properties and are not returned in * this Set. * *

The set is backed by the {@code JMSProducer}, so changes to the map are reflected in the * set. However the set may not be modified. Attempts to modify the returned collection, whether * directly or via its iterator, will result in an {@code * java.lang.UnsupportedOperationException}. Its behaviour matches that defined in the {@code * java.util.Collections} method {@code unmodifiableSet}. * * @return a {@code Set} containing the names of all the message properties that have been set on * this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to get the property names due to some * internal error. * @see Collections#unmodifiableSet */ @Override public Set getPropertyNames() { return properties.keySet(); } /** * Specifies that messages sent using this {@code JMSProducer} will have their {@code * JMSCorrelationID} header value set to the specified correlation ID, where correlation ID is * specified as an array of bytes. * *

This will override any {@code JMSCorrelationID} header value that is already set on the * message being sent. * *

The array is copied before the method returns, so future modifications to the array will not * alter the value in this {@code JMSProducer}. * *

If a provider supports the native concept of correlation ID, a JMS client may need to assign * specific {@code JMSCorrelationID} values to match those expected by native messaging clients. * JMS providers without native correlation ID values are not required to support this method and * its corresponding get method; their implementation may throw a {@code * java.lang.UnsupportedOperationException}. * *

The use of a {@code byte[]} value for {@code JMSCorrelationID} is non-portable. * * @param correlationID the correlation ID value as an array of bytes * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the correlation ID due to some * internal error. * @see JMSProducer#setJMSCorrelationID(String) * @see JMSProducer#getJMSCorrelationID() * @see JMSProducer#getJMSCorrelationIDAsBytes() */ @Override @SuppressFBWarnings("EI_EXPOSE_REP2") public JMSProducer setJMSCorrelationIDAsBytes(byte[] correlationID) { this.correlationID = correlationID; return this; } /** * Returns the {@code JMSCorrelationID} header value that has been set on this {@code * JMSProducer}, as an array of bytes. * *

The use of a {@code byte[]} value for {@code JMSCorrelationID} is non-portable. * * @return the correlation ID as an array of bytes * @throws JMSRuntimeException if the JMS provider fails to get the correlation ID due to some * internal error. * @see JMSProducer#setJMSCorrelationID(String) * @see JMSProducer#getJMSCorrelationID() * @see JMSProducer#setJMSCorrelationIDAsBytes(byte[]) */ @Override @SuppressFBWarnings("EI_EXPOSE_REP") public byte[] getJMSCorrelationIDAsBytes() { return correlationID; } /** * Specifies that messages sent using this {@code JMSProducer} will have their {@code * JMSCorrelationID} header value set to the specified correlation ID, where correlation ID is * specified as a {@code String}. * *

This will override any {@code JMSCorrelationID} header value that is already set on the * message being sent. * *

A client can use the {@code JMSCorrelationID} header field to link one message with another. * A typical use is to link a response message with its request message. * *

{@code JMSCorrelationID} can hold one of the following: * *

    *
  • A provider-specific message ID *
  • An application-specific {@code String} *
  • A provider-native {@code byte[]} value *
* *

Since each message sent by a JMS provider is assigned a message ID value, it is convenient * to link messages via message ID. All message ID values must start with the {@code 'ID:'} * prefix. * *

In some cases, an application (made up of several clients) needs to use an * application-specific value for linking messages. For instance, an application may use {@code * JMSCorrelationID} to hold a value referencing some external information. Application-specified * values must not start with the {@code 'ID:'} prefix; this is reserved for provider-generated * message ID values. * *

If a provider supports the native concept of correlation ID, a JMS client may need to assign * specific {@code JMSCorrelationID} values to match those expected by clients that do not use the * JMS API. A {@code byte[]} value is used for this purpose. JMS providers without native * correlation ID values are not required to support {@code byte[]} values. The use of a {@code * byte[]} value for {@code JMSCorrelationID} is non-portable. * * @param correlationID the message ID of a message being referred to * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the correlation ID due to some * internal error. * @see JMSProducer#getJMSCorrelationID() * @see JMSProducer#getJMSCorrelationIDAsBytes() * @see JMSProducer#setJMSCorrelationIDAsBytes(byte[]) */ @Override public JMSProducer setJMSCorrelationID(String correlationID) { this.correlationID = correlationID.getBytes(StandardCharsets.UTF_8); return this; } /** * Returns the {@code JMSCorrelationID} header value that has been set on this {@code * JMSProducer}, as a {@code String}. * *

This method is used to return correlation ID values that are either provider-specific * message IDs or application-specific {@code String} values. * * @return the correlation ID of a message as a {@code String} * @throws JMSRuntimeException if the JMS provider fails to get the correlation ID due to some * internal error. * @see JMSProducer#setJMSCorrelationID(String) * @see JMSProducer#getJMSCorrelationIDAsBytes() * @see JMSProducer#setJMSCorrelationIDAsBytes(byte[]) */ @Override public String getJMSCorrelationID() { return correlationID == null ? null : new String(correlationID, StandardCharsets.UTF_8); } /** * Specifies that messages sent using this {@code JMSProducer} will have their {@code JMSType} * header value set to the specified message type. * *

This will override any {@code JMSType} header value that is already set on the message being * sent. * *

Some JMS providers use a message repository that contains the definitions of messages sent * by applications. The {@code JMSType} header field may reference a message's definition in the * provider's repository. * *

The JMS API does not define a standard message definition repository, nor does it define a * naming policy for the definitions it contains. * *

Some messaging systems require that a message type definition for each application message * be created and that each message specify its type. In order to work with such JMS providers, * JMS clients should assign a value to {@code JMSType}, whether the application makes use of it * or not. This ensures that the field is properly set for those providers that require it. * *

To ensure portability, JMS clients should use symbolic values for {@code JMSType} that can * be configured at installation time to the values defined in the current provider's message * repository. If string literals are used, they may not be valid type names for some JMS * providers. * * @param type the message type * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the message type due to some * internal error. * @see JMSProducer#getJMSType() */ @Override public JMSProducer setJMSType(String type) { this.jmsType = type; return this; } /** * Returns the {@code JMSType} header value that has been set on this {@code JMSProducer}. * * @return the message type * @throws JMSRuntimeException if the JMS provider fails to get the message type due to some * internal error. * @see JMSProducer#setJMSType(String) */ @Override public String getJMSType() { return jmsType; } /** * Specifies that messages sent using this {@code JMSProducer} will have their {@code JMSReplyTo} * header value set to the specified {@code Destination} object. * *

This will override any {@code JMSReplyTo} header value that is already set on the message * being sent. * *

The {@code JMSReplyTo} header field contains the destination where a reply to the current * message should be sent. If it is null, no reply is expected. The destination may be either a * {@code Queue} object or a {@code Topic} object. * *

Messages sent with a null {@code JMSReplyTo} value may be a notification of some event, or * they may just be some data the sender thinks is of interest. * *

Messages with a {@code JMSReplyTo} value typically expect a response. A response is * optional; it is up to the client to decide. These messages are called requests. A message sent * in response to a request is called a reply. * *

In some cases a client may wish to match a request it sent earlier with a reply it has just * received. The client can use the {@code JMSCorrelationID} header field for this purpose. * * @param replyTo {@code Destination} to which to send a response to this message * @return this {@code JMSProducer} * @throws JMSRuntimeException if the JMS provider fails to set the {@code JMSReplyTo} destination * due to some internal error. * @see JMSProducer#getJMSReplyTo() */ @Override public JMSProducer setJMSReplyTo(Destination replyTo) { this.jmsReplyTo = replyTo; return this; } /** * Returns the {@code JMSReplyTo} header value that has been set on this {@code JMSProducer}. * * @return {@code Destination} the {@code JMSReplyTo} header value * @throws JMSRuntimeException if the JMS provider fails to get the {@code JMSReplyTo} destination * due to some internal error. * @see JMSProducer#setJMSReplyTo(Destination) */ @Override public Destination getJMSReplyTo() { return jmsReplyTo; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy