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