/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
// Package
package gov.va.med.fw.service.jms;

// Java classes
import gov.va.med.fw.service.AbstractComponent;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageFormatException;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;

/**
 * Provides a concrete implementation of a MessageProducer that must be injected with
 * with a JMSDescriptor object.  A JMSDescriptor encapsulates specific information about 
 * a JMS Destination, Connection, and Factory in a J2EE application server.   
 * 
 * @author Vu Le
 * @version 1.0
 */
public abstract class AbstractMessageProducerService extends AbstractComponent implements MessageProducer {

   /**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = 2808696406833332275L;
    
    private boolean supportsDistributedTransactions = true;
    
    private int sessionAcknowledgeMode = Session.AUTO_ACKNOWLEDGE;
	
   /** Creates a new instance of JMSService */
   public AbstractMessageProducerService() {
      super();
   }
  
   /** Closes a session that is used to send a message
    * @param session A session used to send a message
    */
   public void close( Session session ) {
	  RuntimeException methodException = null;
      if( session != null ) {
         try {
        	 if(session.getTransacted()) {
        		 session.commit();
        	 }
         }
         catch( JMSException e ) {
        	 logger.error( "Failed to commit JMSSession", e );
        	 methodException = new IllegalStateException("Failed to commit JMSSession");
        	 methodException.initCause(e);
        	 throw methodException;
         } finally {
        	 try {
        		 session.close();
        	 } catch(Exception e2) {
        		 logger.error( "Failed to close JMSSession", e2 );
        		 if(methodException == null) {
                	 methodException = new IllegalStateException("Failed to close JMSSession");
                	 methodException.initCause(e2);
                	 throw methodException;        			 
        		 }
        	 }
         }
      }
   }
   
   /**
    * Creates a JMS message on an input object's class type. 
    * 
    * @param session A session to create a JMS Message
    * @param data A message data
    * @param properties A properties to be included in a message
    * @return A concrete JMS Message of the specific type
    * @throws JMSException Thrown if failed to create a JMS Message
    */
   protected Message createJMSMessageByType( Session session, 
          	 										 Object data, 
           											 Properties properties ) throws JMSException {

       // Create a JMS message based on an input object's class type
       // The rule here is as follow:
       // a. byte[] = BytesMessage
       // b. String = TextMessage
       // c. gov.va.med.fw.hl7.Message = TextMessage
       // d. Map = MapMessage
       // e. Serializable = ObjectMessage
       // f. Anything else = Not supported throw exception
       
       Message jmsMessage = null;
       if( data instanceof byte[] ) {
           jmsMessage = session.createBytesMessage();
           ((BytesMessage)jmsMessage).writeBytes( (byte[])data );
       }
       else if( data instanceof String ) {
           jmsMessage = session.createTextMessage();
           ((TextMessage)jmsMessage).setText( (String)data );
       }
       else if( data instanceof gov.va.med.fw.hl7.Message ) {
           // use a fully-qualified path to avoid collision with a javax.jms.Message
           jmsMessage = session.createTextMessage();
           ((TextMessage)jmsMessage).setText( ((gov.va.med.fw.hl7.Message)data).getMessageData() );
       }
       else if( data instanceof Map ) {
           jmsMessage = session.createMapMessage();
           Map map = (Map)data;
           Set keys = map.keySet();
           for( Iterator i=keys.iterator(); i.hasNext(); ) {
               Object key = i.next();
               ((MapMessage)jmsMessage).setObject( key.toString(), map.get( key ) );
           }
       }
       else if( data instanceof Serializable ){
           jmsMessage = session.createObjectMessage();
           ((ObjectMessage)jmsMessage).setObject( (Serializable)data );
       }
       else {
           throw new MessageFormatException( "Invalid object format.  Object must be serializable" );
       }

       // Put all properties in a message's properties
       for( Enumeration e=properties.keys(); e.hasMoreElements(); ) {
          String key = e.nextElement().toString();
          jmsMessage.setStringProperty( key, properties.getProperty( key ) );
       }
       return jmsMessage;
   }

    public boolean isSupportsDistributedTransactions() {
        return supportsDistributedTransactions;
    }
    
    public void setSupportsDistributedTransactions(
            boolean supportsDistributedTransactions) {
        this.supportsDistributedTransactions = supportsDistributedTransactions;
    }
    
    public int getSessionAcknowledgeMode() {
        return sessionAcknowledgeMode;
    }
    
    public void setSessionAcknowledgeMode(int sessionAcknowledgeMode) {
        this.sessionAcknowledgeMode = sessionAcknowledgeMode;
    }
}