/*
* Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.wso2.carbon.inbound.endpoint.protocol.jms;
import org.apache.axiom.om.OMElement;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.jms.BytesMessage;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.StreamMessage;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
*
* Maintain the common methods used by inbound JMS protocol
*
*/
public class JMSUtils {
private static final Log log = LogFactory.getLog(JMSUtils.class);
public static String inferJMSMessageType(Message msg) {
if(inferTextMessage(msg)) {
return TextMessage.class.getName();
} else if(inferByteMessage(msg)) {
return BytesMessage.class.getName();
} else if(inferObjectMessage(msg)) {
return ObjectMessage.class.getName();
} else if(inferStreamMessage(msg)) {
return StreamMessage.class.getName();
} else {
return null;
}
}
public static void convertXMLtoJMSMap(OMElement element, MapMessage message) throws JMSException{
Iterator itr = element.getChildElements();
while (itr.hasNext()){
OMElement elem = (OMElement)itr.next();
message.setString(elem.getLocalName(),elem.getText());
}
}
/**
* Set transport headers from the axis message context, into the JMS message
*
* @param msgContext the axis message context
* @param message the JMS Message
* @throws JMSException on exception
*/
public static void setTransportHeaders(MessageContext msgContext, Message message)
throws JMSException {
Map<?,?> headerMap = (Map<?,?>) msgContext.getProperty(MessageContext.TRANSPORT_HEADERS);
if (headerMap == null) {
return;
}
for (Object headerName : headerMap.keySet()) {
String name = (String) headerName;
if (name.startsWith(JMSConstants.JMSX_PREFIX) &&
!(name.equals(JMSConstants.JMSX_GROUP_ID) || name.equals(JMSConstants.JMSX_GROUP_SEQ))) {
continue;
}
if (JMSConstants.JMS_COORELATION_ID.equals(name)) {
message.setJMSCorrelationID(
(String) headerMap.get(JMSConstants.JMS_COORELATION_ID));
} else if (JMSConstants.JMS_DELIVERY_MODE.equals(name)) {
Object o = headerMap.get(JMSConstants.JMS_DELIVERY_MODE);
if (o instanceof Integer) {
message.setJMSDeliveryMode((Integer) o);
} else if (o instanceof String) {
try {
message.setJMSDeliveryMode(Integer.parseInt((String) o));
} catch (NumberFormatException nfe) {
log.warn("Invalid delivery mode ignored : " + o, nfe);
}
} else {
log.warn("Invalid delivery mode ignored : " + o);
}
} else if (JMSConstants.JMS_EXPIRATION.equals(name)) {
message.setJMSExpiration(
Long.parseLong((String) headerMap.get(JMSConstants.JMS_EXPIRATION)));
} else if (JMSConstants.JMS_MESSAGE_ID.equals(name)) {
message.setJMSMessageID((String) headerMap.get(JMSConstants.JMS_MESSAGE_ID));
} else if (JMSConstants.JMS_PRIORITY.equals(name)) {
message.setJMSPriority(
Integer.parseInt((String) headerMap.get(JMSConstants.JMS_PRIORITY)));
} else if (JMSConstants.JMS_TIMESTAMP.equals(name)) {
message.setJMSTimestamp(
Long.parseLong((String) headerMap.get(JMSConstants.JMS_TIMESTAMP)));
} else if (JMSConstants.JMS_MESSAGE_TYPE.equals(name)) {
message.setJMSType((String) headerMap.get(JMSConstants.JMS_MESSAGE_TYPE));
} else {
Object value = headerMap.get(name);
if (value instanceof String) {
if (name.contains("-")) {
if (isHyphenReplaceMode(msgContext)) { // we replace
message.setStringProperty(transformHyphenatedString(name), (String) value);
} else if (isHyphenDeleteMode(msgContext)) { // we skip
continue;
} else {
message.setStringProperty(name, (String) value);
}
} else {
message.setStringProperty(name, (String) value);
}
} else if (value instanceof Boolean) {
message.setBooleanProperty(name, (Boolean) value);
} else if (value instanceof Integer) {
message.setIntProperty(name, (Integer) value);
} else if (value instanceof Long) {
message.setLongProperty(name, (Long) value);
} else if (value instanceof Double) {
message.setDoubleProperty(name, (Double) value);
} else if (value instanceof Float) {
message.setFloatProperty(name, (Float) value);
}
}
}
}
/**
* Return the JMS destination with the given destination name looked up from the context
*
* @param context the Context to lookup
* @param destinationName name of the destination to be looked up
* @param destinationType type of the destination to be looked up
* @return the JMS destination, or null if it does not exist
*/
public static Destination lookupDestination(Context context, String destinationName,
String destinationType) throws NamingException {
if (destinationName == null) {
return null;
}
try {
return JMSUtils.lookup(context, Destination.class, destinationName);
} catch (NameNotFoundException e) {
try {
Properties initialContextProperties = new Properties();
if (context.getEnvironment() != null) {
if (context.getEnvironment().get(JMSConstants.NAMING_FACTORY_INITIAL) != null) {
initialContextProperties.put(JMSConstants.NAMING_FACTORY_INITIAL, context.getEnvironment().get(JMSConstants.NAMING_FACTORY_INITIAL));
}
if (context.getEnvironment().get(JMSConstants.CONNECTION_STRING) != null) {
initialContextProperties.put(JMSConstants.CONNECTION_STRING, context.getEnvironment().get(JMSConstants.CONNECTION_STRING));
}
if (context.getEnvironment().get(JMSConstants.PROVIDER_URL) != null) {
initialContextProperties.put(JMSConstants.PROVIDER_URL, context.getEnvironment().get(JMSConstants.PROVIDER_URL));
}
}
if (JMSConstants.DESTINATION_TYPE_TOPIC.equalsIgnoreCase(destinationType)) {
initialContextProperties.put(JMSConstants.TOPIC_PREFIX + destinationName, destinationName);
} else if (JMSConstants.DESTINATION_TYPE_QUEUE.equalsIgnoreCase(destinationType)
|| JMSConstants.DESTINATION_TYPE_GENERIC.equalsIgnoreCase(destinationType)) {
initialContextProperties.put(JMSConstants.QUEUE_PREFIX + destinationName, destinationName);
}
InitialContext initialContext = new InitialContext(initialContextProperties);
try {
return JMSUtils.lookup(initialContext, Destination.class, destinationName);
} catch (NamingException e1) {
return JMSUtils.lookup(context, Destination.class,
(JMSConstants.DESTINATION_TYPE_TOPIC.equalsIgnoreCase(destinationType) ?
"dynamicTopics/" : "dynamicQueues/") + destinationName);
}
} catch (NamingException x) {
log.warn("Cannot locate destination : " + destinationName);
throw x;
}
} catch (NamingException e) {
log.warn("Cannot locate destination : " + destinationName, e);
throw e;
}
}
private static <T> T lookup(Context context, Class<T> clazz, String name)
throws NamingException {
Object object = context.lookup(name);
try {
return clazz.cast(object);
} catch (ClassCastException ex) {
// Instead of a ClassCastException, throw an exception with some
// more information.
if (object instanceof Reference) {
Reference ref = (Reference)object;
handleException("JNDI failed to de-reference Reference with name " +
name + "; is the factory " + ref.getFactoryClassName() +
" in your classpath?");
return null;
} else {
handleException("JNDI lookup of name " + name + " returned a " +
object.getClass().getName() + " while a " + clazz + " was expected");
return null;
}
}
}
protected static void handleException(String s) throws NamingException{
log.error(s);
throw new NamingException(s);
}
/**
* Extract transport level headers from JMS message into a Map
* @param message JMS message
* @param msgContext axis2 message context
* @return a Map of the transport headers
*/
public static Map<String, Object> getTransportHeaders(Message message, MessageContext msgContext) {
// create a Map to hold transport headers
Map<String, Object> map = new HashMap<>();
try {
Enumeration<?> propertyNamesEnm = message.getPropertyNames();
while (propertyNamesEnm.hasMoreElements()) {
String headerName = (String) propertyNamesEnm.nextElement();
Object headerValue = message.getObjectProperty(headerName);
if (headerValue instanceof String) {
if (isHyphenReplaceMode(msgContext)) {
map.put(inverseTransformHyphenatedString(headerName), message.getStringProperty(headerName));
} else {
map.put(headerName, message.getStringProperty(headerName));
}
} else if (headerValue instanceof Integer) {
map.put(headerName, message.getIntProperty(headerName));
} else if (headerValue instanceof Boolean) {
map.put(headerName, message.getBooleanProperty(headerName));
} else if (headerValue instanceof Long) {
map.put(headerName, message.getLongProperty(headerName));
} else if (headerValue instanceof Double) {
map.put(headerName, message.getDoubleProperty(headerName));
} else if (headerValue instanceof Float) {
map.put(headerName, message.getFloatProperty(headerName));
} else {
map.put(headerName, headerValue);
}
}
} catch (JMSException e) {
log.error("Error while reading the Transport Headers from JMS Message", e);
}
return map;
}
private static boolean isHyphenReplaceMode(MessageContext msgContext) {
if (msgContext == null) {
return false;
}
String hyphenSupport = (String) msgContext.getProperty(JMSConstants.PARAM_JMS_HYPHEN_MODE);
if (hyphenSupport != null && hyphenSupport.equals(JMSConstants.HYPHEN_MODE_REPLACE)) {
return true;
}
return false;
}
/**
* This method is to fix ESBJAVA-3687 - certain brokers do not support '-' in JMS property name, in such scenarios
* we will replace the dash with a special character sequence. This support is configurable and is turned off by
* default.
* @return modified string name if broker does not support name format
*/
private static String transformHyphenatedString(String name) {
return name.replaceAll("-", JMSConstants.HYPHEN_REPLACEMENT_STR);
}
private static boolean isHyphenDeleteMode(MessageContext msgContext) {
if (msgContext == null) {
return false;
}
String hyphenSupport = (String) msgContext.getProperty(JMSConstants.PARAM_JMS_HYPHEN_MODE);
if (hyphenSupport != null && hyphenSupport.equals(JMSConstants.HYPHEN_MODE_DELETE)) {
return true;
}
return false;
}
private static boolean inferTextMessage(Message msg) {
if (msg instanceof TextMessage) {
return true;
}
return false;
}
private static boolean inferStreamMessage(Message msg) {
if (msg instanceof StreamMessage) {
return true;
}
return false;
}
private static boolean inferObjectMessage(Message msg) {
if (msg instanceof ObjectMessage) {
return true;
}
return false;
}
private static boolean inferByteMessage(Message msg) {
if (msg instanceof BytesMessage) {
return true;
}
return false;
}
private static String inverseTransformHyphenatedString(String name) {
return name.replaceAll(JMSConstants.HYPHEN_REPLACEMENT_STR, "-");
}
}