/*
* Copyright (c) 2015, 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.event.output.adapter.jms.internal.util;
import org.apache.axiom.om.OMElement;
import org.apache.axis2.transport.base.BaseConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.event.output.adapter.core.exception.ConnectionUnavailableException;
import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterRuntimeException;
import org.wso2.carbon.event.output.adapter.jms.JMSEventAdapter;
import javax.jms.*;
import java.util.HashMap;
import java.util.Map;
/**
* Performs the actual sending of a JMS message, and the subsequent committing of a JTA transaction
* (if requested) or the local session transaction, if used. An instance of this class is unique
* to a single message send out operation and will not be shared.
*/
public class JMSMessageSender {
private static final Log log = LogFactory.getLog(JMSMessageSender.class);
/**
* Should this sender use JMS 1.1 ? (if false, defaults to 1.0.2b)
*/
private boolean jmsSpec11 = true;
/**
* Are we sending to a Queue ?
*/
private Boolean isQueue = null;
/**
* Factory used by the connection pool.
*/
private JMSConnectionFactory jmsConnectionFactory;
/**
* Create a JMSSender using a JMSConnectionFactory and target EPR
*
* @param jmsConnectionFactory the JMSConnectionFactory
*/
public JMSMessageSender(JMSConnectionFactory jmsConnectionFactory) {
this.jmsSpec11 = jmsConnectionFactory.isJmsSpec11();
this.jmsConnectionFactory = jmsConnectionFactory;
}
/**
* Perform actual send of JMS message to the Destination selected
*/
public void send(Object message, JMSEventAdapter.PublisherDetails publisherDetails, String jmsHeaders) {
Map<String, String> messageProperties = publisherDetails.getMessageConfig();
Boolean jtaCommit = getBooleanProperty(messageProperties, BaseConstants.JTA_COMMIT_AFTER_SEND);
Boolean rollbackOnly = getBooleanProperty(messageProperties, BaseConstants.SET_ROLLBACK_ONLY);
Boolean persistent = getBooleanProperty(messageProperties, JMSConstants.JMS_DELIVERY_MODE);
Integer priority = getIntegerProperty(messageProperties, JMSConstants.JMS_PRIORITY);
Integer timeToLive = getIntegerProperty(messageProperties, JMSConstants.JMS_TIME_TO_LIVE);
MessageProducer producer = null;
Destination destination = null;
Session session = null;
boolean sendingSuccessful = false;
JMSConnectionFactory.JMSPooledConnectionHolder pooledConnection = null;
try {
pooledConnection = jmsConnectionFactory.getConnectionFromPool();
producer = pooledConnection.getProducer();
session = pooledConnection.getSession();
Message jmsMessage = convertToJMSMessage(message, publisherDetails.getMessageConfig(), session);
setJMSTransportHeaders(jmsMessage, jmsHeaders);
// Do not commit, if message is marked for rollback
if (rollbackOnly != null && rollbackOnly) {
jtaCommit = Boolean.FALSE;
}
if (persistent != null) {
try {
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
} catch (JMSException e) {
handleConnectionException("Error setting JMS Producer for PERSISTENT delivery", e);
}
}
if (priority != null) {
try {
producer.setPriority(priority);
} catch (JMSException e) {
handleConnectionException("Error setting JMS Producer priority to : " + priority, e);
}
}
if (timeToLive != null) {
try {
producer.setTimeToLive(timeToLive);
} catch (JMSException e) {
handleConnectionException("Error setting JMS Producer TTL to : " + timeToLive, e);
}
}
// perform actual message sending
// try {
if (jmsSpec11 || isQueue == null) {
producer.send(jmsMessage);
} else {
if (isQueue) {
((QueueSender) producer).send(jmsMessage);
} else {
((TopicPublisher) producer).publish(jmsMessage);
}
}
sendingSuccessful = true;
if (log.isDebugEnabled()) {
// // set the actual MessageID to the message context for use by any others down the line
String msgId = null;
try {
msgId = jmsMessage.getJMSMessageID();
} catch (JMSException jmse) {
log.error(jmse.getMessage(), jmse);
}
log.debug(
" with JMS Message ID : " + msgId +
" to destination : " + producer.getDestination());
}
} catch (JMSException e) {
handleConnectionException("Error sending message to destination : " + destination, e);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (jtaCommit != null) {
try {
if (session.getTransacted()) {
if (sendingSuccessful && (rollbackOnly == null || !rollbackOnly)) {
session.commit();
} else {
session.rollback();
}
}
if (log.isDebugEnabled()) {
log.debug((sendingSuccessful ? "Committed" : "Rolled back") +
" local (JMS Session) Transaction");
}
} catch (Exception e) {
handleConnectionException("Error committing/rolling back local (i.e. session) " +
"transaction after sending of message "//with MessageContext ID : " +
+ " to destination : " + destination, e);
}
}
if (pooledConnection != null) {
jmsConnectionFactory.returnPooledConnection(pooledConnection);
}
}
}
private Message setJMSTransportHeaders(Message message, String headerProperty) {
Map<String, String> messageConfiguration = new HashMap<String, String>();
if (headerProperty != null && message != null) {
String[] headers = headerProperty.split(JMSEventAdapterConstants.HEADER_SEPARATOR);
if (headers.length > 0) {
for (String header : headers) {
try {
String[] headerPropertyWithValue = header.split(JMSEventAdapterConstants.ENTRY_SEPARATOR, 2);
messageConfiguration.put(headerPropertyWithValue[0], headerPropertyWithValue[1]);
} catch (Exception e) {
log.warn("Header property \" " + header + " \" is not defined in the correct format", e);
}
}
}
try {
return JMSUtils.setTransportHeaders(messageConfiguration, message);
} catch (JMSException e) {
throw new OutputEventAdapterRuntimeException(e);
}
}
return message;
}
public Message convertToJMSMessage(Object messageObj, Map<String, String> messageProperties, Session session) {
Message jmsMessage = null;
try {
if (messageObj instanceof OMElement) {
jmsMessage = session.createTextMessage(messageObj.toString());
} else if (messageObj instanceof String) {
jmsMessage = session.createTextMessage((String) messageObj);
} else if (messageObj instanceof Map) {
MapMessage mapMessage = session.createMapMessage();
Map sourceMessage = (Map) messageObj;
for (Object key : sourceMessage.keySet()) {
mapMessage.setObject((String) key, sourceMessage.get(key));
}
jmsMessage = mapMessage;
}
} catch (JMSException e) {
handleException("Failed to publish to topic:" + messageProperties.get(JMSConstants.PARAM_DESTINATION), e);
}
return jmsMessage;
}
/**
* Close non-shared producer, session and connection if any
*/
public void close() {
jmsConnectionFactory.close();
}
private void handleException(String message, Exception e) {
log.error(message, e);
throw new OutputEventAdapterRuntimeException(message, e);
}
private void handleConnectionException(String message, Exception e) {
log.error(message, e);
throw new ConnectionUnavailableException(message, e);
}
private Boolean getBooleanProperty(Map<String, String> messageProperties, String name) {
String o = messageProperties.get(name);
if (o != null) {
return Boolean.valueOf(o);
}
return null;
}
private Integer getIntegerProperty(Map<String, String> messageProperties, String name) {
String o = messageProperties.get(name);
if (o != null) {
return Integer.parseInt(o);
}
return null;
}
}