001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.camel;
018
019 import java.net.URI;
020 import java.net.URISyntaxException;
021
022 import javax.jbi.messaging.InOptionalOut;
023 import javax.jbi.messaging.InOut;
024 import javax.jbi.messaging.MessageExchange;
025 import javax.jbi.messaging.MessageExchangeFactory;
026 import javax.jbi.messaging.MessagingException;
027 import javax.jbi.messaging.NormalizedMessage;
028 import javax.jbi.messaging.RobustInOnly;
029 import javax.security.auth.Subject;
030 import javax.xml.namespace.QName;
031 import javax.xml.transform.Source;
032
033 import org.apache.camel.CamelContext;
034 import org.apache.camel.Exchange;
035 import org.apache.camel.ExchangePattern;
036 import org.apache.camel.Message;
037 import org.apache.camel.NoTypeConversionAvailableException;
038 import org.apache.camel.spi.HeaderFilterStrategy;
039 import org.apache.commons.logging.Log;
040 import org.apache.commons.logging.LogFactory;
041 import org.apache.servicemix.camel.util.BasicSerializationHeaderFilterStrategy;
042 import org.apache.servicemix.camel.util.HeaderFilterStrategies;
043 import org.apache.servicemix.camel.util.StrictSerializationHeaderFilterStrategy;
044 import org.apache.servicemix.jbi.FaultException;
045
046 /**
047 * The binding of how Camel messages get mapped to JBI and back again
048 *
049 * @version $Revision: 563665 $
050 */
051 public class JbiBinding {
052
053 public static final String MESSAGE_EXCHANGE = "JbiMessageExchange";
054 public static final String OPERATION = "JbiOperation";
055 public static final String SECURITY_SUBJECT = "JbiSecuritySubject";
056
057 @Deprecated
058 public static final String OPERATION_STRING = "jbi.operation";
059
060 private static final Log LOG = LogFactory.getLog(JbiBinding.class);
061
062 private final CamelContext context;
063
064 private HeaderFilterStrategies strategies = new HeaderFilterStrategies();
065 private boolean convertExceptions;
066
067 /**
068 * Create the binding instance for a given CamelContext
069 *
070 * @param context the CamelContext
071 */
072 public JbiBinding(CamelContext context) {
073 this(context, false);
074 }
075
076 public JbiBinding(CamelContext context, boolean strictSerialization) {
077 this.context = context;
078 if (strictSerialization) {
079 strategies.add(new StrictSerializationHeaderFilterStrategy());
080 } else {
081 strategies.add(new BasicSerializationHeaderFilterStrategy());
082 }
083 }
084
085 public void addHeaderFilterStrategy(HeaderFilterStrategy strategy) {
086 strategies.add(strategy);
087 }
088
089 public void setConvertExceptions(boolean convertExceptions) {
090 this.convertExceptions = convertExceptions;
091 }
092
093 public MessageExchange makeJbiMessageExchange(Exchange camelExchange,
094 MessageExchangeFactory exchangeFactory,
095 String defaultMep)
096 throws MessagingException, URISyntaxException {
097
098 MessageExchange jbiExchange = createJbiMessageExchange(camelExchange, exchangeFactory, defaultMep);
099 NormalizedMessage normalizedMessage = jbiExchange.getMessage("in");
100 if (normalizedMessage == null) {
101 normalizedMessage = jbiExchange.createMessage();
102 jbiExchange.setMessage(normalizedMessage, "in");
103 }
104 copyFromCamelToJbi(camelExchange.getIn(), normalizedMessage);
105 return jbiExchange;
106 }
107
108 protected MessageExchange createJbiMessageExchange(Exchange camelExchange,
109 MessageExchangeFactory exchangeFactory, String defaultMep)
110 throws MessagingException, URISyntaxException {
111
112 // option 1 -- use the MEP that was configured on the endpoint URI
113 ExchangePattern mep = ExchangePattern.fromWsdlUri(defaultMep);
114 if (mep == null) {
115 // option 2 -- use the MEP from the Camel Exchange
116 mep = camelExchange.getPattern();
117 }
118 MessageExchange answer = null;
119 if (mep != null) {
120 if (mep == ExchangePattern.InOnly) {
121 answer = exchangeFactory.createInOnlyExchange();
122 } else if (mep == ExchangePattern.InOptionalOut) {
123 answer = exchangeFactory.createInOptionalOutExchange();
124 } else if (mep == ExchangePattern.InOut) {
125 answer = exchangeFactory.createInOutExchange();
126 } else if (mep == ExchangePattern.RobustInOnly) {
127 answer = exchangeFactory.createRobustInOnlyExchange();
128 } else {
129 answer = exchangeFactory.createExchange(new URI(mep.toString()));
130 }
131 }
132 // TODO: this is not really usefull as the out will not be
133 // TODO: populated at that time
134 if (answer == null) {
135 // lets try choose the best MEP based on the camel message
136 Message out = camelExchange.getOut(false);
137 if (out == null || out.getBody() == null) {
138 answer = exchangeFactory.createInOnlyExchange();
139 } else {
140 answer = exchangeFactory.createInOutExchange();
141 }
142 }
143
144 if (getOperation(camelExchange) != null) {
145 answer.setOperation(getOperation(camelExchange));
146 }
147
148 // let's try to use the old way too
149 if (answer.getOperation() == null && camelExchange.getProperties().containsKey(JbiBinding.OPERATION_STRING)) {
150 answer.setOperation(QName.valueOf(camelExchange.getProperty(JbiBinding.OPERATION_STRING, String.class)));
151 }
152
153 return answer;
154 }
155
156 public Exchange createExchange(MessageExchange exchange) {
157 Exchange result = new JbiExchange(context, this);
158 result.setProperty(MESSAGE_EXCHANGE, exchange);
159 result.setPattern(getExchangePattern(exchange));
160 if (exchange.getOperation() != null) {
161 result.setProperty(OPERATION, exchange.getOperation());
162 result.setProperty(OPERATION_STRING, exchange.getOperation().toString());
163 }
164 if (exchange.getMessage("in") != null) {
165 copyJbiToCamel(exchange.getMessage("in"), result.getIn());
166 }
167 return result;
168 }
169
170 /*
171 * Get the corresponding Camel ExchangePattern for a given JBI Exchange
172 */
173 private ExchangePattern getExchangePattern(MessageExchange exchange) {
174 if (exchange instanceof InOut) {
175 return ExchangePattern.InOut;
176 } else if (exchange instanceof InOptionalOut) {
177 return ExchangePattern.InOptionalOut;
178 } else if (exchange instanceof RobustInOnly) {
179 return ExchangePattern.RobustInOnly;
180 } else {
181 return ExchangePattern.InOnly;
182 }
183 }
184
185 private void copyJbiToCamel(NormalizedMessage from, Message to) {
186 to.setBody(from.getContent());
187 if (from.getSecuritySubject() != null) {
188 to.setHeader(SECURITY_SUBJECT, from.getSecuritySubject());
189 }
190 for (Object key : from.getPropertyNames()) {
191 to.setHeader(key.toString(), from.getProperty(key.toString()));
192 }
193 for (Object id : from.getAttachmentNames()) {
194 to.addAttachment(id.toString(), from.getAttachment(id.toString()));
195 }
196 }
197
198 public void copyFromCamelToJbi(Message message, NormalizedMessage normalizedMessage) throws MessagingException {
199 try {
200 normalizedMessage.setContent(message.getBody(Source.class));
201 } catch (NoTypeConversionAvailableException e) {
202 LOG.warn("Unable to convert message body of type " + message.getBody().getClass() + " into an XML Source");
203 }
204
205 if (getSecuritySubject(message) != null) {
206 normalizedMessage.setSecuritySubject(getSecuritySubject(message));
207 }
208
209 for (String key : message.getHeaders().keySet()) {
210 Object value = message.getHeader(key);
211 if (!strategies.applyFilterToCamelHeaders(key, value)) {
212 normalizedMessage.setProperty(key, value);
213 }
214 }
215
216 for (String id : message.getAttachmentNames()) {
217 normalizedMessage.addAttachment(id, message.getAttachment(id));
218 }
219 }
220
221 public void copyFromCamelToJbi(Exchange exchange, MessageExchange messageExchange) throws MessagingException {
222 NormalizedMessage in = messageExchange.getMessage("in");
223 for (String key : exchange.getIn().getHeaders().keySet()) {
224 in.setProperty(key, exchange.getIn().getHeader(key));
225 }
226
227 if (isOutCapable(messageExchange)) {
228 if (exchange.getOut(false) == null) {
229 //JBI MEP requires a reply and the Camel exchange failed to produce one -- echoing back the request
230 NormalizedMessage out = messageExchange.createMessage();
231 copyFromCamelToJbi(exchange.getIn(), out);
232 messageExchange.setMessage(out, "out");
233 } else {
234 NormalizedMessage out = messageExchange.createMessage();
235 copyFromCamelToJbi(exchange.getOut(), out);
236 messageExchange.setMessage(out, "out");
237 }
238 }
239 }
240
241
242 /**
243 * Extract an Exception from the exchange. This method will always return a
244 *
245 * @param exchange the Camel exchange
246 * @return an exception
247 */
248 public Exception extractException(Exchange exchange) {
249 Throwable e = exchange.getException();
250 if (!(e instanceof Exception) || convertExceptions) {
251 e = context.getTypeConverter().convertTo(FaultException.class, exchange);
252 }
253 return (Exception) e;
254 }
255
256 private boolean isOutCapable(MessageExchange exchange) {
257 return exchange instanceof InOut || exchange instanceof InOptionalOut;
258 }
259
260 /**
261 * Access the JBI MessageExchange that has been stored on the Camel Exchange
262 * @return the JBI MessageExchange
263 */
264 public static MessageExchange getMessageExchange(Exchange exchange) {
265 return exchange.getProperty(MESSAGE_EXCHANGE, MessageExchange.class);
266 }
267
268 /**
269 * Access the JBI Operation that has been stored on a Camel Exchange
270 * @param exchange the Camel Exchange
271 * @return the JBI Operation as a QName
272 */
273 public static QName getOperation(Exchange exchange) {
274 return exchange.getProperty(OPERATION, QName.class);
275 }
276
277 /**
278 * Access the security subject that has been stored on the Camel Message
279 * @param message the Camel message
280 * @return the Subject or <code>null</code> is no Subject is available in the headers
281 */
282 public static Subject getSecuritySubject(Message message) {
283 if (message.getHeader(SECURITY_SUBJECT) != null) {
284 return message.getHeader(SECURITY_SUBJECT, Subject.class);
285 }
286 return null;
287 }
288 }