/*
* Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The
* University of Hong Kong (HKU). All Rights Reserved.
*
* This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1]
*
* [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
package hk.hku.cecid.ebms.spa.handler;
import hk.hku.cecid.ebms.pkg.EbxmlMessage;
import hk.hku.cecid.ebms.pkg.MessageHeader;
import hk.hku.cecid.ebms.pkg.MessageOrder;
import hk.hku.cecid.ebms.pkg.PayloadContainer;
import hk.hku.cecid.ebms.pkg.MessageHeader.PartyId;
import hk.hku.cecid.ebms.spa.EbmsProcessor;
import hk.hku.cecid.ebms.spa.EbmsUtility;
import hk.hku.cecid.ebms.spa.dao.MessageDAO;
import hk.hku.cecid.ebms.spa.dao.MessageDVO;
import hk.hku.cecid.ebms.spa.dao.MessageServerDAO;
import hk.hku.cecid.ebms.spa.dao.OutboxDVO;
import hk.hku.cecid.ebms.spa.dao.PartnershipDAO;
import hk.hku.cecid.ebms.spa.dao.PartnershipDVO;
import hk.hku.cecid.ebms.spa.dao.RepositoryDVO;
import hk.hku.cecid.ebms.spa.listener.EbmsRequest;
import hk.hku.cecid.ebms.spa.listener.EbmsResponse;
import hk.hku.cecid.piazza.commons.dao.DAOException;
import hk.hku.cecid.piazza.commons.message.Message;
import hk.hku.cecid.piazza.commons.soap.SOAPRequest;
import hk.hku.cecid.piazza.commons.soap.WebServicesRequest;
import hk.hku.cecid.piazza.commons.util.Generator;
import hk.hku.cecid.piazza.commons.net.HostInfo;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.xml.soap.SOAPException;
/**
* @author Donahue Sze
*
*/
public class OutboundMessageProcessor {
static OutboundMessageProcessor outboundMessageProcessor;
static boolean outboundMessageProcessor_initFlag = false;
public synchronized static OutboundMessageProcessor getInstance() {
if (!outboundMessageProcessor_initFlag) {
outboundMessageProcessor = new OutboundMessageProcessor();
outboundMessageProcessor_initFlag = true;
}
return outboundMessageProcessor;
}
private OutboundMessageProcessor() {
}
public void processOutgoingMessage(EbmsRequest request,
EbmsResponse response) throws MessageServiceHandlerException {
EbxmlMessage ebxmlMsg;
try {
ebxmlMsg = request.getMessage();
// generate message id if it does not exist
if (ebxmlMsg.getMessageHeader().getMessageId() == null) {
String messageId = Generator.generateMessageID();
ebxmlMsg.getMessageHeader().setMessageId(messageId);
EbmsProcessor.core.log.info("Genereating message id: " + messageId);
}
// classify where the message come from
Object requestSource = request.getSource();
if (requestSource != null) {
boolean validMessage;
if (requestSource instanceof SOAPRequest) {
validMessage = handleSOAPRequest((SOAPRequest) requestSource, ebxmlMsg);
} else if (requestSource instanceof WebServicesRequest) {
validMessage = handleWebServiceRequest((WebServicesRequest) requestSource, ebxmlMsg);
} else if(requestSource instanceof Message) {
validMessage = handleMessageRequest((Message)requestSource, ebxmlMsg);
} else {
validMessage = false;
}
if (!validMessage) {
throw new RuntimeException(
"Outbound Message Processor - invalid messsage: "
+ ebxmlMsg.getMessageId());
}
} else {
// msh generate reply msg (without source, no content type)
// such as acknowledgement, error message
storeOutgoingMessage(ebxmlMsg, null);
}
} catch (Exception e) {
throw new MessageServiceHandlerException("Error in processing outgoing message", e);
}
}
private boolean handleMessageRequest(Message requestSource, EbxmlMessage ebxmlMsg) throws DAOException, MessageServiceHandlerException, SOAPException {
// String contentType = (String)requestSource.getHeader().get("content-type");
// MessageClassifier messageClassifier = new MessageClassifier(ebxmlMsg);
// if (messageClassifier.isMessageOrder()) {
// storeOutgoingOrderedMessage(ebxmlMsg, contentType);
// } else {
// storeOutgoingMessage(ebxmlMsg, contentType);
// }
generateAndStoreEbxmlMessage(ebxmlMsg);
return true;
}
/**
* Handle message came from EbmsOutboundListener
*
* @param request
* @return true if valid message, otherwise false
* @throws DAOException
* @throws MessageServiceHandlerException
* @throws SOAPException
*/
private boolean handleSOAPRequest(SOAPRequest soapRequest, EbxmlMessage ebxmlMsg)
throws DAOException, MessageServiceHandlerException, SOAPException {
if (!(soapRequest.getSource() instanceof HttpServletRequest)) {
return false;
}
HttpServletRequest httpServletRequest = (HttpServletRequest) soapRequest.getSource();
if (httpServletRequest.getUserPrincipal() == null) {
return false;
}
String contentType = httpServletRequest.getHeader("content-type");
MessageClassifier messageClassifier = new MessageClassifier(ebxmlMsg);
if (messageClassifier.isMessageOrder()) {
storeOutgoingOrderedMessage(ebxmlMsg, contentType);
} else {
storeOutgoingMessage(ebxmlMsg, contentType);
}
return true;
}
/**
* Handle message came from EbmsMessageSenderService
*
* @param request
* @return true if valid message, otherwise false
* @throws DAOException
* @throws MessageServiceHandlerException
* @throws SOAPException
*/
private boolean handleWebServiceRequest(WebServicesRequest wsRequest, EbxmlMessage ebxmlMsg)
throws DAOException, MessageServiceHandlerException, SOAPException {
if (wsRequest.getSource() instanceof SOAPRequest) {
SOAPRequest soapRequest = (SOAPRequest) wsRequest.getSource();
if (soapRequest.getSource() instanceof HttpServletRequest) {
generateAndStoreEbxmlMessage(ebxmlMsg);
return true;
}
}
return false;
}
/**
* @param ebxmlRequestMessage
* @param principalId
* @param contentType
* @throws DAOException
* @throws SOAPException
* @throws MessageServiceHandlerException
*/
private synchronized void storeOutgoingOrderedMessage(
EbxmlMessage ebxmlRequestMessage, String contentType)
throws DAOException, SOAPException, MessageServiceHandlerException {
MessageDAO messageDAO = (MessageDAO) EbmsProcessor.core.dao
.createDAO(MessageDAO.class);
// add the sequence no if necessary
// for auto generating ebxmlmessage by server
if (ebxmlRequestMessage.getMessageOrder() == null) {
MessageDVO messageCPADVO = (MessageDVO) messageDAO.createDVO();
messageCPADVO.setMessageBox(MessageClassifier.MESSAGE_BOX_OUTBOX);
messageCPADVO.setCpaId(ebxmlRequestMessage.getCpaId());
messageCPADVO.setService(ebxmlRequestMessage.getService());
messageCPADVO.setAction(ebxmlRequestMessage.getAction());
messageCPADVO.setConvId(ebxmlRequestMessage.getConversationId());
int previousMaxSequenceNo = messageDAO
.findMaxSequenceNoByMessageBoxAndCpa(messageCPADVO);
int status = (previousMaxSequenceNo == -1) ? MessageOrder.STATUS_RESET
: MessageOrder.STATUS_CONTINUE;
int currentSequenceNo = previousMaxSequenceNo + 1;
if (previousMaxSequenceNo >= 9000000) {
EbmsProcessor.core.log.debug("Try to reset the sequence");
if (isResetAllowed(messageCPADVO)) {
status = MessageOrder.STATUS_RESET;
currentSequenceNo = 0;
EbmsProcessor.core.log.debug("Reset the sequence allowed");
} else {
EbmsProcessor.core.log.debug("Reset the sequence not allowed");
}
}
EbmsProcessor.core.log.debug("Ordered message ("
+ ebxmlRequestMessage.getMessageId()
+ ") with sequence no: " + currentSequenceNo);
ebxmlRequestMessage.addMessageOrder(status, currentSequenceNo);
}
// message type classification
MessageClassifier messageClassifier = new MessageClassifier(ebxmlRequestMessage);
String messageType = messageClassifier.getMessageType();
EbxmlMessageDAOConvertor message = new EbxmlMessageDAOConvertor(
ebxmlRequestMessage,
MessageClassifier.MESSAGE_BOX_OUTBOX,
messageType);
MessageDVO messageDVO = message.getMessageDVO();
messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_PENDING);
// update the sequence group
int currentMaxSequenceGroup = messageDAO
.findMaxSequenceGroupByMessageBoxAndCpa(messageDVO);
if (messageClassifier.isSeqeunceStatusReset()) {
currentMaxSequenceGroup++;
EbmsProcessor.core.log
.debug("Ordered RESET message with new sequence group "
+ currentMaxSequenceGroup + " for message: "
+ ebxmlRequestMessage.getMessageId());
} else {
EbmsProcessor.core.log.debug("Ordered message with sequence group "
+ currentMaxSequenceGroup + " for message: "
+ ebxmlRequestMessage.getMessageId());
}
messageDVO.setSequenceGroup(currentMaxSequenceGroup);
RepositoryDVO repositoryDVO = message.getRepositoryDVO();
if (contentType != null) {
repositoryDVO.setContentType(contentType);
}
OutboxDVO outboxDVO = message.getOutboxDVO();
outboxDVO.setHostname(HostInfo.GetLocalhostAddress());
MessageServerDAO messageServerDAO = (MessageServerDAO) EbmsProcessor.core.dao
.createDAO(MessageServerDAO.class);
messageServerDAO.storeOutboxMessage(messageDVO, repositoryDVO,
outboxDVO, null);
EbmsProcessor.core.log.info("Store outgoing ordered message: "
+ ebxmlRequestMessage.getMessageId());
}
/**
* @param messageCPADVO
* @return
* @throws DAOException
*/
private boolean isResetAllowed(MessageDVO messageCPADVO)
throws DAOException {
MessageDAO messageDAO = (MessageDAO) EbmsProcessor.core.dao
.createDAO(MessageDAO.class);
messageCPADVO
.setStatus(MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE);
// find all the failed outbox sequence messageDVO using cpa
List<?> failedList = messageDAO
.findOrderedMessagesByMessageBoxAndCpaAndStatus(messageCPADVO);
if (failedList.size() != 0) {
return false;
}
messageCPADVO.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSING);
// find all the processing outbox sequence messageDVO using cpa
List<?> processingList = messageDAO
.findOrderedMessagesByMessageBoxAndCpaAndStatus(messageCPADVO);
// the number of processing sequence msg more than 1
if (processingList.size() > 1) {
return false;
}
messageCPADVO
.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSED_ERROR);
// find all the processing outbox sequence messageDVO using cpa
List<?> processedErrorList = messageDAO
.findOrderedMessagesByMessageBoxAndCpaAndStatus(messageCPADVO);
// the number of processing sequence msg more than 1
if (processedErrorList.size() > 1) {
return false;
}
return true;
}
private void storeOutgoingMessage(EbxmlMessage ebxmlRequestMessage,
String contentType) throws DAOException {
storeOutgoingMessage(ebxmlRequestMessage, contentType, null);
}
private void storeOutgoingMessage(EbxmlMessage ebxmlRequestMessage,
String contentType, MessageDVO primalMsgDVO) throws DAOException {
// message type classification
MessageClassifier messageClassifier = new MessageClassifier(
ebxmlRequestMessage);
String messageType = messageClassifier.getMessageType();
MessageServerDAO dao = (MessageServerDAO) EbmsProcessor.core.dao
.createDAO(MessageServerDAO.class);
EbxmlMessageDAOConvertor message = new EbxmlMessageDAOConvertor(
ebxmlRequestMessage, MessageClassifier.MESSAGE_BOX_OUTBOX,
messageType);
MessageDVO messageDVO = message.getMessageDVO();
messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_PENDING);
if (null != primalMsgDVO) {
messageDVO.setPrimalMessageId(primalMsgDVO.getMessageId());
}
RepositoryDVO repositoryDVO = message.getRepositoryDVO();
if (contentType != null) {
repositoryDVO.setContentType(contentType);
}
OutboxDVO outboxDVO = message.getOutboxDVO();
outboxDVO.setHostname(HostInfo.GetLocalhostAddress());
dao.storeOutboxMessage(messageDVO, repositoryDVO, outboxDVO, primalMsgDVO);
EbmsProcessor.core.log.info("Store outgoing message: "
+ ebxmlRequestMessage.getMessageId());
}
private void generateAndStoreEbxmlMessage(EbxmlMessage ebxmlRequestMessage)
throws DAOException, SOAPException, MessageServiceHandlerException {
generateAndStoreEbxmlMessage(ebxmlRequestMessage, null);
}
private void generateAndStoreEbxmlMessage(
EbxmlMessage ebxmlRequestMessage, MessageDVO primalMsgDVO)
throws DAOException, SOAPException, MessageServiceHandlerException {
// find the cpa and set the related element in the ebxmlMessage
PartnershipDAO partnershipDAO = (PartnershipDAO) EbmsProcessor.core.dao
.createDAO(PartnershipDAO.class);
PartnershipDVO partnershipDVO = (PartnershipDVO) partnershipDAO
.createDVO();
partnershipDVO.setCpaId(ebxmlRequestMessage.getCpaId());
partnershipDVO.setService(ebxmlRequestMessage.getService());
partnershipDVO.setAction(ebxmlRequestMessage.getAction());
if (!partnershipDAO.findPartnershipByCPA(partnershipDVO)) {
EbmsProcessor.core.log.error("Partnership not found");
throw new MessageServiceHandlerException("Partnership not found");
}
// Set sync reply
String syncReplyMode = partnershipDVO.getSyncReplyMode();
if (null != syncReplyMode && "mshSignalsOnly".equalsIgnoreCase(syncReplyMode)) {
ebxmlRequestMessage.addSyncReply();
}
// Set ack requested
String ackRequested = partnershipDVO.getAckRequested();
if (null != ackRequested && "always".equalsIgnoreCase(ackRequested)) {
String ackSignRequested = partnershipDVO.getAckSignRequested();
if (null != ackSignRequested && "always".equalsIgnoreCase(ackSignRequested)) {
ebxmlRequestMessage.addAckRequested(true);
} else {
ebxmlRequestMessage.addAckRequested(false);
}
}
// Set message order and duplicate elimination
String messageOrder = partnershipDVO.getMessageOrder();
if (null != messageOrder && "Guaranteed".equalsIgnoreCase(messageOrder)) {
// if msg order is on, find the next sequence no
storeOutgoingOrderedMessage(ebxmlRequestMessage, null);
} else {
if ("always".equalsIgnoreCase(partnershipDVO.getDupElimination())) {
ebxmlRequestMessage.getMessageHeader().setDuplicateElimination();
}
storeOutgoingMessage(ebxmlRequestMessage, null, primalMsgDVO);
}
}
public EbxmlMessage resendAsNew(String primalMessageId) throws Exception {
MessageDAO messageDAO = (MessageDAO) EbmsProcessor.core.dao.createDAO(MessageDAO.class);
MessageDVO primalMsgDVO = (MessageDVO) messageDAO.createDVO();
primalMsgDVO.setMessageId(primalMessageId);
primalMsgDVO.setMessageBox(MessageClassifier.MESSAGE_BOX_OUTBOX);
if (!messageDAO.retrieve(primalMsgDVO)) {
throw new Exception("No message found - Message Id : " + primalMessageId + ", Message Box : " + MessageClassifier.MESSAGE_BOX_OUTBOX);
}
String primalStatus = primalMsgDVO.getStatus();
if (MessageClassifier.INTERNAL_STATUS_PENDING.equals(primalStatus) || MessageClassifier.INTERNAL_STATUS_PROCESSING.equals(primalStatus)) {
throw new Exception("Message can only be resent as new when its status is not Pending or Processing");
}
// Check message type
EbxmlMessage oldEbxmlMessage = EbxmlMessageDAOConvertor.getEbxmlMessage(primalMessageId, MessageClassifier.MESSAGE_BOX_OUTBOX);
MessageClassifier msgClassifier = new MessageClassifier(oldEbxmlMessage);
String messageType = msgClassifier.getMessageType();
if (MessageClassifier.MESSAGE_TYPE_ACKNOWLEDGEMENT.equals(messageType)) {
throw new Exception("Acknowledgement cannot be resent as new");
} else if (MessageClassifier.MESSAGE_TYPE_ERROR.equals(messageType)) {
throw new Exception("Error message cannot be resent as new");
}
// Retrieve partnership
String partnershipId = primalMsgDVO.getPartnershipId();
if (null == partnershipId) {
throw new Exception("Undefined partnership");
}
PartnershipDAO partnershipDAO = (PartnershipDAO) EbmsProcessor.core.dao.createDAO(PartnershipDAO.class);
PartnershipDVO partnershipDVO = (PartnershipDVO) partnershipDAO.createDVO();
partnershipDVO.setPartnershipId(partnershipId);
if (!partnershipDAO.retrieve(partnershipDVO)) {
throw new Exception("No partnership [" + partnershipId + "] is found");
}
// Check partnership setting
if ("Guaranteed".equalsIgnoreCase(partnershipDVO.getMessageOrder())) {
throw new Exception("Cannot resend as new when Message Order is set to \"Guaranteed\"");
}
primalMsgDVO.setHasResendAsNew("true");
EbxmlMessage newEbxmlMessage = createNewEbxmlMessage(oldEbxmlMessage, partnershipDVO);
generateAndStoreEbxmlMessage(newEbxmlMessage, primalMsgDVO);
return newEbxmlMessage;
}
private EbxmlMessage createNewEbxmlMessage(
EbxmlMessage originalMessage,
PartnershipDVO partnershipDVO) throws Exception {
try {
EbxmlMessage newMessage = new EbxmlMessage();
MessageHeader msgHeader = newMessage.addMessageHeader();
msgHeader.setCpaId(partnershipDVO.getCpaId());
msgHeader.setConversationId(originalMessage.getConversationId());
msgHeader.setService(partnershipDVO.getService());
msgHeader.setAction(partnershipDVO.getAction());
String serviceType = originalMessage.getServiceType();
if (serviceType != null && !serviceType.equals("")) {
msgHeader.setServiceType(serviceType);
}
String messageId = Generator.generateMessageID();
msgHeader.setMessageId(messageId);
EbmsProcessor.core.log.info("Genereating message id: " + messageId);
msgHeader.setTimestamp(EbmsUtility.getCurrentUTCDateTime());
Iterator<?> iter;
iter = originalMessage.getFromPartyIds();
while (iter.hasNext()) {
PartyId partyId = (PartyId) iter.next();
msgHeader.addFromPartyId(partyId.getId(), partyId.getType());
}
iter = originalMessage.getToPartyIds();
while (iter.hasNext()) {
PartyId partyId = (PartyId) iter.next();
msgHeader.addToPartyId(partyId.getId(), partyId.getType());
}
iter = originalMessage.getPayloadContainers();
while (iter.hasNext()) {
PayloadContainer payloadContainer = (PayloadContainer) iter
.next();
newMessage.addPayloadContainer(payloadContainer
.getDataHandler(), payloadContainer.getContentId(),
null);
}
return newMessage;
} catch (Exception e) {
EbmsProcessor.core.log.error("Error in constructing ebxml message",
e);
throw new Exception("Error in constructing ebxml message", e);
}
}
}