Java tutorial
/* * Copyright (c) Mirth Corporation. All rights reserved. * http://www.mirthcorp.com * * The software in this package is published under the terms of the MPL * license a copy of which has been included with this distribution in * the LICENSE.txt file. */ package com.mirth.connect.server.controllers; import java.net.Socket; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.activation.UnsupportedDataTypeException; import org.apache.commons.collections.MapUtils; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.lang.SerializationUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.mule.umo.UMOEvent; import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.client.SqlMapSession; import com.mirth.commons.encryption.Encryptor; import com.mirth.connect.model.Attachment; import com.mirth.connect.model.Channel; import com.mirth.connect.model.MessageObject; import com.mirth.connect.model.MessageObject.Status; import com.mirth.connect.model.Response; import com.mirth.connect.model.filters.MessageObjectFilter; import com.mirth.connect.server.builders.ErrorMessageBuilder; import com.mirth.connect.server.util.AttachmentUtil; import com.mirth.connect.server.util.DICOMUtil; import com.mirth.connect.server.util.DatabaseUtil; import com.mirth.connect.server.util.SqlConfig; import com.mirth.connect.server.util.UUIDGenerator; import com.mirth.connect.server.util.VMRouter; import com.mirth.connect.util.QueueUtil; public class DefaultMessageObjectController extends MessageObjectController { private static final String RECEIVE_SOCKET = "receiverSocket"; private Logger logger = Logger.getLogger(this.getClass()); private ConfigurationController configurationController = ControllerFactory.getFactory() .createConfigurationController(); private ChannelStatisticsController statisticsController = ControllerFactory.getFactory() .createChannelStatisticsController(); private ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(); private static DefaultMessageObjectController instance = null; private DefaultMessageObjectController() { } public static MessageObjectController create() { synchronized (DefaultMessageObjectController.class) { if (instance == null) { instance = new DefaultMessageObjectController(); } return instance; } } public void removeAllFilterTables() { Connection conn = null; ResultSet resultSet = null; try { conn = SqlConfig.getSqlMapClient().getDataSource().getConnection(); // Gets the database metadata DatabaseMetaData dbmd = conn.getMetaData(); // Specify the type of object; in this case we want tables String[] types = { "TABLE" }; String tablePattern = "MSG_TMP_%"; resultSet = dbmd.getTables(null, null, tablePattern, types); boolean resultFound = resultSet.next(); // Some databases only accept lowercase table names if (!resultFound) { resultSet = dbmd.getTables(null, null, tablePattern.toLowerCase(), types); resultFound = resultSet.next(); } while (resultFound) { // Get the table name String tableName = resultSet.getString(3); // Get the uid and remove its filter tables/indexes/sequences removeFilterTable(tableName.substring(8)); resultFound = resultSet.next(); } } catch (SQLException e) { logger.error(e); } finally { DbUtils.closeQuietly(resultSet); DbUtils.closeQuietly(conn); } } public void updateMessage(MessageObject incomingMessageObject, boolean checkIfMessageExists) { MessageObject messageObject = (MessageObject) incomingMessageObject.clone(); Socket socket = null; try { // Check if we have a socket. We need to replace with a string // because // Sockets are not serializable and we want to retain the socket if (messageObject.getChannelMap().containsKey(RECEIVE_SOCKET)) { Object socketObj = messageObject.getChannelMap().get(RECEIVE_SOCKET); // XXX: Aren't these two cases doing the exact same thing?? if (socketObj instanceof Socket) { socket = (Socket) socketObj; messageObject.getChannelMap().put(RECEIVE_SOCKET, socket.toString()); } else { messageObject.getChannelMap().put(RECEIVE_SOCKET, socketObj.toString()); } } } catch (Exception e) { logger.error(e); } // update the stats counts if (messageObject.getStatus().equals(MessageObject.Status.TRANSFORMED)) { statisticsController.incrementReceivedCount(messageObject.getChannelId()); } else if (messageObject.getStatus().equals(MessageObject.Status.FILTERED)) { statisticsController.incrementFilteredCount(messageObject.getChannelId()); } else if (messageObject.getStatus().equals(MessageObject.Status.ERROR)) { statisticsController.incrementErrorCount(messageObject.getChannelId()); } else if (messageObject.getStatus().equals(MessageObject.Status.SENT)) { statisticsController.incrementSentCount(messageObject.getChannelId()); } else if (messageObject.getStatus().equals(MessageObject.Status.QUEUED)) { statisticsController.incrementQueuedCount(messageObject.getChannelId()); } Channel channel = ControllerFactory.getFactory().createChannelController() .getDeployedChannelById(messageObject.getChannelId()); if (channel != null && channel.getProperties().containsKey("store_messages")) { // If replacing messages that were stored on special conditions // ("store errored only" or "do not store filtered messages"), // then try to remove the old message if the new one succeeded. if (checkIfMessageExists) { if (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("error_messages_only").equals("true") && !messageObject.getStatus().equals(MessageObject.Status.ERROR)) { try { /* * MIRTH-2098: Don't remove the message from the actual * queue if the new status is QUEUED. Still remove it * from the database so that a queued message doesn't * show up in the message browser when only storing * errored messages. */ if (!messageObject.getStatus().equals(MessageObject.Status.QUEUED)) { removeMessageFromQueue(messageObject); } removeMessage(messageObject); } catch (Exception e) { logger.error("Could not remove old message: id=" + messageObject.getId(), e); } } if (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("dont_store_filtered").equals("true") && messageObject.getStatus().equals(MessageObject.Status.FILTERED)) { try { removeMessageFromQueue(messageObject); removeMessage(messageObject); } catch (Exception e) { logger.error("Could not remove old message: id=" + messageObject.getId(), e); } } } if (channel.getProperties().get("store_messages").equals("false") || (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("error_messages_only").equals("true") && !messageObject.getStatus().equals(MessageObject.Status.ERROR)) || (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("dont_store_filtered").equals("true") && messageObject.getStatus().equals(MessageObject.Status.FILTERED))) { logger.debug("message is not stored"); return; } else if (channel.getProperties().getProperty("encryptData").equals("true")) { encryptMessageData(messageObject); } } writeMessageToDatabase(messageObject, checkIfMessageExists); if (socket != null) { messageObject.getChannelMap().put(RECEIVE_SOCKET, socket); } } public void updateMessageStatus(String channelId, String messageId, MessageObject.Status newStatus) { // update the stats counts if (newStatus.equals(MessageObject.Status.TRANSFORMED)) { statisticsController.incrementReceivedCount(channelId); } else if (newStatus.equals(MessageObject.Status.FILTERED)) { statisticsController.incrementFilteredCount(channelId); } else if (newStatus.equals(MessageObject.Status.ERROR)) { statisticsController.incrementErrorCount(channelId); } else if (newStatus.equals(MessageObject.Status.SENT)) { statisticsController.incrementSentCount(channelId); } else if (newStatus.equals(MessageObject.Status.QUEUED)) { statisticsController.incrementQueuedCount(channelId); } Channel channel = ControllerFactory.getFactory().createChannelController() .getDeployedChannelById(channelId); if (channel == null) { logger.warn("Cannot update message " + messageId + " status as the channel " + channelId + " doesn't exists "); return; } if (channel.getProperties().containsKey("store_messages")) { if (channel.getProperties().get("store_messages").equals("false") || (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("error_messages_only").equals("true") && (newStatus == MessageObject.Status.ERROR)) || (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("dont_store_filtered").equals("true") && (newStatus == MessageObject.Status.FILTERED))) { logger.debug("message " + messageId + " status is not stored because channel store configuration parameters"); return; } } try { HashMap<String, String> params = new HashMap<String, String>(); params.put("status", newStatus.toString()); params.put("id", messageId); SqlConfig.getSqlMapClient().update("Message.updateMessageStatus", params); } catch (SQLException e) { logger.error("Error updating message " + messageId + " status due to a database problem", e); } } public void importMessage(MessageObject messageObject) { writeMessageToDatabase(messageObject, true); } private void writeMessageToDatabase(MessageObject messageObject, boolean checkIfMessageExists) { try { if (checkIfMessageExists) { int count = (Integer) SqlConfig.getSqlMapClient().queryForObject("Message.getMessageCount", messageObject.getId()); if (count == 0) { logger.debug("adding message: id=" + messageObject.getId()); SqlConfig.getSqlMapClient().insert("Message.insertMessage", messageObject); } else { logger.debug("updating message: id=" + messageObject.getId()); SqlConfig.getSqlMapClient().update("Message.updateMessage", messageObject); } } else { logger.debug("adding message (not checking for message): id=" + messageObject.getId()); SqlConfig.getSqlMapClient().insert("Message.insertMessage", messageObject); } } catch (SQLException e) { logger.error("could not log message: id=" + messageObject.getId(), e); } } private void encryptMessageData(MessageObject messageObject) { Encryptor encryptor = configurationController.getEncryptor(); if (messageObject.getRawData() != null) { String encryptedRawData = encryptor.encrypt(messageObject.getRawData()); messageObject.setRawData(encryptedRawData); } if (messageObject.getTransformedData() != null) { String encryptedTransformedData = encryptor.encrypt(messageObject.getTransformedData()); messageObject.setTransformedData(encryptedTransformedData); } if (messageObject.getEncodedData() != null) { String encryptedEncodedData = encryptor.encrypt(messageObject.getEncodedData()); messageObject.setEncodedData(encryptedEncodedData); } messageObject.setEncrypted(true); } private void decryptMessageData(MessageObject messageObject) { if (messageObject.isEncrypted()) { Encryptor encryptor = configurationController.getEncryptor(); if (messageObject.getRawData() != null) { String decryptedRawData = encryptor.decrypt(messageObject.getRawData()); messageObject.setRawData(decryptedRawData); } if (messageObject.getTransformedData() != null) { String decryptedTransformedData = encryptor.decrypt(messageObject.getTransformedData()); messageObject.setTransformedData(decryptedTransformedData); } if (messageObject.getEncodedData() != null) { String decryptedEncodedData = encryptor.decrypt(messageObject.getEncodedData()); messageObject.setEncodedData(decryptedEncodedData); } } } public int createMessagesTempTable(MessageObjectFilter filter, String uid, boolean forceTemp) throws ControllerException { logger.debug("creating temporary message table: filter=" + filter.toString()); if (!forceTemp && DatabaseUtil.statementExists("Message.getMessageByPageLimit")) { return -1; } // If it's not forcing temp tables (export or reprocessing), // then it's reusing the same ones, so remove them. if (!forceTemp) { removeFilterTable(uid); } try { if (DatabaseUtil.statementExists("Message.createTempMessageTableSequence")) { SqlConfig.getSqlMapClient().update("Message.createTempMessageTableSequence", uid); } SqlConfig.getSqlMapClient().update("Message.createTempMessageTable", uid); SqlConfig.getSqlMapClient().update("Message.createTempMessageTableIndex", uid); return SqlConfig.getSqlMapClient().update("Message.populateTempMessageTable", getFilterMap(filter, uid)); } catch (SQLException e) { throw new ControllerException(e); } } // ast: allow ordering with derby public List<MessageObject> getMessagesByPageLimit(int page, int pageSize, int maxMessages, String uid, MessageObjectFilter filter) throws ControllerException { logger.debug("retrieving messages by page: page=" + page); try { Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("uid", uid); int offset = page * pageSize; parameterMap.put("offset", offset); parameterMap.put("limit", pageSize); parameterMap.put("offpluslim", offset + pageSize); parameterMap.putAll(getFilterMap(filter, uid)); List<MessageObject> messages = SqlConfig.getSqlMapClient().queryForList("Message.getMessageByPageLimit", parameterMap); for (MessageObject messageObject : messages) { decryptMessageData(messageObject); } return messages; } catch (Exception e) { throw new ControllerException(e); } } // ast: allow ordering with derby public List<MessageObject> getMessagesByPage(int page, int pageSize, int maxMessages, String uid, boolean descending) throws ControllerException { logger.debug("retrieving messages by page: page=" + page); int last = maxMessages; int first = 1; try { Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("uid", uid); // Use descending for most queries, use ascending for // reprocessing messages in the correct order. if (descending) { parameterMap.put("order", "DESC"); last = maxMessages - (page * pageSize); first = last - pageSize + 1; } else { parameterMap.put("order", "ASC"); first = (page * pageSize) + 1; last = (page + 1) * pageSize; } if ((page != -1) && (pageSize != -1)) { parameterMap.put("first", first); parameterMap.put("last", last); } List<MessageObject> messages = SqlConfig.getSqlMapClient().queryForList("Message.getMessageByPage", parameterMap); for (MessageObject messageObject : messages) { decryptMessageData(messageObject); } return messages; } catch (Exception e) { throw new ControllerException(e); } } public void removeMessage(MessageObject messageObject) throws ControllerException { logger.debug("removing message: id=" + messageObject.getId()); try { MessageObjectFilter filter = new MessageObjectFilter(); filter.setId(messageObject.getId()); SqlConfig.getSqlMapClient().delete("Message.deleteMessage", getFilterMap(filter, null)); SqlConfig.getSqlMapClient().delete("Message.deleteUnusedAttachments"); } catch (Exception e) { throw new ControllerException(e); } } public int removeMessages(MessageObjectFilter filter) throws ControllerException { logger.debug("removing messages: filter=" + filter.toString()); try { removeMessagesFromQueue(filter); int rowCount = SqlConfig.getSqlMapClient().delete("Message.deleteMessage", getFilterMap(filter, null)); SqlConfig.getSqlMapClient().delete("Message.deleteUnusedAttachments"); return rowCount; } catch (Exception e) { throw new ControllerException(e); } } private void vacuumMessageAndAttachmentTable() throws SQLException { if (DatabaseUtil.statementExists("Message.vacuumMessageTable")) { SqlConfig.getSqlMapClient().update("Message.vacuumMessageTable"); } if (DatabaseUtil.statementExists("Message.vacuumAttachmentTable")) { SqlConfig.getSqlMapClient().update("Message.vacuumAttachmentTable"); } } public int pruneMessages(MessageObjectFilter filter, int limit) throws ControllerException { logger.debug("pruning messages: filter=" + filter.toString()); try { int totalRowCount = 0; int rowCount = 0; do { Map<String, Object> parameterMap = getFilterMap(filter, null); if (limit > 0) { parameterMap.put("limit", limit); } // Retry blocks of pruning if they fail in case of deadlocks int retryCount = 0; do { Connection connection = null; SqlMapSession session = null; try { SqlMapClient sqlMapClient = SqlConfig.getSqlMapClient(); // get a connection from the conection pool connection = sqlMapClient.getDataSource().getConnection(); // Must set auto commit to false or the commit will fail connection.setAutoCommit(false); // open a new session with that connection session = sqlMapClient.openSession(connection); // start "ionice" if exists if (DatabaseUtil.statementExists("Message.startPruningTransaction", session)) { session.update("Message.startPruningTransaction"); } // delete the messages rowCount = session.delete("Message.pruneMessages", parameterMap); // end "ionice" if exists if (DatabaseUtil.statementExists("Message.endPruningTransaction", session)) { session.update("Message.endPruningTransaction"); } connection.commit(); retryCount = 0; } catch (Exception e) { if (retryCount < 10) { logger.error("Could not prune messages, retry count: " + retryCount, e); retryCount++; } else { throw e; // Quit trying to prune after 10 failures } } finally { session.close(); DbUtils.close(connection); } } while (retryCount > 0); totalRowCount += rowCount; Thread.sleep(100); /* * Only run again if the limit was used (limit > 0) and the * number of rows removed was >= limit. */ } while (rowCount >= limit && limit > 0); // Retry attachment pruning if it fails in case of deadlocks int retryCount = 0; do { try { SqlConfig.getSqlMapClient().delete("Message.deleteUnusedAttachments"); retryCount = 0; } catch (Exception e) { if (retryCount < 10) { logger.error("Could not prune attachments, retry count: " + retryCount, e); retryCount++; } else { throw e; // Quit trying to prune after 10 failures } } } while (retryCount > 0); vacuumMessageAndAttachmentTable(); return totalRowCount; } catch (Exception e) { throw new ControllerException(e); } } private void removeMessagesFromQueue(MessageObjectFilter filter) throws Exception { String uid = String.valueOf(System.currentTimeMillis()); // clone the filter so that we don't modify the original MessageObjectFilter queueFilter = (MessageObjectFilter) SerializationUtils.clone(filter); queueFilter.setStatus(Status.QUEUED); int size = createMessagesTempTable(queueFilter, uid, true); int page = 0; int interval = 10; while ((page * interval) < size) { for (MessageObject message : getMessagesByPage(page, interval, size, uid, true)) { removeMessageFromQueue(message); } page++; } removeFilterTable(uid); } private void removeMessageFromQueue(MessageObject message) throws Exception { /* * Since removeMessagesFromQueue sets the filter status to QUEUED, we * want to apply that same filter logic here when removing a single * message. */ if (!Status.QUEUED.equals(message.getStatus())) { return; } String queueName = null; String messageId = null; if (message.getConnectorMap().get(QueueUtil.QUEUE_NAME) != null) { queueName = message.getConnectorMap().get(QueueUtil.QUEUE_NAME).toString(); } if (message.getConnectorMap().get(QueueUtil.MESSAGE_ID) != null) { messageId = message.getConnectorMap().get(QueueUtil.MESSAGE_ID).toString(); } if ((queueName == null) || (queueName.length() == 0)) { String connectorId = ControllerFactory.getFactory().createChannelController() .getDeployedConnectorId(message.getChannelId(), message.getConnectorName()); queueName = QueueUtil.getInstance().getQueueName(message.getChannelId(), connectorId); } if ((messageId == null) || (messageId.length() == 0)) { messageId = message.getId(); } QueueUtil.getInstance().removeMessageFromQueue(queueName, messageId); ControllerFactory.getFactory().createChannelStatisticsController() .decrementQueuedCount(message.getChannelId()); } public void removeFilterTable(String uid) { logger.debug("Removing temporary message table: uid=" + uid); try { if (DatabaseUtil.statementExists("Message.dropTempMessageTableSequence")) { SqlConfig.getSqlMapClient().update("Message.dropTempMessageTableSequence", uid); } } catch (SQLException e) { // supress any warnings about the sequence not existing logger.debug(e); } try { if (DatabaseUtil.statementExists("Message.deleteTempMessageTableIndex")) { SqlConfig.getSqlMapClient().update("Message.deleteTempMessageTableIndex", uid); } } catch (SQLException e) { // supress any warnings about the index not existing logger.debug(e); } try { SqlConfig.getSqlMapClient().update("Message.dropTempMessageTable", uid); } catch (SQLException e) { // supress any warnings about the table not existing logger.debug(e); } } public void clearMessages(String channelId) throws ControllerException { logger.debug("clearing messages: channelId=" + channelId); try { Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("channelId", channelId); SqlConfig.getSqlMapClient().delete("Message.deleteMessage", parameterMap); SqlConfig.getSqlMapClient().delete("Message.deleteUnusedAttachments"); Channel filterChannel = new Channel(); filterChannel.setId(channelId); Channel channel = ControllerFactory.getFactory().createChannelController().getChannel(filterChannel) .get(0); QueueUtil.getInstance().removeAllQueuesForChannel(channel); } catch (Exception e) { throw new ControllerException(e); } } public void reprocessMessages(final MessageObjectFilter filter, final boolean replace, final List<String> destinations) throws ControllerException { try { // since get message by page expects a session, we'll make up a // session ID using a the current system time final String sessionId = String.valueOf(System.currentTimeMillis()); final int size = createMessagesTempTable(filter, sessionId, true); Thread reprocessThread = new Thread(new Runnable() { public void run() { try { int page = 0; int interval = 10; VMRouter router = new VMRouter(); while ((page * interval) < size) { List<MessageObject> messages = getMessagesByPage(page, interval, size, sessionId, false); try { for (MessageObject message : messages) { try { Thread.sleep(10); } catch (InterruptedException ie) { logger.debug(ie); } // get attachment for old message if (message.isAttachment()) { if (message.getRawDataProtocol().equals(MessageObject.Protocol.DICOM)) { String rawData = DICOMUtil.getDICOMRawData(message); message.setRawData(rawData); } else { String rawData = AttachmentUtil.reAttachMessage(message); message.setRawData(rawData); } } if (replace) { message.getContext().put("replace", "true"); if (!message.getConnectorName().equalsIgnoreCase("source")) { message.getContext().put("messageId", message.getCorrelationId()); } else { message.getContext().put("messageId", message.getId()); } } message.getContext().put("destinations", destinations); /* Keep the original filename if reprocessing * See MIRTH-1372 for more details */ if (message.getChannelMap().containsKey("originalFilename")) { message.getContext().put("originalFilename", MapUtils.getString(message.getChannelMap(), "originalFilename")); } router.routeMessageByChannelId(message.getChannelId(), message, true); } } catch (Exception e) { throw new ControllerException("could not reprocess message", e); } page++; } } catch (Exception e) { logger.error(e); } finally { // remove any temp tables we created removeFilterTable(sessionId); } } }); reprocessThread.start(); } catch (ControllerException e) { throw new ControllerException(e); } } public void processMessage(MessageObject message) throws ControllerException { try { VMRouter router = new VMRouter(); router.routeMessageByChannelId(message.getChannelId(), message, true); } catch (Exception e) { throw new ControllerException("could not reprocess message", e); } } private Map<String, Object> getFilterMap(MessageObjectFilter filter, String uid) { Map<String, Object> parameterMap = new HashMap<String, Object>(); if (uid != null) { parameterMap.put("uid", uid); } parameterMap.put("id", filter.getId()); parameterMap.put("correlationId", filter.getCorrelationId()); parameterMap.put("channelId", filter.getChannelId()); parameterMap.put("status", filter.getStatus()); parameterMap.put("type", filter.getType()); parameterMap.put("status", filter.getStatus()); parameterMap.put("connectorName", filter.getConnectorName()); parameterMap.put("protocol", filter.getProtocol()); parameterMap.put("source", filter.getSource()); parameterMap.put("searchCriteria", filter.getSearchCriteria()); parameterMap.put("searchRawData", filter.isSearchRawData()); parameterMap.put("searchTransformedData", filter.isSearchTransformedData()); parameterMap.put("searchEncodedData", filter.isSearchEncodedData()); parameterMap.put("searchErrors", filter.isSearchErrors()); parameterMap.put("quickSearch", filter.getQuickSearch()); parameterMap.put("ignoreQueued", filter.isIgnoreQueued()); parameterMap.put("channelIdList", filter.getChannelIdList()); if (filter.getStartDate() != null) { parameterMap.put("startDate", String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", filter.getStartDate())); } if (filter.getEndDate() != null) { parameterMap.put("endDate", String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", filter.getEndDate())); } return parameterMap; } public MessageObject cloneMessageObjectForBroadcast(MessageObject messageObject, String connectorName) { MessageObject clone = new MessageObject(); // We could use deep copy here, but see the notes below // look up ID based on connector name + correlation ID String existingId = null; if ((messageObject.getContext().get("replace") != null) && messageObject.getContext().get("replace").equals("true")) { clone.getContext().put("replace", "true"); try { existingId = lookupMessageId(messageObject.getId(), connectorName); } catch (Exception e) { logger.error("Could not locate existing message", e); } } if (existingId != null) { clone.setId(existingId); } else { clone.setId(UUIDGenerator.getUUID()); } clone.setServerId(configurationController.getServerId()); clone.setDateCreated(Calendar.getInstance()); clone.setCorrelationId(messageObject.getId()); clone.setConnectorName(connectorName); clone.setRawData(messageObject.getEncodedData()); clone.setResponseMap(messageObject.getResponseMap()); clone.setChannelMap(messageObject.getChannelMap()); clone.setChannelId(messageObject.getChannelId()); clone.setAttachment(messageObject.isAttachment()); return clone; } private String lookupMessageId(String correlationId, String destinationId) throws SQLException { Map<String, Object> params = new HashMap<String, Object>(); params.put("correlationId", correlationId); params.put("connectorName", destinationId); return (String) SqlConfig.getSqlMapClient().queryForObject("Message.lookupMessageId", params); } private String lookupMessageStatus(String messageId) throws SQLException { return (String) SqlConfig.getSqlMapClient().queryForObject("Message.lookupMessageStatus", messageId); } public MessageObject getMessageObjectFromEvent(UMOEvent event) throws Exception { MessageObject messageObject = null; Object incomingData = event.getTransformedMessage(); if ((incomingData == null) || !(incomingData instanceof MessageObject)) { logger.warn("received data is not of expected type"); return null; } messageObject = (MessageObject) incomingData; if (messageObject.getStatus().equals(MessageObject.Status.FILTERED)) { return null; } return messageObject; } public void setError(MessageObject messageObject, String errorType, String errorMessage, Throwable e, Object payload) { String fullErrorMessage = errorBuilder.buildErrorMessage(errorType, errorMessage, e); // send alert // Set the errors on the MO if (messageObject != null) { messageObject .setErrors( messageObject.getErrors() != null ? messageObject.getErrors() + System.getProperty("line.separator") + System.getProperty("line.separator") + fullErrorMessage : fullErrorMessage); } // Set the response error String responseException = new String(); if (e != null) { responseException = "\t" + e.getClass().getSimpleName() + "\t" + e.getMessage(); } setStatus(messageObject, MessageObject.Status.ERROR, Response.Status.FAILURE, errorMessage + responseException, payload); } public void setSuccess(MessageObject messageObject, String responseMessage, Object payload) { setStatus(messageObject, MessageObject.Status.SENT, Response.Status.SUCCESS, responseMessage, payload); } public void setTransformed(MessageObject messageObject, Object payload) { setStatus(messageObject, MessageObject.Status.TRANSFORMED, Response.Status.SUCCESS, new String(), payload); } public void setQueued(MessageObject messageObject, String responseMessage, Object payload) { // queued messages are stored into a persistence media, so their socket // element should be removed if (messageObject.getChannelMap().containsKey(RECEIVE_SOCKET)) { Object socketObj = messageObject.getChannelMap().get(RECEIVE_SOCKET); messageObject.getChannelMap().put(RECEIVE_SOCKET, socketObj.toString()); } setStatus(messageObject, MessageObject.Status.QUEUED, Response.Status.QUEUED, responseMessage, payload); } public void setFiltered(MessageObject messageObject, String responseMessage, Object payload) { setStatus(messageObject, MessageObject.Status.FILTERED, Response.Status.FILTERED, responseMessage, payload); } private void setStatus(MessageObject messageObject, MessageObject.Status newStatus, Response.Status responseStatus, String responseMessage, Object payload) { if ((messageObject.getResponseMap() != null) && !messageObject.getConnectorName().equalsIgnoreCase("source")) { messageObject.getResponseMap().put(messageObject.getConnectorName(), new Response(responseStatus, responseMessage, payload)); } MessageObject.Status oldStatus = MessageObject.Status.valueOf(messageObject.getStatus().toString()); messageObject.setStatus(newStatus); if (oldStatus.equals(MessageObject.Status.QUEUED) && !newStatus.equals(MessageObject.Status.QUEUED)) { statisticsController.decrementQueuedCount(messageObject.getChannelId()); } boolean replace = oldStatus.equals(MessageObject.Status.QUEUED) || ((messageObject.getContext().get("replace") != null) && messageObject.getContext().get("replace").equals("true")); if (replace) { if ((messageObject.getContext() != null && messageObject.getContext().get("replace") != null) && messageObject.getContext().get("replace").equals("true")) { try { String existingId = null; if (!messageObject.getConnectorName().equalsIgnoreCase("source")) { existingId = lookupMessageId(messageObject.getCorrelationId(), messageObject.getConnectorName()); } else { existingId = messageObject.getId(); } if (existingId != null) { String messageStatus = lookupMessageStatus(existingId); // The message status will be null if the destination // message was // reprocessed without the associated source message // being in the database if (messageStatus != null) { oldStatus = MessageObject.Status.valueOf(lookupMessageStatus(existingId)); } } else { oldStatus = null; } } catch (Exception e) { logger.error("Could not locate existing message", e); } } if (oldStatus != null && oldStatus.equals(MessageObject.Status.ERROR)) { statisticsController.decrementErrorCount(messageObject.getChannelId()); } else if (oldStatus != null && oldStatus.equals(MessageObject.Status.FILTERED)) { statisticsController.decrementFilteredCount(messageObject.getChannelId()); } else if (oldStatus != null && oldStatus.equals(MessageObject.Status.TRANSFORMED)) { statisticsController.decrementReceivedCount(messageObject.getChannelId()); } else if (oldStatus != null && oldStatus.equals(MessageObject.Status.SENT)) { statisticsController.decrementSentCount(messageObject.getChannelId()); } } updateMessage(messageObject, replace); } public void resetQueuedStatus(MessageObject messageObject) { if (messageObject != null) { messageObject.setStatus(Status.QUEUED); updateMessage(messageObject, true); statisticsController.decrementErrorCount(messageObject.getChannelId()); } } public Attachment getAttachment(String attachmentId) throws ControllerException { try { return (Attachment) SqlConfig.getSqlMapClient().queryForObject("Message.getAttachment", attachmentId); } catch (Exception e) { throw new ControllerException(e); } } /** * Returns messages that match either the message id or message correlation * id. * * @param message * @return * @throws ControllerException */ public List<Attachment> getAttachmentsByMessage(MessageObject messageObject) throws ControllerException { List<Attachment> attachments = new ArrayList<Attachment>(); try { if (StringUtils.isNotEmpty(messageObject.getCorrelationId())) { attachments.addAll(SqlConfig.getSqlMapClient().queryForList("Message.getAttachmentsByMessageId", messageObject.getCorrelationId())); } attachments.addAll(SqlConfig.getSqlMapClient().queryForList("Message.getAttachmentsByMessageId", messageObject.getId())); } catch (Exception e) { throw new ControllerException(e); } return attachments; } public List<Attachment> getAttachmentsByMessageId(String messageId) throws ControllerException { try { return SqlConfig.getSqlMapClient().queryForList("Message.getAttachmentsByMessageId", messageId); } catch (Exception e) { throw new ControllerException(e); } } public List<Attachment> getAttachmentIdsByMessageId(String messageId) throws ControllerException { try { return SqlConfig.getSqlMapClient().queryForList("Message.getAttachmentIdsByMessageId", messageId); } catch (Exception e) { throw new ControllerException(e); } } public void insertAttachment(Attachment attachment) { try { SqlConfig.getSqlMapClient().insert("Message.insertAttachment", attachment); } catch (SQLException e) { logger.error("could not insert attachment: id=" + attachment.getAttachmentId(), e); } } public void deleteAttachments(MessageObject message) { try { SqlConfig.getSqlMapClient().delete("Message.deleteAttachments", message); } catch (SQLException e) { logger.error("could not delete attachment: message id=" + message.getId(), e); } } public void deleteUnusedAttachments() { try { SqlConfig.getSqlMapClient().delete("Message.deleteUnusedAttachments"); } catch (SQLException e) { logger.error("problem deleting unused attachments", e); } } public Attachment createAttachment(Object data, String type) throws UnsupportedDataTypeException { byte[] byteData; if (data instanceof byte[]) { byteData = (byte[]) data; } else if (data instanceof String) { byteData = ((String) data).getBytes(); } else { throw new UnsupportedDataTypeException("Attachment can be of type String or byte[]"); } Attachment attachment = new Attachment(); attachment.setAttachmentId(UUIDGenerator.getUUID()); attachment.setData(byteData); attachment.setSize(byteData.length); attachment.setType(type); return attachment; } public Attachment createAttachment(Object data, String type, MessageObject messageObject) throws UnsupportedDataTypeException { Attachment attachment = createAttachment(data, type); setAttachmentMessageId(messageObject, attachment); return attachment; } public void setAttachmentMessageId(MessageObject messageObject, Attachment attachment) { attachment.setMessageId(messageObject.getId()); // MIRTH-602 --- The following block of code sets the attachment message // ID to be the source message id in case we are not storing messages. // This will cause all message attachments to be removed in // JavaScriptPostProcessor. Otherwise we will have dangling attachments // in the DB . if (messageObject.getCorrelationId() != null && !messageObject.getCorrelationId().equals("")) { Channel channel = ControllerFactory.getFactory().createChannelController() .getDeployedChannelById(messageObject.getChannelId()); if (channel != null && channel.getProperties().containsKey("store_messages")) { if (channel.getProperties().get("store_messages").equals("false") || (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("error_messages_only").equals("true") && !messageObject.getStatus().equals(MessageObject.Status.ERROR)) || (channel.getProperties().get("store_messages").equals("true") && channel.getProperties().get("dont_store_filtered").equals("true") && messageObject.getStatus().equals(MessageObject.Status.FILTERED))) { attachment.setMessageId(messageObject.getCorrelationId()); } } } } }