/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.transport.jms;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.HttpHeaderHelper;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.transport.jms.uri.JMSEndpoint;
import org.apache.cxf.transport.jms.util.JMSMessageConverter;
import org.apache.cxf.transport.jms.util.JMSUtil;
/**
* Static util methods for converting cxf to jms messages and vice a versa
*/
final class JMSMessageUtils {
private static final Logger LOG = LogUtils.getL7dLogger(JMSMessageUtils.class);
private JMSMessageUtils() {
}
public static org.apache.cxf.message.Message asCXFMessage(Message message, String jmsHeadersKey)
throws UnsupportedEncodingException, JMSException {
org.apache.cxf.message.Message inMessage = new MessageImpl();
JMSMessageHeadersType messageHeaders = JMSMessageHeadersType.from(message);
inMessage.put(jmsHeadersKey, messageHeaders);
populateIncomingContext(messageHeaders, inMessage);
retrieveAndSetPayload(inMessage, message);
return inMessage;
}
/**
* Extract the payload of an incoming JMS message
*
* @param inMessage
* @param message the incoming message
* @throws UnsupportedEncodingException
* @throws JMSException
*/
private static void retrieveAndSetPayload(org.apache.cxf.message.Message inMessage, Message message)
throws UnsupportedEncodingException, JMSException {
String messageType = null;
Object converted = new JMSMessageConverter().fromMessage(message);
if (converted instanceof String) {
inMessage.setContent(Reader.class, new StringReader((String)converted));
messageType = JMSConstants.TEXT_MESSAGE_TYPE;
} else if (converted instanceof byte[]) {
inMessage.setContent(InputStream.class, new ByteArrayInputStream((byte[])converted));
messageType = JMSConstants.BYTE_MESSAGE_TYPE;
} else {
messageType = "unknown";
}
Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)inMessage
.get(org.apache.cxf.message.Message.PROTOCOL_HEADERS));
headers.put(JMSConstants.JMS_MESSAGE_TYPE, Collections.singletonList(messageType));
}
private static void populateIncomingContext(JMSMessageHeadersType messageHeaders,
org.apache.cxf.message.Message inMessage)
throws UnsupportedEncodingException {
String contentType = messageHeaders.getContentType();
if (contentType != null) {
inMessage.put(org.apache.cxf.message.Message.CONTENT_TYPE, contentType);
inMessage.put(org.apache.cxf.message.Message.ENCODING, getEncoding(contentType));
}
String responseCode = (String)messageHeaders.getProperty(org.apache.cxf.message.Message.RESPONSE_CODE);
if (responseCode != null) {
inMessage.put(org.apache.cxf.message.Message.RESPONSE_CODE, Integer.valueOf(responseCode));
}
Map<String, List<String>> protHeaders = new TreeMap<String, List<String>>();
for (String name : messageHeaders.getPropertyKeys()) {
String val = (String)messageHeaders.getProperty(name);
protHeaders.put(name, Collections.singletonList(val));
}
String requestURI = messageHeaders.getSOAPJMSRequestURI();
if (requestURI != null) {
try {
JMSEndpoint endpoint = new JMSEndpoint(requestURI);
if (endpoint.getTargetService() != null) {
protHeaders.put(JMSConstants.TARGET_SERVICE_IN_REQUESTURI,
Collections.singletonList("true"));
}
if (requestURI != null) {
inMessage.put(org.apache.cxf.message.Message.REQUEST_URI, requestURI);
}
} catch (Exception e) {
protHeaders.put(JMSConstants.MALFORMED_REQUESTURI, Collections.singletonList("true"));
}
}
inMessage.put(org.apache.cxf.message.Message.PROTOCOL_HEADERS, protHeaders);
}
static String getEncoding(String ct) throws UnsupportedEncodingException {
String contentType = ct.toLowerCase();
String enc = null;
String[] tokens = StringUtils.split(contentType, ";");
for (String token : tokens) {
int index = token.indexOf("charset=");
if (index >= 0) {
enc = token.substring(index + 8);
break;
}
}
String normalizedEncoding = HttpHeaderHelper.mapCharset(enc, StandardCharsets.UTF_8.name());
if (normalizedEncoding == null) {
String m = new org.apache.cxf.common.i18n.Message("INVALID_ENCODING_MSG", LOG, new Object[] {
enc
}).toString();
LOG.log(Level.WARNING, m);
throw new UnsupportedEncodingException(m);
}
return normalizedEncoding;
}
private static String getContentType(org.apache.cxf.message.Message message) {
String contentType = (String)message.get(org.apache.cxf.message.Message.CONTENT_TYPE);
String enc = (String)message.get(org.apache.cxf.message.Message.ENCODING);
// add the encoding information
if (null != contentType) {
if (enc != null && contentType.indexOf("charset=") == -1
&& !contentType.toLowerCase().contains("multipart/related")) {
contentType = contentType + "; charset=" + enc;
}
} else if (enc != null) {
contentType = "text/xml; charset=" + enc;
} else {
contentType = "text/xml";
}
// Retrieve or create protocol headers
Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)message
.get(org.apache.cxf.message.Message.PROTOCOL_HEADERS));
if (null == headers) {
headers = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
message.put(org.apache.cxf.message.Message.PROTOCOL_HEADERS, headers);
}
return contentType;
}
private static String getContentEncoding(org.apache.cxf.message.Message message) {
Map<String, List<String>> headers
= CastUtils.cast((Map<?, ?>)message.get(org.apache.cxf.message.Message.PROTOCOL_HEADERS));
if (headers != null) {
List<String> l = headers.get("Content-Encoding");
if (l != null && !l.isEmpty()) {
return l.get(0);
}
}
return null;
}
public static Message asJMSMessage(JMSConfiguration jmsConfig,
org.apache.cxf.message.Message outMessage,
Object payload,
String messageType,
Session session,
String correlationId,
String headerType)
throws JMSException {
Message jmsMessage = JMSUtil.createAndSetPayload(payload, session, messageType);
JMSMessageHeadersType messageHeaders = getOrCreateHeader(outMessage, headerType);
if (!messageHeaders.isSetJMSDeliveryMode()) {
messageHeaders.setJMSDeliveryMode(jmsConfig.getDeliveryMode());
}
if (!messageHeaders.isSetTimeToLive()) {
messageHeaders.setTimeToLive(jmsConfig.getTimeToLive());
}
if (!messageHeaders.isSetJMSPriority()) {
messageHeaders.setJMSPriority(jmsConfig.getPriority());
}
// Retrieve or create protocol headers
Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)outMessage
.get(org.apache.cxf.message.Message.PROTOCOL_HEADERS));
boolean isSoapMessage =
!MessageUtils.isTrue(outMessage.getExchange().get(org.apache.cxf.message.Message.REST_MESSAGE));
if (isSoapMessage) {
if (!messageHeaders.isSetSOAPJMSTargetService()) {
messageHeaders.setSOAPJMSTargetService(jmsConfig.getTargetService());
}
if (!messageHeaders.isSetSOAPJMSBindingVersion()) {
messageHeaders.setSOAPJMSBindingVersion("1.0");
}
messageHeaders.setSOAPJMSContentType(JMSMessageUtils.getContentType(outMessage));
if (JMSMessageUtils.getContentEncoding(outMessage) != null) {
messageHeaders.setSOAPJMSContentEncoding(JMSMessageUtils.getContentEncoding(outMessage));
}
String soapAction = JMSMessageUtils.getSoapAction(messageHeaders, outMessage, headers);
if (soapAction != null) {
messageHeaders.setSOAPJMSSOAPAction(soapAction);
}
if (!messageHeaders.isSetSOAPJMSIsFault()) {
boolean isFault = outMessage.getContent(Exception.class) != null;
messageHeaders.setSOAPJMSIsFault(isFault);
}
if (!messageHeaders.isSetSOAPJMSRequestURI()) {
messageHeaders.setSOAPJMSRequestURI(jmsConfig.getRequestURI());
}
} else {
if (MessageUtils.isRequestor(outMessage)) {
addJMSPropertiesFromMessage(messageHeaders,
outMessage,
org.apache.cxf.message.Message.HTTP_REQUEST_METHOD,
org.apache.cxf.message.Message.REQUEST_URI,
org.apache.cxf.message.Message.ACCEPT_CONTENT_TYPE);
} else {
addJMSPropertyFromMessage(messageHeaders,
outMessage,
org.apache.cxf.message.Message.RESPONSE_CODE);
}
addJMSPropertyFromMessage(messageHeaders,
outMessage,
JMSConstants.RS_CONTENT_TYPE);
}
if (headers != null) {
for (Map.Entry<String, List<String>> ent : headers.entrySet()) {
messageHeaders.putProperty(ent.getKey(), JMSMessageUtils.join(ent.getValue(), ','));
}
}
messageHeaders.writeTo(jmsMessage);
jmsMessage.setJMSCorrelationID(correlationId);
return jmsMessage;
}
private static JMSMessageHeadersType getOrCreateHeader(org.apache.cxf.message.Message message,
String headerName) {
JMSMessageHeadersType messageProperties = (JMSMessageHeadersType)message
.get(headerName);
if (messageProperties == null) {
messageProperties = new JMSMessageHeadersType();
message.put(headerName, messageProperties);
}
return messageProperties;
}
private static String join(List<String> valueList, char seperator) {
StringBuilder b = new StringBuilder();
for (String s : valueList) {
if (b.length() > 0) {
b.append(seperator);
}
b.append(s);
}
return b.toString();
}
private static String getSoapAction(JMSMessageHeadersType messageProperties,
org.apache.cxf.message.Message outMessage,
Map<String, List<String>> headers) {
String soapAction = null;
if (headers != null) {
List<String> action = headers.remove("SOAPAction");
if (action != null && !action.isEmpty()) {
soapAction = action.get(0);
}
}
if (soapAction == null) {
soapAction = messageProperties.getSOAPJMSSOAPAction();
}
if (soapAction == null) {
soapAction = extractActionFromSoap12(outMessage);
}
return soapAction;
}
private static void addJMSPropertiesFromMessage(JMSMessageHeadersType messageProperties,
org.apache.cxf.message.Message message,
String... keys) {
for (String key : keys) {
addJMSPropertyFromMessage(messageProperties, message, key);
}
}
private static void addJMSPropertyFromMessage(JMSMessageHeadersType messageProperties,
org.apache.cxf.message.Message message,
String key) {
Object value = message.get(key);
if (value != null) {
messageProperties.putProperty(key, value.toString());
}
}
public static String getMessageType(final javax.jms.Message request) {
final String msgType;
if (request instanceof TextMessage) {
msgType = JMSConstants.TEXT_MESSAGE_TYPE;
} else if (request instanceof BytesMessage) {
msgType = JMSConstants.BYTE_MESSAGE_TYPE;
} else {
msgType = JMSConstants.BINARY_MESSAGE_TYPE;
}
return msgType;
}
private static String extractActionFromSoap12(org.apache.cxf.message.Message message) {
String ct = (String) message.get(org.apache.cxf.message.Message.CONTENT_TYPE);
if (ct == null) {
return null;
}
int start = ct.indexOf("action=");
if (start != -1) {
int end;
if (ct.charAt(start + 7) == '\"') {
start += 8;
end = ct.indexOf('\"', start);
} else {
start += 7;
end = ct.indexOf(';', start);
if (end == -1) {
end = ct.length();
}
}
return ct.substring(start, end);
}
return null;
}
public static boolean isMtomEnabled(final org.apache.cxf.message.Message message) {
return MessageUtils.isTrue(message.getContextualProperty(
org.apache.cxf.message.Message.MTOM_ENABLED));
}
}