package com.google.pubsub.jms.light; import com.google.api.gax.core.RpcFuture; import com.google.api.gax.core.RpcFutureCallback; import com.google.cloud.pubsub.spi.v1.Publisher; import com.google.protobuf.ByteString; import com.google.pubsub.v1.PubsubMessage; import com.google.pubsub.v1.TopicName; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.jms.CompletionListener; import javax.jms.Destination; import javax.jms.IllegalStateException; import javax.jms.InvalidDestinationException; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import javax.jms.Topic; /** * Default PubSub {@link javax.jms.MessageProducer} implementation. * * @author Maksym Prokhorenko */ public class PubSubMessageProducer extends AbstractMessageProducer { private static final Logger LOGGER = Logger.getLogger(PubSubMessageProducer.class.getName()); private Publisher publisher; /** * Default PubSub's message producer constructor. * @param session is a jms session. * @param destination is a jms destination. * @throws JMSException in case {@link PubSubMessageProducer} doesn't support destination * or destination object fails to return topic/queue name. */ public PubSubMessageProducer( final Session session, final Destination destination) throws JMSException { super(session, destination); publisher = createPublisher(destination); } @Override public void send( final Destination destination, final Message message, final int deliveryMode, final int priority, final long timeToLive, final CompletionListener completionListener) throws JMSException { if (isClosed()) { throw new IllegalStateException("Producer has been closed."); } if (!getDestination().equals(destination)) { throw new IllegalArgumentException("Destination [" + destination + "] is invalid. Expected [" + getDestination() + "]."); } final RpcFuture<String> messageIdFuture = publisher.publish( PubsubMessage.newBuilder() .setData(ByteString.copyFromUtf8(message.getBody(String.class))) .build()); messageIdFuture.addCallback( new RpcFutureCallback<String>() { @Override public void onSuccess(final String messageId) { LOGGER.fine(String.format("%s has been sent successfully.", messageId)); if (null != completionListener) { completionListener.onCompletion(message); } } @Override public void onFailure(final Throwable thrown) { LOGGER.log(Level.SEVERE, "Message sending error:", thrown); if (null != completionListener) { completionListener.onException(message, (Exception) thrown); } } }); } protected Publisher createPublisher(final Destination destination) throws JMSException { final Publisher result; if (destination instanceof Topic) { result = createPublisher((Topic) destination); } else { throw new InvalidDestinationException("Unsupported destination."); } return result; } protected Publisher createPublisher(final Topic topic) throws JMSException { final PubSubConnection connection = ((PubSubSession) getSession()).getConnection(); try { return Publisher .newBuilder(TopicName.parse(topic.getTopicName())) .setChannelProvider(connection.getProviderManager()) .setExecutorProvider(connection.getProviderManager()) .setFlowControlSettings(connection.getFlowControlSettings()) .setRetrySettings(connection.getRetrySettings()) .build(); } catch (final IOException e) { LOGGER.log(Level.SEVERE, "Can't create publisher.", e); throw new JMSException(e.getMessage()); } } @SuppressWarnings("PMD.AvoidCatchingGenericException") @Override public synchronized void close() throws JMSException { super.close(); try { publisher.shutdown(); } catch (final Exception e) { LOGGER.log(Level.SEVERE, "Can't close message producer.", e); throw new JMSException(e.getMessage()); } } }