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

com.getperka.sea.jms.impl.EventSubscription Maven / Gradle / Ivy

package com.getperka.sea.jms.impl;

/*
 * #%L
 * Simple Event Architecture - JMS Support
 * %%
 * Copyright (C) 2012 Perka 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.
 * #L%
 */

import java.util.concurrent.atomic.AtomicBoolean;

import javax.inject.Inject;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.slf4j.Logger;

import com.getperka.sea.Event;
import com.getperka.sea.EventDispatch;
import com.getperka.sea.inject.EventLogger;
import com.getperka.sea.jms.EventTransport;
import com.getperka.sea.jms.EventTransportException;
import com.getperka.sea.jms.inject.EventSession;

/**
 * Manages a single subscription to a JMS destination.
 */
public class EventSubscription implements MessageListener {

  private MessageProducer defaultMessageProducer;
  private EventDispatch dispatch;
  private EventMetadataMap eventMetadata;
  private Class eventType;
  private boolean honorReplyTo;
  private Logger logger;
  private MessageConsumer messageConsumer;
  private Destination returnQueue;
  private Session session;
  private final AtomicBoolean shutdown = new AtomicBoolean();
  private EventTransport transport;

  protected EventSubscription() {}

  public void cancel() {
    if (shutdown.getAndSet(true)) {
      return;
    }
    try {
      messageConsumer.close();
    } catch (JMSException e) {
      // Probably already closed
    }
  }

  public void maybeSendToJms(Event event, Object context) {
    if (!eventType.isAssignableFrom(event.getClass())) {
      return;
    }
    // Avoid send-loops
    if (this.equals(context)) {
      return;
    }
    try {
      MessageProducer producer = getTargetProducer(event);
      if (producer != null) {
        sendEvent(producer, event);

        /*
         * If the MessageProducer is a temporary one, close it to avoid the need to wait for the
         * finalizers to run.
         */
        if (producer != defaultMessageProducer) {
          producer.close();
        }
      }
    } catch (JMSException e) {
      logger.error("Unable to send event to JMS service", e);
    }
  }

  @Override
  public void onMessage(Message message) {
    try {
      Event event = transport.decode(eventType, message);
      EventMetadata meta = eventMetadata.get(event);
      try {
        if (message.getJMSReplyTo() != null) {
          meta.setReplyTo(message.getJMSReplyTo());
        }
      } catch (JMSException e) {
        logger.error("Unable to determine reply-to information. " +
          "This event may be lost if re-fired.", e);
      }
      // Pass this as the dispatch context to detect send-loops in maybeSendToJms
      dispatch.fire(event, this);
    } catch (EventTransportException e) {
      logger.error("Unable to dispatch incoming JMS message", e);
    }
  }

  public void subscribe(Class eventType, MessageProducer defaultProducer,
      boolean honorReplyTo) throws JMSException {
    this.defaultMessageProducer = defaultProducer;
    this.eventType = eventType;
    this.honorReplyTo = honorReplyTo;

    returnQueue = session.createTemporaryQueue();
    messageConsumer = session.createConsumer(returnQueue, null, false);
    messageConsumer.setMessageListener(this);
  }

  @Inject
  void inject(EventDispatch dispatch, EventMetadataMap eventMetadataMap,
      @EventLogger Logger logger, @EventSession Session session,
      EventTransport transport) {
    this.dispatch = dispatch;
    this.eventMetadata = eventMetadataMap;
    this.logger = logger;
    this.session = session;
    this.transport = transport;
  }

  /**
   * Returns the {@link MessageProducer} that the event should be sent to or {@code null} if the
   * Event cannot or should not be sent.
   */
  private MessageProducer getTargetProducer(Event event) throws JMSException {
    // Prevent event instances dispatched from the subscriber from being immediately re-broadcast
    EventMetadata meta = eventMetadata.get(event);

    // Events being re-fired may need to be routed to specific locations
    if (honorReplyTo) {
      Destination dest = meta.getReplyTo();
      if (dest != null) {
        return session.createProducer(dest);
      }
    }

    // Use the event type's default queue, if it is registered anywhere.
    // TODO: Allow subscribing to entire type hierarchies?
    return defaultMessageProducer;
  }

  private void sendEvent(MessageProducer producer, Event event) {
    Throwable ex;
    try {
      Message message = transport.encode(event);
      message.setJMSReplyTo(returnQueue);
      producer.send(message);
      return;
    } catch (JMSException e) {
      ex = e;
    } catch (EventTransportException e) {
      ex = e;
    }
    logger.error("Unable to send Event via JMS message", ex);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy