View Javadoc

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