/*
* 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.task;
import hk.hku.cecid.ebms.pkg.Description;
import hk.hku.cecid.ebms.pkg.EbxmlMessage;
import hk.hku.cecid.ebms.pkg.ErrorList;
import hk.hku.cecid.ebms.pkg.SignatureHandler;
import hk.hku.cecid.ebms.pkg.validation.EbxmlMessageValidator;
import hk.hku.cecid.ebms.spa.EbmsProcessor;
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.OutboxDAO;
import hk.hku.cecid.ebms.spa.dao.OutboxDVO;
import hk.hku.cecid.ebms.spa.handler.EbxmlMessageDAOConvertor;
import hk.hku.cecid.ebms.spa.handler.MessageClassifier;
import hk.hku.cecid.ebms.spa.handler.MessageServiceHandler;
import hk.hku.cecid.ebms.spa.handler.MessageServiceHandlerException;
import hk.hku.cecid.ebms.spa.handler.SignalMessageGenerator;
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.module.ActiveTask;
import hk.hku.cecid.piazza.commons.security.SMimeMessage;
import hk.hku.cecid.piazza.commons.security.TrustedHostnameVerifier;
import hk.hku.cecid.piazza.commons.soap.SOAPHttpConnector;
import hk.hku.cecid.piazza.commons.soap.SOAPMailSender;
import hk.hku.cecid.piazza.commons.util.StringUtilities;
import hk.hku.cecid.piazza.commons.net.HostInfo;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.sql.Timestamp;
import java.util.Iterator;
import java.util.List;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
/**
* @author Donahue Sze, Twinsen Tsang (modifiers)
*
*/
public class OutboxTask implements ActiveTask {
// The error message when internal error thrown.
private static String ERROR_TYPE_INTERNAL_ERROR = "internal_error";
// The error message when delivery failure (communication protocol error)
private static String ERROR_TYPE_DELIVERY_FAILTURE = "delivery_failure";
// The error message when the message is tried to deliver exceeding the retries times.
private static String ERROR_TYPE_MAXIMUM_RETRIES_REACHED = "maximum_retries_reached";
/**
* The DAO for accessing the DVO Format of <code>EbxmlMessage</code>
*/
private MessageDAO messageDAO;
/**
* The DVO Format representing <code>EbXML Message</code> needed
* to deliver in this task.
*/
private MessageDVO messageDVO;
/**
* The flag indicating whether the active task is able to
* retry (repeat execution again).
*/
private boolean retryEnabled = true;
/**
* The flag indicating whether the <code>EbXML Message</code> requests
* recipient acknowledgment for reliable messaging.
*/
private boolean isAckRequested;
/**
* The flag indicating whether the <code>EbXML Message</code> requested
* for processing has been processed by other thread.
*/
private boolean isProcessedAlready = false;
/**
* The number of times which the <code>EbXML Message</code> can be attempted
* to deliver to receipient.<br><br/>
*
* It is calculated by maximum retries defined in the partnership agreement
* for this message and minus 1 plus 1 non-retry delivery attempt.
*/
private int maxAllowedAttempt = 0;
/**
* The number of times attempted to deliver the <code>EbXML Message</code>. This
* value should be less than {@link #maxAllowedAttempt}.
*/
private int attempted = 0;
/**
* The number of times that the active task has been repeated to execute. This
* value does not relate to the <em>Retries</em> of the <code>EbXML Message</code>,
* it is just a counter counting how many times the {@link #execute()} has been
* invoked in this task. The scope is local and reset after thread terminate.
*/
private int localRetried = 0;
/**
* The agreement validator for the <code>EbXML Message</code> and it's corresponding
* partnership.
*/
private AgreementHandler outboxAgreement;
private String errorMessage = null;
private String contentType;
/**
* Explicit Constructor.
*
* @param message
*/
public OutboxTask(MessageDVO message){
this.messageDVO = message;
// Update the messageDVO status to 'Processing' (PR)
try {
this.messageDAO = (MessageDAO) EbmsProcessor.core.dao.createDAO(MessageDAO.class);
message.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSING);
this.messageDAO.updateMessage(message);
// get the agreement parameters and check by agreement guard
boolean isOutboundAgreementCheck = MessageServiceHandler.getInstance()
.isOutboundAgreementCheck();
if (isOutboundAgreementCheck) {
EbmsProcessor.core.log.info("Outbound agreement checking for interop");
outboxAgreement = new AgreementHandler(message, true);
} else {
outboxAgreement = new AgreementHandler(message, false);
}
// Get smtp parameters if send by mail
if (outboxAgreement.getToPartyProtocol().equalsIgnoreCase("mailto")) {
MessageServiceHandler msh = MessageServiceHandler.getInstance();
if (!msh.isHasSmtp()) {
throw new DeliveryException(
"No smtp specified in ebms system properties, cannot delivery msg: "
+ message.getMessageId());
}
// smtp does not support sync reply
if (message.getSyncReply().equals("true")) {
throw new DeliveryException(
"Smtp does not support sync reply, cannot delivery msg: "
+ message.getMessageId());
}
}
// reset guard - reset is not allowed if previous msg have not ack
if (message.getSequenceStatus() == 0) {
checkResetIsAllow(message);
}
this.isAckRequested = this.messageDVO.getAckRequested().equals("true");
} catch (MessageValidationException mve) {
errorMessage = mve.getMessage();
EbmsProcessor.core.log.error("Message Validation Exception: "
+ messageDVO.getMessageId(), mve);
} catch (Throwable e) {
errorMessage = "Internal Server Error: " + e;
EbmsProcessor.core.log.error("Internal Server Error: ", e);
}
}
/**
* Get the maximum execution times for this task. It returns the total
* number of times that the <code>EbXML Message</code> can be attempted to deliver
* (including non-retry delivery).<br/><br/>
*
* For <code>EbXML Message</code> does not requests ACK, this method always
* return zero because it does not support retry / re-sending schema when
* ACK does not requested. [ebMSS section 6.4.3]
*
* It is calculated by : <br/>
*
* <em> Max(maximum retry defined in partnership for this message - 1,0) + 1<em>.
*
* @see hk.hku.cecid.piazza.commons.module.ActiveTask#getMaxRetries()
* @see hk.hku.cecid.ebms.spa.dao.PartnershipDVO#getRetries()
* @see hk.hku.cecid.ebms.spa.dao.OutboxDVO#getRetried()
*/
public int getMaxRetries()
{
if (this.isAckRequested)
{
// Get max retries in the agreement
try {
OutboxDAO outboxDao = (OutboxDAO) EbmsProcessor.core.dao.createDAO(OutboxDAO.class);
OutboxDVO outboxDVO = (OutboxDVO) outboxDao.createDVO();
outboxDVO.setMessageId(messageDVO.getMessageId());
if (!outboxDao.findOutbox(outboxDVO))
{
//throw new NullPointerException("Unable to retrieve the outbox record for :" + messageDVO.getMessageId());
EbmsProcessor.core.log.warn("Redundant working thread for : " + messageDVO.getMessageId());
/*
* 18/12/2007 Temporary Solution for further guarding the status being overridden by the out-box
* task.
*/
MessageDVO ackDVO = (MessageDVO) messageDAO.createDVO();
ackDVO.setMessageBox (MessageClassifier.MESSAGE_BOX_INBOX);
ackDVO.setMessageType (MessageClassifier.MESSAGE_TYPE_ACKNOWLEDGEMENT);
ackDVO.setRefToMessageId(this.messageDVO.getMessageId());
if (!messageDAO.findRefToMessage(ackDVO))
{
ackDVO.setMessageType(MessageClassifier.MESSAGE_TYPE_ERROR);
if (!messageDAO.findRefToMessage(ackDVO))
{
// Seems impossible ?
EbmsProcessor.core.log.error(
"Redundant working thread, Cannot find the ACK / Error, Update message to PE:" +
this.messageDVO.getMessageId());
this.messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSED_ERROR);
this.messageDVO.setStatusDescription("Cannot find the ACK / Error.");
this.messageDVO.setTimeoutTimestamp(null);
this.messageDAO.updateMessage(this.messageDVO);
}
}
else
{
EbmsProcessor.core.log.warn(
"Redundant working thread, found associated ACK / Error : " + ackDVO.getMessageId());
// Get the ebxml acknowledgment from repository.
EbxmlMessage ebxmlMessage = EbxmlMessageDAOConvertor.getEbxmlMessage(
ackDVO.getMessageId(), MessageClassifier.MESSAGE_BOX_INBOX);
if (ebxmlMessage.getAcknowledgment() != null)
{
EbmsProcessor.core.log.warn(
"Re-update status for message " +
this.messageDVO.getMessageId() +
" to " + MessageClassifier.INTERNAL_STATUS_PROCESSED);
// Update back the status
this.messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSED);
this.messageDVO.setStatusDescription("Acknowledgement is received");
this.messageDVO.setTimeoutTimestamp(null);
this.messageDAO.updateMessage(this.messageDVO);
}
else if (ebxmlMessage.getErrorList() != null)
{
EbmsProcessor.core.log.warn(
"Re-update status for message " +
this.messageDVO.getMessageId() +
" to " + MessageClassifier.INTERNAL_STATUS_PROCESSED_ERROR);
this.messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSED_ERROR);
this.messageDVO.setTimeoutTimestamp(null);
StringBuffer sb = new StringBuffer();
Iterator i = ebxmlMessage.getErrorList().getErrors();
while (i.hasNext())
{
ErrorList.Error error = (ErrorList.Error) i.next();
Description description = error.getDescription();
sb.append(error.getErrorCode() + ": " + description.getDescription());
}
this.messageDVO.setStatusDescription(sb.toString());
this.messageDAO.updateMessage(this.messageDVO);
}
}
/* The message is processed already by other thread. */
this.isProcessedAlready = true;
return 0;
}
// NOTE: Lazy initialization here.
this.maxAllowedAttempt = Math.max(outboxAgreement.getRetries() - 1, 0) + 1;
this.attempted = outboxDVO.getRetried();
} catch (Throwable t) {
EbmsProcessor.core.log.error("Cannot get the maximum retries", t);
}
}
return this.maxAllowedAttempt;
}
/**
* @param retried The number of times that this active task has been retried (local retired times).
*
* @see hk.hku.cecid.piazza.commons.module.ActiveTask#setRetried(int)
*/
public void setRetried(int retried) {
int delta = retried - this.localRetried;
this.attempted += delta;
this.localRetried = retried;
}
/**
* The main execution of <code>OutboxTask</code>. The overview
* procedure is listed in below:
* <ol>
* <li>Extract the <code>EbXMLMessage</code> from the <code><em>messageDVO</em></code>.</li>
* <li>Sign the <code>EbXMLMessage</code> by it's keystore if necessary.</li>
* <li>Send the <code>EbXMLMessage</code> through HTTP/HTTPS/SMTP protocol.</li>
* <li>Update the number of retry attempted to deliver for this <code>EbXMLMessage</code>.</li>
* </ol>
*/
public void execute() throws Exception
{
/*
* Return immediately when the message is processed by others already.
*/
if (this.isProcessedAlready)
{
this.retryEnabled = false;
return ;
}
// Declare share variable during exeuction.
String mID = messageDVO.getMessageId();
// Get the ebxml message and sign the message from repository.
EbxmlMessage ebxmlMessage = EbxmlMessageDAOConvertor.getEbxmlMessage(
mID, MessageClassifier.MESSAGE_BOX_OUTBOX);
// Create MessageServer DAO object.
MessageServerDAO messageServerDAO = (MessageServerDAO) EbmsProcessor.core.dao
.createDAO(MessageServerDAO.class);
try {
checkAndSignEbxmlMessage(ebxmlMessage);
} catch (MessageValidationException mve) {
EbmsProcessor.core.log.error("Cannot get the sign the message: ", mve);
errorMessage = mve.getMessage();
}
// Error occurred when constructing the outbox messageDVO or sign the EbXML message.
if (errorMessage != null) {
try {
EbmsProcessor.core.log.info("Mark as failed (Message id: " + mID + ")");
messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE);
messageDVO.setStatusDescription(errorMessage);
messageServerDAO.clearMessage(messageDVO);
generateErrorMessage(ERROR_TYPE_INTERNAL_ERROR);
} catch (DAOException daoe) {
EbmsProcessor.core.log.error("Cannot generate error message", daoe);
}
retryEnabled = false;
throw new DeliveryException(errorMessage);
}
/*
* Do some post operation when acknowledgment is required.
*/
if (this.isAckRequested)
{
/*
* The sender task does not receive an Acknowledgment Message after the maximum number of retries,
* mark it as failure to receive due to maximum retries has been reached. [ebMSS section 6.5.4]
*/
if ((attempted >= maxAllowedAttempt))
{
this.messageDAO.findMessage(this.messageDVO); // Update it to latest from the persistence DB.
if (this.checkUpdateStatusIsAllow()){
// TODO: The operations below is not atomic and may have race condition between the incoming thread.
EbmsProcessor.core.log.info("Reliable message (" + mID
+ ") - no acknowledgement received until maximum retries");
try {
EbmsProcessor.core.log.info("Mark as failed (Message id: " + mID + ")");
this.messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE);
this.messageDVO.setTimeoutTimestamp(null);
messageServerDAO.clearMessage(messageDVO);
generateErrorMessage(ERROR_TYPE_MAXIMUM_RETRIES_REACHED);
} catch (DAOException e) {
EbmsProcessor.core.log.error("Cannot generate error message", e);
}
} else {
EbmsProcessor.core.log.warn("Reliable message (" + mID
+ ") - Redundant working thread is now terminated");
}
this.retryEnabled = false;
return; // retrun immediately and terminate the thread.
}
/*
* It may be very dangerous when updates the message after delivery because
* it might have two thread (incoming ACK and outbox task) updating status together.
*/
try{
// Update the timeout timestamp for re-sending if necessary [ebMS_v2_0 section 6.5.4]
this.messageDAO.findMessage(this.messageDVO); // Update it to latest from the persistence.
/*
* Only need to set if the message has not reached final status.
* There is a very small possibility that the acknowledgment has been
* received and processed in between the Order message delivery and here.
* So we need to track whether the Order message has been processed before
* setting the timeout timestamp.
*/
if (this.checkUpdateStatusIsAllow()){
Timestamp timeout = new Timestamp(System.currentTimeMillis() + this.getRetryInterval());
this.messageDVO.setTimeoutTimestamp(timeout);
this.messageDAO.updateMessage(this.messageDVO);
}
} catch (DAOException daoe){
String detail = "Cannot update the timeout timestamp (Message id: " + mID + ")";
EbmsProcessor.core.log.error(detail, daoe);
throw new DeliveryException (detail, daoe);
}
}
Exception protocolCommErr = null;
// Send the ebxml message
try {
String protocol = outboxAgreement.getToPartyProtocol();
URL url = outboxAgreement.getToPartyURL();
// Send EbXML Message By HTTP/HTTPS Protocol
if (protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https")){
sendMsgByHttp(url, messageDVO.getSyncReply().equals("true"), ebxmlMessage);
} else
// Send EbXML Message By SMTP Protocol
if (protocol.equalsIgnoreCase("mailto")){
sendMsgBySmtp(url, ebxmlMessage, contentType);
// Unknown protocol.
} else {
throw new DeliveryException("Unknown protocol (" + protocol + ") for delivery.");
}
} catch (DeliveryException de){
protocolCommErr = de;
}
if (!this.isAckRequested)
{
String toStatus = null;
String toStatusDesc = null;
/*
* According to the EbMS specification, there is no send retry / duplication
* schema when no acknowledgment is requested. Mark the messsage record
* to delivery failure (DF) immediately and generate the appropriate error
* message when there is error in delivery.
*/
if (protocolCommErr != null){
EbmsProcessor.core.log.info("Mark as failed (Message id: " + mID + ")");
toStatus = MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE;
toStatusDesc = "Delivery Failure when sending message: "
+ StringUtilities.toString(protocolCommErr);
// Generate the error before mark failure.
generateErrorMessage(ERROR_TYPE_DELIVERY_FAILTURE);
}
/*
* If the EbXML message does not request ACK and no connection exception thrown
* encountered. We assume it is delivered successfully and mark it to (DL).
*/
else {
toStatus = MessageClassifier.INTERNAL_STATUS_DELIVERED;
toStatusDesc = "Message was sent.";
}
// Update the status and clear the message.
try{
this.messageDVO.setStatus(toStatus);
this.messageDVO.setStatusDescription(toStatusDesc);
messageServerDAO.clearMessage(this.messageDVO);
} catch (DAOException daoe){
String detail = "Error in clear the non-reliable message: " + mID;
EbmsProcessor.core.log.error(detail, daoe);
throw new DeliveryException (detail, daoe);
}
}
// If the EbXML message request ACK, update the retried count and timeout time in the outbox.
else if (this.isAckRequested)
{
try {
// TODO: Refactor (prevent create outboxDAO object)
// Update the retries count
OutboxDAO outboxDao = (OutboxDAO) EbmsProcessor.core.dao.createDAO(OutboxDAO.class);
OutboxDVO outboxDVO = (OutboxDVO) outboxDao.createDVO();
outboxDVO.setMessageId(mID);
outboxDVO.setRetried(this.attempted + 1);
outboxDVO.setHostname(HostInfo.GetLocalhostAddress());
outboxDao.updateOutbox(outboxDVO);
} catch (DAOException daoe){
String detail = "Cannont update the retires count (Message id: " + mID + ")";
EbmsProcessor.core.log.error(detail, daoe);
throw new DeliveryException (detail, daoe);
}
}
// Message sent event is fired already in sendMsgByHttp for sync reply
if (messageDVO.getSyncReply().equals("false") && null == protocolCommErr) {
fireMessageSentEvent(ebxmlMessage);
}
// Terminate after this execution.
this.retryEnabled = false;
// Some problem has been captured when delivering ebxml message. throw it and handle by the active task.
if (protocolCommErr != null) throw protocolCommErr;
}
/**
* Sign the <code>EbXML Message</code> by the private key storing in the
* <code>KeyStoreManager</code> if the agreement is requested signing the
* <code>EbXML Message</code>
*
* @param ebxmlMessage The <code>EbXML Message</code> needed to sign.
* @throws MessageValidationException
* Unable to sign the <code>ebxmlMessage</code>.
*/
private void checkAndSignEbxmlMessage(EbxmlMessage ebxmlMessage) throws MessageValidationException {
try {
MessageClassifier messageClassifier = new MessageClassifier(ebxmlMessage);
// sign the order message
if (messageClassifier.getMessageType().equalsIgnoreCase(
MessageClassifier.MESSAGE_TYPE_ORDER)) {
boolean isSign = outboxAgreement.isSign();
if (isSign){
EbmsProcessor.core.log.info("Sign the message: " + ebxmlMessage.getMessageId());
String dsAlgorithm = outboxAgreement.getDsAlgorithm();
String mdAlgorithm = outboxAgreement.getMdAlgorithm();
SignatureHandler signatureHandler = MessageServiceHandler
.createSignatureHandler(ebxmlMessage);
if (MessageServiceHandler.getInstance().isSignHeaderOnly()) {
EbmsProcessor.core.log.info("Sign header only for interop");
signatureHandler.sign(dsAlgorithm, mdAlgorithm, true);
} else {
signatureHandler.sign(dsAlgorithm, mdAlgorithm, false);
}
}
}
} catch (Exception e) {
String detail = "Cannot sign the ebxml message";
// EbmsProcessor.core.log.error(detail, e);
throw new MessageValidationException(detail, e);
}
}
/**
* Deliver the <code>EbXML Message</code> through HTTP protocol.
*
* @param link
*/
private void sendMsgByHttp(URL link, boolean isSyncReply, EbxmlMessage ebxmlMessage) throws DeliveryException {
EbmsProcessor.core.log.info("Send message " + messageDVO.getMessageId() + " to " + link);
HttpURLConnection conn = null;
SOAPMessage reply = null;
try {
SOAPHttpConnector connector = new SOAPHttpConnector(link);
String username = EbmsProcessor.core.properties.getProperty("/ebms/http_basic_auth/username");
String password = EbmsProcessor.core.properties.getProperty("/ebms/http_basic_auth/password");
if(username != null && password != null) {
connector.setUsername(username);
connector.setPassword(password);
}
if (!outboxAgreement.isHostnameVerified()) {
connector.setHostnameVerifier(new TrustedHostnameVerifier());
}
conn = connector.createConnection();
conn.setDoOutput(true);
reply = connector.send(ebxmlMessage.getSOAPMessage(), conn);
} catch (Exception e) {
throw new DeliveryException("Cannot send the message", e);
}
// If it requests sync reply
if (isSyncReply) {
try {
fireMessageSentEvent(ebxmlMessage);
EbxmlMessage replyMsg = new EbxmlMessage(reply);;
EbmsProcessor.core.log.info("Store incoming sync reply message");
// Philip 20081116
// in case sync reply = true and ack requested = false
// this is not allowed in real situation
try {
EbxmlMessageValidator validator = new EbxmlMessageValidator();
validator.validate(replyMsg);
} catch (Exception e) {
EbmsProcessor.core.log.info("Reply message is not a valid ebxml message");
return;
}
EbmsRequest ebmsRequest = new EbmsRequest();
ebmsRequest.setMessage(replyMsg);
EbmsResponse ebmsResponse = new EbmsResponse();
EbxmlMessage ebxmlResponseMessage = new EbxmlMessage();
ebmsResponse.setMessage(ebxmlResponseMessage);
MessageServiceHandler msh = MessageServiceHandler.getInstance();
msh.processInboundMessage(ebmsRequest, ebmsResponse);
} catch (Exception e) {
EbmsProcessor.core.log.error("Cannot convert the reply message", e);
throw new DeliveryException("Cannot convert the reply message", e);
}
}
}
private void sendMsgBySmtp(URL toPartyURL, EbxmlMessage ebxmlMessage,
String contentType) throws DeliveryException, CertificateException,
IOException {
MessageServiceHandler msh = MessageServiceHandler.getInstance();
String toMailAddress = toPartyURL.getPath();
EbmsProcessor.core.log.info("Send message " + messageDVO.getMessageId()
+ " to " + toMailAddress);
SOAPMailSender smtp = new SOAPMailSender(msh.smtpProtocol,
msh.smtpHost, msh.smtpUsername, msh.smtpPassword);
if (!msh.smtpPort.equalsIgnoreCase("")) {
smtp.addProperty("mail.smtp.port", msh.smtpPort);
}
try {
Session session = smtp.createSession();
MimeMessage message = smtp.createMessage(msh.smtpFromMailAddress,
toMailAddress, null, "ebxml",
ebxmlMessage.getSOAPMessage(), session);
message.setHeader(EbxmlMessage.SOAP_ACTION, EbxmlMessage.SOAP_ACTION_VALUE);
if (outboxAgreement.isEncrypt()) {
EbmsProcessor.core.log.info("Encrypt the message");
X509Certificate serverCert = null;
if (outboxAgreement.getEncryptCert() != null) {
ByteArrayInputStream bais = new ByteArrayInputStream(
outboxAgreement.getEncryptCert());
CertificateFactory cf = CertificateFactory
.getInstance("X.509");
serverCert = (X509Certificate) cf.generateCertificate(bais);
bais.close();
bais = null;
} else {
EbmsProcessor.core.log.error("Please upload the cert");
throw new RuntimeException("Please upload the cert");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
message.writeTo(baos);
MimeBodyPart mimeBodyPart = new MimeBodyPart(
new ByteArrayInputStream(baos.toByteArray()));
baos.close();
SMimeMessage smsg = new SMimeMessage(mimeBodyPart, serverCert,
session);
// set encryption algorithm
if (outboxAgreement.getEncryptAlgorithm() != null) {
smsg.setEncryptAlgorithm(outboxAgreement
.getEncryptAlgorithm());
}
MimeBodyPart bp = smsg.encrypt().getBodyPart();
message.setContent(bp.getContent(), bp.getContentType());
}
smtp.send(message);
} catch (Exception e) {
EbmsProcessor.core.log.error("Cannot send the message", e);
throw new DeliveryException("Cannot send the message", e);
}
}
/**
* Invoke when {@link #execute()} throw any kind of uncaught exception.
*/
public void onFailure(Throwable t) {
EbmsProcessor.core.log.error("Error in outbox task", t);
}
public boolean isSucceedFast(){
return this.isAckRequested;
}
/*
* (non-Javadoc)
*
* @see hk.hku.cecid.piazza.commons.module.ActiveTask#isRetryEnabled()
*/
public boolean isRetryEnabled() {
return retryEnabled;
}
/*
* (non-Javadoc)
*
* @see hk.hku.cecid.piazza.commons.module.ActiveTask#getRetryInterval()
*/
public long getRetryInterval() {
// get retry interval in agreement
long retryInterval = 1000;
try {
retryInterval = new Long(outboxAgreement.getRetryInterval()).longValue();
} catch (Throwable e) {
}
return retryInterval;
}
public void onAwake() {
}
/**
* @param failureType
*/
private void generateErrorMessage(String failureType) {
// get the ebxml message and sign the message from repository
EbxmlMessage ebxmlMessage = null;
try {
ebxmlMessage = EbxmlMessageDAOConvertor.getEbxmlMessage(messageDVO
.getMessageId(), MessageClassifier.MESSAGE_BOX_OUTBOX);
} catch (MessageValidationException mve) {
EbmsProcessor.core.log.error("Cannot get the ebxml message: "
+ messageDVO.getMessageId(), mve);
try {
ebxmlMessage = new EbxmlMessage();
} catch (SOAPException e1) {
EbmsProcessor.core.log.error("Cannot new the ebxml message: "
+ messageDVO.getMessageId(), e1);
}
}
try {
EbxmlMessage errorEbxmlMessage = null;
if (failureType.equalsIgnoreCase(ERROR_TYPE_INTERNAL_ERROR)) {
EbmsProcessor.core.log.info("Generate internal error message");
// messageDVO error, agreement error or repository error
// when initiation
errorEbxmlMessage = SignalMessageGenerator
.generateErrorMessageBySender(ebxmlMessage,
ErrorList.CODE_UNKNOWN,
ErrorList.SEVERITY_ERROR, errorMessage, null);
} else
if (failureType.equalsIgnoreCase(ERROR_TYPE_DELIVERY_FAILTURE)) {
EbmsProcessor.core.log.info("Generate delivery failure error message");
// delivery failure if no error messageDVO
errorEbxmlMessage = SignalMessageGenerator
.generateErrorMessageBySender(ebxmlMessage,
ErrorList.CODE_DELIVERY_FAILURE,
ErrorList.SEVERITY_ERROR, "Delivery failure",
null);
} else
if (failureType.equalsIgnoreCase(ERROR_TYPE_MAXIMUM_RETRIES_REACHED)) {
EbmsProcessor.core.log.info("Generate delivery failure error message");
// delivery failure if no error messageDVO
errorEbxmlMessage = SignalMessageGenerator
.generateErrorMessageBySender(ebxmlMessage,
ErrorList.CODE_DELIVERY_FAILURE,
ErrorList.SEVERITY_ERROR,
"Delivery failure - Maximum retries reached",
null);
}
storeIncomingMessage(errorEbxmlMessage);
} catch (SOAPException e) {
EbmsProcessor.core.log.error("Cannot generate error message", e);
} catch (MessageServiceHandlerException e) {
EbmsProcessor.core.log.error("Cannot store incoming message", e);
}
}
/**
* Check whether the status of the current processing <code>EbxmlMessage</code>
* is allowed to update to next status.
*/
private boolean checkUpdateStatusIsAllow()
{
if (this.messageDVO == null) return false;
String status = this.messageDVO.getStatus();
return !status.equalsIgnoreCase(MessageClassifier.INTERNAL_STATUS_DELIVERED) ||
!status.equalsIgnoreCase(MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE) ||
!status.equalsIgnoreCase(MessageClassifier.INTERNAL_STATUS_PROCESSED) ||
!status.equalsIgnoreCase(MessageClassifier.INTERNAL_STATUS_PROCESSED_ERROR);
}
private void checkResetIsAllow(MessageDVO sequenceResetMessage)
throws DAOException, MessageValidationException {
// this function will throw exception when corresponding outbox
// messageDVO
// acknowledgement did not exist
MessageDAO messageDAO = (MessageDAO) EbmsProcessor.core.dao
.createDAO(MessageDAO.class);
MessageDVO finder = (MessageDVO) messageDAO.createDVO();
finder.setMessageBox(MessageClassifier.MESSAGE_BOX_OUTBOX);
finder.setCpaId(sequenceResetMessage.getCpaId());
finder.setService(sequenceResetMessage.getService());
finder.setAction(sequenceResetMessage.getAction());
finder.setConvId(sequenceResetMessage.getConvId());
finder.setStatus(MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE);
// find all the failed outbox sequence messageDVO using cpa
List failedList = messageDAO
.findOrderedMessagesByMessageBoxAndCpaAndStatus(finder);
if (failedList.size() != 0) {
throw new MessageValidationException(
"Reset is not allowed as previous msg delivery failed");
}
finder.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSING);
// find all the processing outbox sequence messageDVO using cpa
List processingList = messageDAO
.findOrderedMessagesByMessageBoxAndCpaAndStatus(finder);
// the number of processing sequence msg more than 1
if (processingList.size() > 1) {
throw new MessageValidationException(
"Reset is not allowed as previous msg has not acknowledged");
}
finder.setStatus(MessageClassifier.INTERNAL_STATUS_PROCESSED_ERROR);
// find all the processing outbox sequence messageDVO using cpa
List processedErrorList = messageDAO
.findOrderedMessagesByMessageBoxAndCpaAndStatus(finder);
// the number of processing sequence msg more than 1
if (processedErrorList.size() > 1) {
throw new MessageValidationException(
"Reset is not allowed as previous msg has error");
}
}
private void storeIncomingMessage(EbxmlMessage ebxmlMessage)
throws MessageServiceHandlerException {
MessageClassifier messageClassifier = new MessageClassifier(ebxmlMessage);
EbxmlMessageDAOConvertor message = new EbxmlMessageDAOConvertor(
ebxmlMessage, MessageClassifier.MESSAGE_BOX_INBOX,
messageClassifier.getMessageType());
try {
MessageDVO messageDVO = message.getMessageDVO();
messageDVO.setStatus(MessageClassifier.INTERNAL_STATUS_PENDING);
MessageServerDAO messageServerDAO = (MessageServerDAO) EbmsProcessor.core.dao
.createDAO(MessageServerDAO.class);
messageServerDAO.storeMessage(messageDVO, message.getRepositoryDVO());
} catch (DAOException daoe) {
String detail = "Error in storing incoming message.";
EbmsProcessor.core.log.error(detail, daoe);
throw new MessageServiceHandlerException(detail, daoe);
}
}
private void fireMessageSentEvent(EbxmlMessage ebxmlMessage) {
MessageClassifier messageClassifier = new MessageClassifier(ebxmlMessage);
String messageType = messageClassifier.getMessageType();
if (MessageClassifier.MESSAGE_TYPE_ORDER.equalsIgnoreCase(messageType)) {
EbmsEventModule eventModule = (EbmsEventModule) EbmsProcessor
.getModuleGroup().getModule(EbmsEventModule.MODULE_ID);
eventModule.fireMessageSent(ebxmlMessage);
}
}
/*
private String findPrincipalId(EbxmlMessage ebxmlRequestMessage) {
// first - from ref to messageDVO
// second - from receiver channel
if (ebxmlRequestMessage.getAcknowledgment() != null) {
// if it contains reference messageDVO id
try {
MessageDAO messageDAO = (MessageDAO) EbmsProcessor.core.dao
.createDAO(MessageDAO.class);
MessageDVO messageDVO = (MessageDVO) messageDAO.createDVO();
messageDVO.setMessageId(ebxmlRequestMessage.getMessageHeader()
.getRefToMessageId());
messageDVO.setMessageBox(MessageClassifier.MESSAGE_BOX_INBOX);
if (messageDAO.findMessage(messageDVO)) {
return messageDVO.getPrincipalId();
}
} catch (DAOException e) {
EbmsProcessor.core.log.error("Error in finding principal id", e);
}
}
// search in receiver channel
try {
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());
partnershipDAO.findPartnershipByCPA(partnershipDVO);
return partnershipDVO.getPrincipalId();
} catch (DAOException e) {
EbmsProcessor.core.log.error("Error in finding principal id", e);
}
return "nobody";
}
*/
}