View Javadoc

1   /*
2    * $Id: AbstractJmsTransformer.java 19250 2010-08-30 16:53:14Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.jms.transformers;
12  
13  import org.mule.api.MuleMessage;
14  import org.mule.api.config.MuleProperties;
15  import org.mule.api.transaction.Transaction;
16  import org.mule.api.transformer.DiscoverableTransformer;
17  import org.mule.api.transformer.TransformerException;
18  import org.mule.api.transport.Connector;
19  import org.mule.transaction.TransactionCoordination;
20  import org.mule.transformer.AbstractMessageTransformer;
21  import org.mule.transport.jms.JmsConnector;
22  import org.mule.transport.jms.JmsConstants;
23  import org.mule.transport.jms.JmsMessageUtils;
24  import org.mule.util.ClassUtils;
25  
26  import java.io.IOException;
27  
28  import javax.jms.Destination;
29  import javax.jms.JMSException;
30  import javax.jms.Message;
31  import javax.jms.Session;
32  
33  /**
34   * <code>AbstractJmsTransformer</code> is an abstract class that should be used for
35   * all transformers where a JMS message will be the transformed or transformee
36   * object. It provides services for compressing and uncompressing messages.
37   */
38  
39  public abstract class AbstractJmsTransformer extends AbstractMessageTransformer implements DiscoverableTransformer
40  {
41      private int priorityWeighting = DiscoverableTransformer.DEFAULT_PRIORITY_WEIGHTING;
42  
43      public AbstractJmsTransformer()
44      {
45          super();
46          declareInputOutputClasses();
47      }
48  
49      protected abstract void declareInputOutputClasses();
50      
51      protected Message transformToMessage(MuleMessage message) throws TransformerException
52      {
53          Session session = null;
54          try
55          {
56              Message result;
57  
58              Object src = message.getPayload();
59              if (src instanceof Message)
60              {
61                  result = (Message) src;
62                  result.clearProperties();
63              }
64              else
65              {
66                  session = this.getSession();
67                  result = JmsMessageUtils.toMessage(src, session);
68              }
69              this.setJmsProperties(message, result);
70  
71              return result;
72          }
73          catch (Exception e)
74          {
75              throw new TransformerException(this, e);
76          }
77          finally
78          {
79              /*
80                  session.getTransacted() would be easier in most cases, but e.g. in Weblogic 8.x
81                  Java EE apps there could be some quirks, see http://forums.bea.com/thread.jspa?threadID=200007643
82                  to get a picture.
83  
84                  Though JmsTransaction has this session.getTransacted() validation already, we're taking extra precautions
85                  to cover XA cases and potentially to make up for a configuration error. E.g. omitting transaction
86                  configuration from an outbound endpoint or router. Note, XA support in Mule will deliberately
87                  fail with fanfares to signal this case, which is really a user error.
88                */
89  
90              if (session != null && endpoint != null) // endpoint can be null in some programmatic tests only in fact
91              {
92                  Transaction muleTx = TransactionCoordination.getInstance().getTransaction();
93  
94                  final JmsConnector connector = (JmsConnector) endpoint.getConnector();
95                  if (muleTx == null)
96                  {
97                      if (logger.isDebugEnabled())
98                      {
99                          logger.debug("Closing non-transacted jms session: " + session);
100                     }
101                     connector.closeQuietly(session);
102                 }
103                 else if (!muleTx.hasResource(connector.getConnection()))
104                 {
105                     // this is some other session from another connection, don't let it leak
106                     if (logger.isDebugEnabled())
107                     {
108                         logger.debug("Closing an orphaned, but transacted jms session: " + session +
109                                 ", transaction: " + muleTx);
110                     }
111                     connector.closeQuietly(session);
112                 }
113             }
114             // aggressively killing any session refs
115             session = null;
116         }
117     }
118 
119     protected Object transformFromMessage(Message source, String outputEncoding) throws IOException, JMSException
120     {
121         if (logger.isDebugEnabled())
122         {
123             logger.debug("Message type received is: " +
124                     ClassUtils.getSimpleName(source.getClass()));
125         }
126 
127         // Try to figure out our endpoint's JMS Specification and fall back to
128         // 1.0.2 if none is set.
129         String jmsSpec = JmsConstants.JMS_SPECIFICATION_102B;
130         if (endpoint != null)
131         {
132             Connector connector = endpoint.getConnector();
133             if (connector instanceof JmsConnector)
134             {
135                 jmsSpec = ((JmsConnector) connector).getSpecification();
136             }
137         }
138 
139         return JmsMessageUtils.toObject(source, jmsSpec, outputEncoding);
140     }
141 
142     public void setJmsProperties(MuleMessage message, Message msg) throws JMSException
143     {
144         for (String key : message.getOutboundPropertyNames())
145         {
146             if (JmsConstants.JMS_PROPERTY_NAMES.contains(key))
147             {
148                 continue;
149             }
150 
151             Object value = message.getOutboundProperty(key);
152 
153             if (MuleProperties.MULE_CORRELATION_ID_PROPERTY.equals(key))
154             {
155                 msg.setJMSCorrelationID(message.getCorrelationId());
156             }
157 
158             // We don't want to set the ReplyTo property again as it will be set
159             // using JMSReplyTo
160             if (!(MuleProperties.MULE_REPLY_TO_PROPERTY.equals(key) && value instanceof Destination))
161             {
162                 // sanitize key as JMS header
163                 key = JmsMessageUtils.encodeHeader(key);
164 
165                 try
166                 {
167                     msg.setObjectProperty(key, value);
168                 }
169                 catch (JMSException e)
170                 {
171                     // Various JMS servers have slightly different rules to what
172                     // can be set as an object property on the message; therefore
173                     // we have to take a hit n' hope approach
174                     if (logger.isDebugEnabled())
175                     {
176                         logger.debug("Unable to set property '" + key + "' of type "
177                                      + ClassUtils.getSimpleName(value.getClass())
178                                      + "': " + e.getMessage());
179                     }
180                 }
181             }
182         }
183     }
184 
185     protected Session getSession() throws JMSException
186     {
187         if (endpoint != null)
188         {
189             return ((JmsConnector) endpoint.getConnector()).getSession(endpoint);
190         }
191         else
192         {
193             throw new IllegalStateException("This transformer needs a valid endpoint");
194         }
195     }
196 
197     public int getPriorityWeighting()
198     {
199         return priorityWeighting;
200     }
201 
202     public void setPriorityWeighting(int priorityWeighting)
203     {
204         this.priorityWeighting = priorityWeighting;
205     }
206 }