Package org.jbpm.enterprise.internal.ejb

Source Code of org.jbpm.enterprise.internal.ejb.CommandReceiverMDB

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.enterprise.internal.ejb;

import java.io.Serializable;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;

import org.jbpm.api.job.Job;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.cmd.Command;
import org.jbpm.pvm.internal.cmd.ExecuteJobCmd;

/**
* Message driven command receiver.
*
* <h3>Configuration</h3>
*
* <p>
* The command receiver listens for messages on the destination established
* during deployment. Upon delivery, this bean extracts a
* {@linkplain Command command} from the message according to the format
* described in the {@link #onMessage(Message) onMessage} method.
*
* <p>
* If the message does not match the expected format, it is forwarded to the
* message destination referenced by <code>jms/DeadLetterQueue</code>. No
* further processing is done on the message.
* </p>
*
* <p>
* After extraction, the receiver dispatches the command to the
* {@linkplain LocalCommandExecutor local command executor} specified by the EJB
* reference <code>ejb/LocalCommandExecutor</code>.
* </p>
*
* <p>
* In case the incoming message includes a <code>replyTo</code> destination,
* the result of the command execution is wrapped into a message and sent there.
* The resource manager connection factory reference
* <code>jms/CommandConnectionFactory</code> specifies the factory used to
* obtain JMS connections.
* </p>
*
* @author Jim Rigsbee
* @author Tom Baeyens
* @author Alejandro Guizar
*/
public class CommandReceiverMDB implements MessageDrivenBean, MessageListener {

  private MessageDrivenContext messageDrivenContext;
  private LocalCommandExecutor commandExecutor;
  private Connection jmsConnection;
  private Destination deadLetterQueue;

  private static final long serialVersionUID = 1L;

  private static final Log log = Log.getLog(CommandReceiverMDB.class.getName());

  /**
   * Processes a command message.
   *
   * <h3>Message format</h3>
   *
   * <p>
   * The incoming messages must conform to one of the following formats.
   * </p>
   *
   * <ul>
   * <li><strong>Command object</strong>. The body of the message is a Java
   * {@linkplain ObjectMessage object} that implements the {@link Command}
   * interface. The header properties of the message, if any, are ignored.</li>
   * <li><strong>Job reference</strong>. The message contains a header
   * property called <code>jobId</code> of type <code>long</code> which
   * references a saved {@linkplain Job job} that is outstanding for execution.
   * The body of the message, if any, is ignored.</li>
   * </ul>
   */
  public void onMessage(Message message) {
    try {
      // extract command from message
      Command<?> command = extractCommand(message);
      if (command == null) {
        discard(message);
        return;
      }
      // execute command via local command executor bean
      Object result = commandExecutor.execute(command);
      // send a response back if a "reply to" destination is set
      Destination replyTo = message.getJMSReplyTo();
      if (replyTo != null && (result instanceof Serializable || result == null)) {
        sendResult((Serializable) result, replyTo, message.getJMSMessageID());
      }
    }
    catch (JMSException e) {
      throw new EJBException("could not process message " + message, e);
    }
  }

  private static Command<?> extractCommand(Message message) throws JMSException {
    Command<?> command = null;
    // messages can contain serialized commands
    if (message instanceof ObjectMessage) {
      ObjectMessage objectMessage = (ObjectMessage) message;
      Serializable body = objectMessage.getObject();
      log.debug("received object message with body " + body);
      if (body instanceof Command) {
        command = (Command<?>) body;
      }
      else {
        log.info("WARNING: ignoring object message, body is not a command: " + message);
      }
    }
    // messages can also contain a jobId property
    else if (message.getObjectProperty("jobId") != null) {
      String jobId = message.getStringProperty("jobId");
      log.debug("received message with job id " + jobId);
      // create a command with the given jobId
      command = new ExecuteJobCmd(jobId);
    }
    else {
      log.info("WARNING: ignoring message, unknown format: " + message);
    }
    return command;
  }

  private void discard(Message message) throws JMSException {
    // if a dead letter queue is configured, send the message there
    if (deadLetterQueue != null) {
      Session jmsSession = createSession();
      try {
        jmsSession.createProducer(deadLetterQueue).send(message);
      }
      finally {
        jmsSession.close();
      }
    }
    else {
      // reject the message
      messageDrivenContext.setRollbackOnly();
    }
  }

  private Session createSession() throws JMSException {
    /*
     * if the connection supports xa, the session will be transacted, else the
     * session will auto acknowledge; in either case no explicit transaction
     * control must be performed - see ejb 2.1 section 17.3.5
     */
    return jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  }

  private void sendResult(Serializable result, Destination destination, String correlationId)
      throws JMSException {
    log.debug("sending result " + result + " to " + destination);
    Session jmsSession = createSession();
    try {
      Message resultMessage = jmsSession.createObjectMessage(result);
      resultMessage.setJMSCorrelationID(correlationId);
      jmsSession.createProducer(destination).send(resultMessage);
    }
    finally {
      jmsSession.close();
    }
  }

  public void setMessageDrivenContext(MessageDrivenContext messageDrivenContext) {
    this.messageDrivenContext = messageDrivenContext;
  }

  public void ejbCreate() {
    try {
      Context context = new InitialContext();
      LocalCommandExecutorHome commandExecutorHome = (LocalCommandExecutorHome) context.lookup("java:comp/env/ejb/LocalCommandExecutor");
      ConnectionFactory jmsConnectionFactory = (ConnectionFactory) context.lookup("java:comp/env/jms/JbpmConnectionFactory");
      try {
        deadLetterQueue = (Destination) context.lookup("java:comp/env/jms/DeadLetterQueue");
      }
      catch (NameNotFoundException e) {
        log.info("dead letter queue not specified, messages with unknown formats will be rejected");
      }
      context.close();

      commandExecutor = commandExecutorHome.create();
      jmsConnection = jmsConnectionFactory.createConnection();
    }
    catch (NamingException e) {
      throw new EJBException("error retrieving managed objects", e);
    }
    catch (CreateException e) {
      throw new EJBException("error creating command executor", e);
    }
    catch (JMSException e) {
      throw new EJBException("error creating jms connection", e);
    }
  }

  public void ejbRemove() {
    if (jmsConnection != null) {
      try {
        jmsConnection.close();
      }
      catch (JMSException e) {
        log.debug("could not close jms connection", e);
      }
      jmsConnection = null;
    }
    commandExecutor = null;
    messageDrivenContext = null;
  }
}
TOP

Related Classes of org.jbpm.enterprise.internal.ejb.CommandReceiverMDB

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.