/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author daniel.brum@jboss.com
*/
package org.jboss.internal.soa.esb.persistence.format.db;
import java.io.Serializable;
import java.net.URI;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.message.urigen.DefaultMessageURIGenerator;
import org.jboss.internal.soa.esb.util.Encoding;
import org.jboss.soa.esb.Service;
import org.jboss.soa.esb.client.ServiceInvoker;
import org.jboss.soa.esb.common.Environment;
import org.jboss.soa.esb.common.ModulePropertyManager;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.urigen.MessageURIGenerator;
import org.jboss.soa.esb.persistence.manager.ConnectionManager;
import org.jboss.soa.esb.persistence.manager.ConnectionManagerException;
import org.jboss.soa.esb.persistence.manager.ConnectionManagerFactory;
import org.jboss.soa.esb.services.persistence.MessageStore;
import org.jboss.soa.esb.services.persistence.MessageStoreException;
import org.jboss.soa.esb.services.persistence.RedeliverStore;
import org.jboss.soa.esb.util.Util;
public class DBMessageStoreImpl implements RedeliverStore
{
public static final String DEFAULT_TABLE_NAME = "message";
public static final String UNCLASSIFIED_CLASSIFICATION = "NONE";
private Logger logger = Logger.getLogger(this.getClass());
protected ConnectionManager mgr = null;
private Integer maxRedeliverCount = 10;
private String tableName = DEFAULT_TABLE_NAME;
protected MessageURIGenerator uriGenerator = new DefaultMessageURIGenerator();
public DBMessageStoreImpl() throws ConnectionManagerException
{
mgr = ConnectionManagerFactory.getConnectionManager();
tableName = ModulePropertyManager.getPropertyManager(ModulePropertyManager.DBSTORE_MODULE).getProperty(Environment.MSG_STORE_DB_TABLE_NAME, DEFAULT_TABLE_NAME);
}
/* (non-Javadoc)
* @see org.jboss.soa.esb.services.persistence.MessageStore#getMessageURIGenerator()
*/
public MessageURIGenerator getMessageURIGenerator() {
return uriGenerator;
}
/**
* add's a @Message to the database persistence store
* will set the 'delivered' flag to TRUE by default - assuming that the @Message has been delivered
* If classification is null or "", then NONE will be used.
*/
public synchronized URI addMessage (Message message, String classification) throws MessageStoreException
{
URI uid = null;
Connection conn=null;
try{
conn = mgr.getConnection();
uid = uriGenerator.generateMessageURI(message);
if ((classification == null) || (classification.equals("")))
classification = UNCLASSIFIED_CLASSIFICATION;
insert(uid, message, classification, "TRUE", conn);
}
catch (Exception e)
{
logger.error(e);
throw new MessageStoreException(e);
}
finally
{
release(conn);
}
return uid;
}
/**
* return a @Message based on the passed in key in the form of a JBoss ESB @URI
* format for URI: "urn:jboss/esb/message/UID#" + UUID.randomUUID()" - see the method in this class @addMessage
*/
public synchronized Message getMessage (URI uid)
throws MessageStoreException
{
Message message = null;
Connection conn=null;
try {
conn = mgr.getConnection();
message = select(uid, conn);
} catch (Exception e) {
throw new MessageStoreException(e);
} finally {
release(conn);
}
return message;
}
/**
* return a @Message based on the passed in key in the form of a JBoss ESB @URI
* format for URI: "urn:jboss/esb/message/UID#" + UUID.randomUUID()" - see the method in this class @addMessage
*/
public synchronized Message getMessage (URI uid, String classification)
throws MessageStoreException
{
Message message = null;
Connection conn=null;
try {
conn = mgr.getConnection();
message = select(uid, classification, conn);
} catch (Exception e) {
throw new MessageStoreException(e);
} finally {
release(conn);
}
return message;
}
/**
* remove a @Message based on the passed in key in the form of a JBoss ESB @URI
* format for URI: "urn:jboss/esb/message/UID#" + UUID.randomUUID()" - see the method in this class @removeMessage
* If classification is null or "", then NONE will be used.
*/
public synchronized int removeMessage (URI uid, String classification)
throws MessageStoreException
{
int response;
Connection conn=null;
try {
conn = mgr.getConnection();
if ((classification == null) || (classification.equals("")))
classification = UNCLASSIFIED_CLASSIFICATION;
response = delete(uid, classification, conn);
} catch (Exception e) {
throw new MessageStoreException(e);
} finally {
release(conn);
}
return response;
}
/**
*
* @param uid - key for message to set undelivered flag on
* @throws MessageStoreException
*/
public void setUndelivered(URI uid) throws MessageStoreException
{
String sql = "update "+tableName+" set delivered = 'FALSE' where uuid=?";
Connection conn=null;
try {
conn = mgr.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
try
{
ps.setString(1, uid.toString());
ps.execute();
}
finally
{
ps.close();
}
} catch (Exception e) {
throw new MessageStoreException(e);
} finally {
release(conn);
}
}
public void setDelivered(URI uid) throws MessageStoreException{
String sql = "update "+tableName+" set delivered = 'TRUE' where uuid=?";
Connection conn=null;
try {
conn = mgr.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
try
{
ps.setString(1, "FALSE");
ps.execute();
}
finally
{
ps.close() ;
}
} catch (Exception e) {
throw new MessageStoreException(e);
} finally {
release(conn);
}
}
/**
* This method can be used to retrieve a collection of all the undelivered (delivered=FALSE) from the message-store
* You should test for 'null' on the return type to see if any messages exist in the collection
* @return Map<URI, Message> - a collection of all the undelivered messages in the message-store
* @throws MessageStoreException
*/
public Map<URI, Message> getUndeliveredMessages(String classification) throws MessageStoreException {
HashMap<URI, Message> messages = new HashMap<URI, Message>();
String sql = "select uuid from "+tableName+" where delivered='FALSE'";
if (classification!=null) {
sql += " and classification=?";
}
Connection conn=null;
try
{
conn = mgr.getConnection();
PreparedStatement stmt = null;
ResultSet rs = null;
try
{
stmt = conn.prepareStatement(sql);
if (classification != null)
{
stmt.setString(1, classification) ;
}
rs = stmt.executeQuery();
while (rs.next()) {
URI uid = new URI(rs.getString(1));
Message msg = getMessage(uid);
messages.put(uid, msg);
}
}
finally
{
try
{
if (rs != null)
rs.close();
}
catch (final Exception ex)
{
logger.warn("Could not close ResultSet.", ex);
}
try
{
if (stmt != null)
stmt.close();
}
catch (final Exception ex)
{
logger.warn("Could not close Statement.", ex);
}
}
}
catch (Exception e)
{
throw new MessageStoreException(e);
}
finally
{
release(conn);
}
logger.info("retrieved " + messages.size() + " undelivered messages");
return messages;
}
/**
* This method can be used to retrieve a collection of all from the message-store
* You should test for 'null' on the return type to see if any messages exist in the collection
* @return Map<URI, Message> - a collection of all the undelivered messages in the message-store
* @throws MessageStoreException
*/
public Map<URI, Message> getAllMessages(String classification) throws MessageStoreException {
HashMap<URI, Message> messages = new HashMap<URI, Message>();
String sql = "select uuid, message from "+tableName;
if (classification!=null) {
sql += " where classification=?";
}
Connection conn=null;
try
{
conn = mgr.getConnection();
PreparedStatement stmt = null;
ResultSet rs = null;
try
{
stmt = conn.prepareStatement(sql);
if (classification != null)
{
stmt.setString(1, classification) ;
}
rs = stmt.executeQuery();
while (rs.next()) {
URI uid = new URI(rs.getString(1));
Message msg = Util.deserialize((Serializable) Encoding.decodeToObject( rs.getString(2)));
messages.put(uid, msg);
}
}
finally
{
try
{
if (rs != null)
rs.close();
}
catch (final Exception ex)
{
logger.warn("Could not close ResultSet.", ex);
}
try
{
if (stmt != null)
stmt.close();
}
catch (final Exception ex)
{
logger.warn("Could not close Statement.", ex);
}
}
}
catch (Exception e)
{
throw new MessageStoreException(e);
}
finally
{
release(conn);
}
logger.debug("retrieved " + messages.size() + " " + classification + " messages");
return messages;
}
private void release (Connection conn)
{
if (conn != null)
{
try
{
conn.close();
}
catch (Exception e2)
{
logger.warn(e2.getMessage(), e2);
}
}
}
/**
*
*/
public boolean redeliver(URI uuid) throws MessageStoreException
{
boolean isDelivered=false;
boolean error=false;
Connection con = null;
try
{
con = mgr.getConnection();
con.setAutoCommit(false);
}
catch (final SQLException e)
{
if (logger.isDebugEnabled()) {
logger.debug("dbms doesn't support setAutoCommit(false), deadlocks may occur under normal processing.");
logger.debug(e.getMessage(), e);
}
}
try
{
Message message=select(uuid, con);
if (message!=null && delete(uuid, RedeliverStore.CLASSIFICATION_RDLVR, con)==1) {
//now any good db should have set a read lock on this record, until we commit.
//if exception is thrown up the delivery count on the message
//if exceeds the maxcount then update the classification to DLQ.
Service to = (Service) message.getProperties().getProperty(ServiceInvoker.DELIVER_TO);
try {
ServiceInvoker si = new ServiceInvoker(to.getCategory(), to.getName());
message.getProperties().setProperty(RedeliverStore.IS_REDELIVERY, true);
si.deliverAsync(message);
isDelivered=true;
} catch (MessageDeliverException e) {
logger.debug(e.getMessage(), e);
}
if (isDelivered) {
//the message is delivered, we're good so remove it from the store
delete(uuid, RedeliverStore.CLASSIFICATION_RDLVR, con);
} else {
//the message was not delivered
if (message.getProperties().getProperty(DELIVER_COUNT)==null) {
//apparently it was the first time
message.getProperties().setProperty(RedeliverStore.DELIVER_COUNT, Integer.valueOf("1"));
logger.debug("attempt 1 to redeliver " + uuid + " failed");
insert(uuid, message, MessageStore.CLASSIFICATION_RDLVR, "FALSE", con);
} else {
Integer redeliverCount = (Integer) message.getProperties().getProperty(DELIVER_COUNT);
if (redeliverCount < maxRedeliverCount || maxRedeliverCount < 0) {
//up the count
message.getProperties().setProperty(RedeliverStore.DELIVER_COUNT, ++redeliverCount);
logger.debug("attempt " + redeliverCount + " to redeliver " + uuid + " failed");
insert(uuid, message, MessageStore.CLASSIFICATION_RDLVR, "FALSE", con);
} else {
//undeliverable, send to the DLQ
logger.warn(" giving up and writing " + uuid + " to " + MessageStore.CLASSIFICATION_DLQ);
insert(uuid, message, MessageStore.CLASSIFICATION_DLQ, "FALSE", con);
}
}
}
}
}
catch (final SQLException e)
{
logger.warn("DBMessageStoreImpl caught exception "+e+". Will force transaction to rollback.");
error=true;
}
finally
{
if (con!=null) {
try {
if (!error) {
con.commit();
} else {
con.rollback();
}
} catch (SQLException e) {
logger.error(e);
}
try {
con.close();
} catch (Exception e2) {
logger.error(e2);
}
}
}
return isDelivered;
}
private Message select(URI uid, Connection connection)
throws SQLException, MessageStoreException
{
Message message=null;
String selectSql = "select * from "+tableName+" where uuid=?";
PreparedStatement selectStmt = null;
ResultSet rs = null;
try
{
selectStmt = connection.prepareStatement(selectSql);
selectStmt.setObject(1, uid.toString());
rs = selectStmt.executeQuery();
if (rs.next()) {
try {
message = Util.deserialize((Serializable) Encoding.decodeToObject(rs.getString("message")));
} catch (Exception e) {
throw new MessageStoreException(e);
}
}
}
finally
{
try
{
if (rs != null)
rs.close();
}
catch (final Exception ex)
{
logger.warn("Could not close ResultSet.", ex);
}
try
{
if (selectStmt != null)
selectStmt.close();
}
catch (final Exception ex)
{
logger.warn("Could not close Statement.", ex);
}
}
return message;
}
private Message select(URI uid, String classification, Connection connection)
throws SQLException, MessageStoreException
{
Message message=null;
String selectSql = "select * from "+tableName+" where uuid=? and classification=?";
PreparedStatement selectStmt = null;
ResultSet rs = null;
try
{
selectStmt = connection.prepareStatement(selectSql);
selectStmt.setObject(1, uid.toString());
selectStmt.setObject(2, classification);
rs = selectStmt.executeQuery();
if (rs.next()) {
try {
message = Util.deserialize((Serializable) Encoding.decodeToObject(rs.getString("message")));
} catch (Exception e) {
throw new MessageStoreException(e);
}
}
}
finally
{
try
{
if (rs != null)
rs.close();
}
catch (final Exception ex)
{
logger.warn("Could not close ResultSet.", ex);
}
try
{
if (selectStmt != null)
selectStmt.close();
}
catch (final Exception ex)
{
logger.warn("Could not close Statement.", ex);
}
}
return message;
}
private int delete(URI uid, String classification, Connection connection)
throws SQLException
{
String deleteSql = "delete from "+tableName+" where uuid=? and classification=?";
PreparedStatement stmt = null;
int result;
try
{
stmt = connection.prepareStatement(deleteSql);
stmt.setObject(1, uid.toString());
stmt.setObject(2, classification);
result = stmt.executeUpdate();
}
finally
{
try
{
if (stmt != null)
stmt.close();
}
catch (final Exception ex)
{
logger.warn("Could not close Statement.", ex);
}
}
return result;
}
private void insert(URI uid, Message message, String classification, String delivered, Connection conn)
throws SQLException, MessageStoreException
{
String sql = "insert into "+tableName+"(uuid, type, message, delivered, classification) values(?,?,?,?,?)";
PreparedStatement ps = null;
try
{
ps = conn.prepareStatement(sql);
ps.setString(1, uid.toString());
ps.setString(2, message.getType().toString());
try {
String messageString = Encoding.encodeObject(Util.serialize(message));
ps.setString(3, messageString);
} catch (Exception e) {
throw new MessageStoreException(e);
}
ps.setString(4, "TRUE");
ps.setString(5, classification);
ps.execute();
}
finally
{
if (ps != null)
ps.close();
}
}
public Integer getMaxRedeliverCount() {
return maxRedeliverCount;
}
public void setMaxRedeliverCount(Integer maxRedeliverCount) {
this.maxRedeliverCount = maxRedeliverCount;
}
}
|