Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.openas2.processor.sender.AS2SenderModule Maven / Gradle / Ivy
Go to download
Open source implementation of the AS2 standard for signed encrypted and compressed document transfer
package org.openas2.processor.sender;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeBodyPart;
import javax.net.ssl.SSLHandshakeException;
import org.apache.commons.io.filefilter.AgeFileFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openas2.ComponentNotFoundException;
import org.openas2.OpenAS2Exception;
import org.openas2.Session;
import org.openas2.cert.CertificateFactory;
import org.openas2.lib.helper.ICryptoHelper;
import org.openas2.message.AS2Message;
import org.openas2.message.AS2MessageMDN;
import org.openas2.message.DataHistoryItem;
import org.openas2.message.FileAttribute;
import org.openas2.message.Message;
import org.openas2.message.MessageMDN;
import org.openas2.message.NetAttribute;
import org.openas2.params.InvalidParameterException;
import org.openas2.partner.Partnership;
import org.openas2.processor.resender.ResenderModule;
import org.openas2.schedule.HasSchedule;
import org.openas2.util.AS2Util;
import org.openas2.util.DateUtil;
import org.openas2.util.DispositionOptions;
import org.openas2.util.HTTPUtil;
import org.openas2.util.IOUtil;
import org.openas2.util.Properties;
import org.openas2.util.ResponseWrapper;
public class AS2SenderModule extends HttpSenderModule implements HasSchedule {
private Log logger = LogFactory.getLog(AS2SenderModule.class.getSimpleName());
public boolean canHandle(String action, Message msg, Map options)
{
if (!action.equals(SenderModule.DO_SEND))
{
return false;
}
return (msg instanceof AS2Message);
}
public void handle(String action, Message msg, Map options) throws OpenAS2Exception
{
if (logger.isInfoEnabled())
{
logger.info("message sender invoked" + msg.getLogMsgID());
}
boolean isResend = Message.MSG_STATUS_MSG_RESEND.equals(msg.getStatus());
options.put("DIRECTION", "SEND");
options.put("IS_RESEND", isResend ? "Y" : "N");
if (!(msg instanceof AS2Message))
{
throw new OpenAS2Exception("Can't send non-AS2 message");
}
// verify all required information is present for sending
checkRequired(msg);
// Store options on the message object
if (options != null)
{
msg.getOptions().putAll(options);
}
if (logger.isTraceEnabled())
{
logger.trace("Retry count from options: " + options);
}
// Get the resend retry count
String retries = AS2Util.retries(options, getParameter(SenderModule.SOPT_RETRIES, false));
// Get any static custom headers
addCustomHeaders(msg);
// encrypt and/or sign and/or compress the message if needed
MimeBodyPart securedData;
try
{
securedData = secure(msg);
//Add any additional headers if configured since this will be the outermost Mime body part
addCustomOuterMimeHeaders(msg, securedData);
storePendingInfo((AS2Message) msg, isResend);
} catch (Exception e)
{
msg.setLogMsg(org.openas2.logging.Log.getExceptionMsg(e));
logger.error(msg, e);
// Log significant msg state
msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION);
msg.trackMsgState(getSession());
throw new OpenAS2Exception("Error setting up message for sending.", e);
}
if (logger.isTraceEnabled())
{
try
{
logger.trace("Message object in sender module. Content-Disposition: " + msg.getContentDisposition()
+ "\n Content-Type : " + msg.getContentType() + "\n HEADERS : " + AS2Util.printHeaders(msg.getData().getAllHeaders())
+ "\n Content-Disposition in MSG getData() MIMEPART: " + msg.getData().getContentType()
+ msg.getLogMsgID());
} catch (Exception e)
{
}
}
String url = msg.getPartnership().getAttribute(Partnership.PA_AS2_URL);
try
{
// Create the HTTP connection and set up headers
// Log significant msg state
msg.setOption("STATE", Message.MSG_STATE_SEND_START);
msg.trackMsgState(getSession());
sendMessage(url, msg, securedData, retries);
} catch (HttpResponseException hre)
{
// Will have been logged so just resend
resend(msg, hre, retries, false);
// Log significant msg state
msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION);
msg.trackMsgState(getSession());
return;
} catch (SSLHandshakeException e)
{
msg.setLogMsg("Failed to connect to partner using SSL certificate. Please run the SSL certificate checker utility to identify the issue: " + url);
logger.error(msg, e);
msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL);
msg.trackMsgState(getSession());
return;
} catch (Exception e)
{
msg.setLogMsg("Unexpected error sending file: " + org.openas2.logging.Log.getExceptionMsg(e));
logger.error(msg, e);
resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(e)), retries, false);
// Log significant msg state
msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION);
msg.trackMsgState(getSession());
return;
}
}
protected void checkRequired(Message msg) throws InvalidParameterException
{
Partnership partnership = msg.getPartnership();
try
{
InvalidParameterException.checkValue(msg, "ContentType", msg.getContentType());
InvalidParameterException.checkValue(msg, "Attribute: " + Partnership.PA_AS2_URL,
partnership.getAttribute(Partnership.PA_AS2_URL));
InvalidParameterException.checkValue(msg, "Receiver: " + Partnership.PID_AS2,
partnership.getReceiverID(Partnership.PID_AS2));
InvalidParameterException.checkValue(msg, "Sender: " + Partnership.PID_AS2,
partnership.getSenderID(Partnership.PID_AS2));
InvalidParameterException.checkValue(msg, "Subject", msg.getSubject());
InvalidParameterException.checkValue(msg, "Sender: " + Partnership.PID_EMAIL,
partnership.getSenderID(Partnership.PID_EMAIL));
InvalidParameterException.checkValue(msg, "Message Data", msg.getData());
} catch (InvalidParameterException rpe)
{
rpe.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg);
throw rpe;
}
}
private void sendMessage(String url, Message msg, MimeBodyPart securedData, String retries)
throws Exception {
URL urlObj = new URL(url);
InternetHeaders ih = getHttpHeaders(msg, securedData);
msg.setAttribute(NetAttribute.MA_DESTINATION_IP, urlObj.getHost());
msg.setAttribute(NetAttribute.MA_DESTINATION_PORT, Integer.toString(urlObj.getPort()));
if (logger.isInfoEnabled())
{
logger.info("Connecting to: " + url + msg.getLogMsgID());
}
Map httpOptions = getHttpOptions();
httpOptions.put(HTTPUtil.PARAM_HTTP_USER, msg.getPartnership().getAttribute(HTTPUtil.PARAM_HTTP_USER));
httpOptions.put(HTTPUtil.PARAM_HTTP_PWD, msg.getPartnership().getAttribute(HTTPUtil.PARAM_HTTP_PWD));
long maxSize = msg.getPartnership().getNoChunkedMaxSize();
ResponseWrapper resp = HTTPUtil.execRequest(HTTPUtil.Method.POST, url, ih.getAllHeaders()
, null, securedData.getInputStream(), httpOptions, maxSize);
if (logger.isInfoEnabled())
{
logger.info("Message sent and response received in " + resp.getTransferTimeMs() + "ms" + msg.getLogMsgID());
}
// Check the HTTP Response code
int rc = resp.getStatusCode();
if ((rc != HttpURLConnection.HTTP_OK) && (rc != HttpURLConnection.HTTP_CREATED)
&& (rc != HttpURLConnection.HTTP_ACCEPTED) && (rc != HttpURLConnection.HTTP_PARTIAL)
&& (rc != HttpURLConnection.HTTP_NO_CONTENT))
{
msg.setLogMsg("Error sending message. URL: " + url
+ " ::: Response Code: " + rc + " " + resp.getStatusPhrase()
+ " ::: Response Message: " + resp.getBody().toString());
logger.error(msg);
throw new HttpResponseException(url, rc, resp.getStatusPhrase());
}
// So far so good ...
processResponse(msg, resp);
}
private void processResponse(Message msg, ResponseWrapper response) {
if (logger.isTraceEnabled()) {
logger.trace("Message sent. Checking if MDN is expected..." + msg.getLogMsgID());
}
if (!msg.isConfiguredForMDN())
return;
// Check if it will be a Sync or AsyncMDN
if (msg.getPartnership().getAttribute(Partnership.PA_AS2_RECEIPT_OPTION) != null) {
// Async MDN
msg.setStatus(Message.MSG_STATUS_MDN_WAIT);
} else {
// Create a MessageMDN and copy HTTP headers
MessageMDN mdn = new AS2MessageMDN((AS2Message) msg, false);
if (logger.isTraceEnabled()) {
logger.trace("MDN msg initalised for inbound contains headers:"
+ AS2Util.printHeaders(mdn.getHeaders().getAllHeaders()) + msg.getLogMsgID());
}
mdn.copyHeaders(response.getHeaders());
if (logger.isTraceEnabled()) {
logger.trace("Synchronous MDN received. Start processing..." + msg.getLogMsgID());
}
msg.setStatus(Message.MSG_STATUS_MDN_PROCESS_INIT);
try {
AS2Util.processMDN((AS2Message) msg, response.getBody(), null, false, getSession(), this);
// Log significant msg state
msg.setOption("STATE", Message.MSG_STATE_MSG_SENT_MDN_RECEIVED_OK);
msg.trackMsgState(getSession());
} catch (Exception e) {
if (Message.MSG_STATUS_MDN_PROCESS_INIT.equals(msg.getStatus())
|| Message.MSG_STATUS_MDN_PARSE.equals(msg.getStatus()) || !(e instanceof OpenAS2Exception)) {
/*
* Cannot identify the target if in init or parse state so not sure what the
* best course of action is apart from do nothing
*/
msg.setLogMsg(
"Unhandled error condition processing synchronous MDN. Message and asociated files cleanup will be attempted but may be in an unknown state.");
logger.error(msg, e);
}
/*
* Most likely a resend abort of max resend reached if OpenAS2Exception so do
* not log as should have been logged upstream ... just clean up the mess
*/
else {
// Must have received MDN successfully
msg.setLogMsg(
"Exception receiving synchronous MDN. Message and asociated files cleanup will be attempted but may be in an unknown state.");
logger.error(msg, e);
}
// Log significant msg state
msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL);
msg.trackMsgState(getSession());
AS2Util.cleanupFiles(msg, true);
}
}
}
private void resend(Message msg, OpenAS2Exception cause, String tries, boolean keepRestoredData) throws OpenAS2Exception
{
AS2Util.resend(getSession(), this, SenderModule.DO_SEND, msg, cause, tries, false, keepRestoredData);
}
/**
* Returns a MimeBodyPart or MimeMultipart object
* @param msg The message object carried around containing necessary information
* @return The secured mimebodypart
* @throws Exception some unforseen issue has occurred
*/
protected MimeBodyPart secure(Message msg) throws Exception
{
// Set up encrypt/sign variables
MimeBodyPart dataBP = msg.getData();
/*
* Based on RFC4130, RFC6362 and RFC5042, the MIC is calculated as
* follows: Signed message - MIME header fields and content that is to
* be signed which may or may not be encrypted and/or compressed.
*
* Unsigned encrypted message - data content including all MIME header
* fields and any applied Content-Transfer-Encoding prior to encryption
* and/or compression
*
* So essentially, calculate the MIC before doing any compression or
* encryption if message not being signed otherwise calculate right
* before signing of the message but include headers for unsigned
* messages (see RFC4130 section 7.3.1 for details)
*/
Partnership partnership = msg.getPartnership();
String contentTxfrEncoding = partnership.getAttribute(Partnership.PA_CONTENT_TRANSFER_ENCODING);
if (contentTxfrEncoding == null)
{
contentTxfrEncoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING;
}
boolean encrypt = partnership.getAttribute(Partnership.PA_ENCRYPTION_ALGORITHM) != null;
boolean sign = partnership.getAttribute(Partnership.PA_SIGNATURE_ALGORITHM) != null;
if (!sign)
{
calcAndStoreMic(msg, dataBP, (sign || encrypt));
}
// Check if compression is enabled
String compressionType = msg.getPartnership().getAttribute("compression_type");
if (logger.isTraceEnabled())
{
logger.trace("Compression type from config: " + compressionType);
}
boolean isCompress = false;
if (compressionType != null && !"NONE".equalsIgnoreCase(compressionType))
{
if (compressionType.equalsIgnoreCase(ICryptoHelper.COMPRESSION_ZLIB))
{
isCompress = true;
}
else
{
throw new OpenAS2Exception("Unsupported compression type: " + compressionType);
}
}
String compressionMode = msg.getPartnership().getAttribute("compression_mode");
boolean isCompressBeforeSign = true; // Defaults to compressing the
// entire message before signing
// and encryption
if (compressionMode != null && compressionMode.equalsIgnoreCase("compress-after-signing"))
{
isCompressBeforeSign = false;
}
if (isCompress && isCompressBeforeSign)
{
if (logger.isTraceEnabled())
{
logger.trace("Compressing outbound message before signing...");
}
if (!sign && !encrypt)
{
//Add any additional headers since this will be the outermost Mime body part if configured
addCustomOuterMimeHeaders(msg, dataBP);
}
dataBP = AS2Util.getCryptoHelper().compress(msg, dataBP, compressionType, contentTxfrEncoding);
}
// Encrypt and/or sign the data if requested
CertificateFactory certFx = getSession().getCertificateFactory();
// Sign the data if requested
if (sign)
{
if (!encrypt && !(isCompress && !isCompressBeforeSign))
{
//Add any additional headers since this will be the outermost Mime body part if configured
addCustomOuterMimeHeaders(msg, dataBP);
}
calcAndStoreMic(msg, dataBP, (sign || encrypt));
X509Certificate senderCert = certFx.getCertificate(msg, Partnership.PTYPE_SENDER);
PrivateKey senderKey = certFx.getPrivateKey(msg, senderCert);
String digest = partnership.getAttribute(Partnership.PA_SIGNATURE_ALGORITHM);
if (logger.isDebugEnabled())
{
logger.debug("Params for creating signed body part:: DATA: " + dataBP + "\n SIGN DIGEST: " + digest
+ "\n CERT ALG NAME EXTRACTED: " + senderCert.getSigAlgName()
+ "\n CERT PUB KEY ALG NAME EXTRACTED: " + senderCert.getPublicKey().getAlgorithm()
+ msg.getLogMsgID());
}
boolean isRemoveCmsAlgorithmProtectionAttr = "true".equalsIgnoreCase(partnership.getAttribute(Partnership.PA_REMOVE_PROTECTION_ATTRIB));
dataBP = AS2Util.getCryptoHelper().sign(dataBP, senderCert, senderKey, digest
, contentTxfrEncoding, msg.getPartnership().isRenameDigestToOldName(), isRemoveCmsAlgorithmProtectionAttr);
DataHistoryItem historyItem = new DataHistoryItem(dataBP.getContentType());
// *** add one more item to msg history
msg.getHistory().getItems().add(historyItem);
if (logger.isDebugEnabled())
{
logger.debug("signed data" + msg.getLogMsgID());
}
}
if (isCompress && !isCompressBeforeSign)
{
if (!encrypt)
{
//Add any additional headers since this will be the outermost Mime body part if configured
addCustomOuterMimeHeaders(msg, dataBP);
}
if (logger.isTraceEnabled())
{
logger.trace("Compressing outbound message after signing...");
}
dataBP = AS2Util.getCryptoHelper().compress(msg, dataBP, compressionType, contentTxfrEncoding);
}
// Encrypt the data if requested
if (encrypt)
{
//Add any additional headers since this will be the outermost Mime body part if configured
addCustomOuterMimeHeaders(msg, dataBP);
String algorithm = partnership.getAttribute(Partnership.PA_ENCRYPTION_ALGORITHM);
X509Certificate receiverCert = certFx.getCertificate(msg, Partnership.PTYPE_RECEIVER);
dataBP = AS2Util.getCryptoHelper().encrypt(dataBP, receiverCert, algorithm, contentTxfrEncoding);
// Asynch MDN 2007-03-12
DataHistoryItem historyItem = new DataHistoryItem(dataBP.getContentType());
// *** add one more item to msg history
msg.getHistory().getItems().add(historyItem);
if (logger.isDebugEnabled())
{
logger.debug("encrypted data" + msg.getLogMsgID());
}
}
String t = dataBP.getEncoding();
if ((t == null || t.length() < 1) && "true".equalsIgnoreCase(partnership.getAttribute(Partnership.PA_SET_CONTENT_TRANSFER_ENCODING_OMBP)))
{
dataBP.setHeader("Content-Transfer-Encoding", contentTxfrEncoding);
}
return dataBP;
}
protected void addCustomHeaders(Message msg) throws OpenAS2Exception {
String customHeaders = msg.getPartnership().getAttribute(Partnership.PA_CUSTOM_MIME_HEADERS);
if (customHeaders != null && customHeaders.length() > 0)
{
if (logger.isTraceEnabled())
{
logger.trace("Adding custom header attribute to custom headers map..." + msg.getLogMsgID());
}
String[] headers = customHeaders.split("\\s*;\\s*");
for (int i = 0; i < headers.length; i++)
{
String[] header = headers[i].split("\\s*:\\s*");
if (logger.isTraceEnabled())
{
logger.trace("Adding custom header: " + headers[i]
+ " :::Split count:" + header.length + msg.getLogMsgID());
}
if (header.length != 2)
{
throw new OpenAS2Exception("Invalid custom header: " + headers[i]);
}
msg.addCustomOuterMimeHeader(header[0].replaceAll(" ", ""), header[1]);
}
}
}
protected void addCustomOuterMimeHeaders(Message msg, MimeBodyPart dataBP) throws MessagingException
{
if (logger.isTraceEnabled())
{
logger.trace("Adding custom headers to outer MBP...." + msg.getLogMsgID());
}
Map hdrs = msg.getCustomOuterMimeHeaders();
if (hdrs == null)
{
return;
}
for (Map.Entry entry : hdrs.entrySet())
{
dataBP.addHeader(entry.getKey(), entry.getValue());
if (logger.isTraceEnabled())
{
logger.trace("Added custom headers to outer MBP: " + entry.getKey() + "--->" + entry.getValue() + msg.getLogMsgID());
}
}
}
protected InternetHeaders getHttpHeaders(Message msg, MimeBodyPart securedData) throws MessagingException {
Partnership partnership = msg.getPartnership();
InternetHeaders ih = new InternetHeaders();
ih.addHeader(HTTPUtil.HEADER_CONNECTION, "close, TE");
String userAgent = Properties.getProperty(Properties.HTTP_USER_AGENT_PROP,
msg.getAppTitle() + " (" + AS2SenderModule.class.getSimpleName() + ")");
ih.addHeader("User-Agent", userAgent);
// Ensure date is formatted in english so there are only USASCII chars to avoid error
ih.addHeader("Date", DateUtil.formatDate(
Properties.getProperty("HTTP_HEADER_DATE_FORMAT", "EEE, dd MMM yyyy HH:mm:ss Z"), Locale.ENGLISH));
ih.addHeader("Message-ID", msg.getMessageID());
ih.addHeader("Mime-Version", "1.0"); // make sure this is the
// encoding used in the msg, run TBF1
try {
ih.addHeader(HTTPUtil.HEADER_CONTENT_TYPE, securedData.getContentType());
} catch (MessagingException e) {
ih.addHeader(HTTPUtil.HEADER_CONTENT_TYPE, msg.getContentType());
}
// AS2 V1.2 additionally supports EDIINT-Features
// ih.addHeader("EDIINT-Features","CEM,multiple-attachments");
// TODO (possibly implement???)
ih.addHeader("AS2-Version", "1.1"); // RFC6017 - AS2 V1.1 supports compression
/* The Content-Transfer-Encoding header is now a restricted header for HTTP so allow
* it to be controlled by config at partnership level
* Java will automatically remove this even if set unless the
* sun.net.http.allowRestrictedHeaders property is set to "true"
*/
if ("true".equalsIgnoreCase(System.getProperty("sun.net.http.allowRestrictedHeaders", "false"))) {
if (logger.isDebugEnabled()) logger.debug("HTTP RESTRICTED HEADERS property is not active");
String cte = null;
cte = msg.getPartnership().getAttributeOrProperty(Partnership.PA_CONTENT_TRANSFER_ENCODING, null);
if (cte != null) {
if ("true".equalsIgnoreCase(msg.getPartnership().getAttributeOrProperty(Partnership.PA_SET_CONTENT_TRANSFER_ENCODING_HTTP, "false")))
ih.addHeader("Content-Transfer-Encoding", cte);
}
}
ih.addHeader("Recipient-Address", partnership.getAttribute(Partnership.PA_AS2_URL));
String rId = partnership.getReceiverID(Partnership.PID_AS2);
if (rId.contains(" ")) rId = "\"" + rId + "\"";
ih.addHeader("AS2-To", rId);
String sId = partnership.getSenderID(Partnership.PID_AS2);
if (sId.contains(" ")) sId = "\"" + sId + "\"";
ih.addHeader("AS2-From", sId);
ih.addHeader("Subject", msg.getSubject());
ih.addHeader("From", partnership.getSenderID(Partnership.PID_EMAIL));
String dispTo = partnership.getAttribute(Partnership.PA_AS2_MDN_TO);
if (dispTo != null) {
ih.addHeader("Disposition-Notification-To", dispTo);
}
String dispOptions = partnership.getAttribute(Partnership.PA_AS2_MDN_OPTIONS);
if (dispOptions != null && !"none".equalsIgnoreCase(dispOptions)) {
ih.addHeader("Disposition-Notification-Options", dispOptions);
}
String receiptOption = partnership.getAttribute(Partnership.PA_AS2_RECEIPT_OPTION);
if (receiptOption != null) {
ih.addHeader("Receipt-Delivery-Option", receiptOption);
}
String contentDisp;
try {
contentDisp = securedData.getDisposition();
} catch (MessagingException e) {
contentDisp = msg.getContentDisposition();
}
if (contentDisp != null) {
ih.addHeader("Content-Disposition", contentDisp);
}
if ("true".equalsIgnoreCase((partnership.getAttribute(Partnership.PA_ADD_CUSTOM_MIME_HEADERS_TO_HTTP)))) {
if (logger.isTraceEnabled()) {
logger.trace("Adding custom headers to HTTP..." + msg.getLogMsgID());
}
for (Map.Entry entry : msg.getCustomOuterMimeHeaders().entrySet()) {
ih.addHeader(entry.getKey(), entry.getValue());
}
}
return ih;
}
/**
* Stores metadata into pending information file and storing
* message object from first send attempt. The message object
* is written to a separate file to avoid repeated rewrites of
* possibly very large objects since it contains the original
* file data
* @param msg AS2Message structure
* @param isResend Boolean to determine if this is a resend of an already sent message or not
* @throws Exception some unforseen issue has occurred
*
*/
protected void storePendingInfo(AS2Message msg, boolean isResend) throws Exception
{
ObjectOutputStream oos = null;
try
{
String pendingInfoFile = AS2Util.buildPendingFileName(msg, getSession().getProcessor(), "pendingmdninfo");
String pendingFile = msg.getAttribute(FileAttribute.MA_PENDINGFILE);
msg.setAttribute(FileAttribute.MA_PENDINGFILE, pendingFile);
msg.setAttribute(FileAttribute.MA_PENDINGINFO, pendingInfoFile);
if (!isResend)
{
// Write the object to a file to keep a lot of the original
// static metadata intact for resends
String pendingMsgObjFile = pendingFile + ".object";
oos = new ObjectOutputStream(new FileOutputStream(pendingMsgObjFile));
oos.writeObject(msg);
oos.flush();
oos.close();
}
oos = new ObjectOutputStream(new FileOutputStream(pendingInfoFile));
oos.writeObject(msg.getCalculatedMIC());
String retries = (String) msg.getOption(ResenderModule.OPTION_RETRIES);
oos.writeObject((retries == null ? "" : retries));
if (logger.isInfoEnabled())
{
logger.info("Save Original mic & message id information into file: " + pendingInfoFile
+ msg.getLogMsgID());
}
oos.writeObject(msg.getAttribute(FileAttribute.MA_FILENAME));
oos.writeObject(pendingFile);
oos.writeObject(msg.getAttribute(FileAttribute.MA_ERROR_DIR));
String sentDir = msg.getAttribute(FileAttribute.MA_SENT_DIR);
oos.writeObject((sentDir == null ? "" : sentDir));
oos.writeObject(msg.getAttributes());
if (logger.isTraceEnabled())
{
logger.trace("Pending info file written to:" + pendingInfoFile + "\n\tOriginal MIC: "
+ msg.getCalculatedMIC() + "\n\tRetry Count: " + retries
+ "\n\tOriginal file name : " + msg.getAttribute(FileAttribute.MA_FILENAME)
+ "\n\tPending message file : " + pendingFile + "\n\tError directory: "
+ msg.getAttribute(FileAttribute.MA_ERROR_DIR) + "\n\tSent directory: "
+ msg.getAttribute(FileAttribute.MA_SENT_DIR)
+ "\n\tAttributes: " + msg.getAttributes()
+ msg.getLogMsgID());
}
msg.setAttribute(FileAttribute.MA_STATUS, FileAttribute.MA_PENDING);
// If ASYNC MDN is requested, set up a file watcher in case partner MDN is not received
if (msg.isConfiguredForAsynchMDN()) {
// Create a listener that will force resend if the pendinginfo file is still there after set amount of time
}
} catch (Exception e)
{
msg.setLogMsg("Error setting up pending information files: " + org.openas2.logging.Log.getExceptionMsg(e));
logger.error(msg, e);
throw new Exception("Unable to set up pending information files.");
} finally
{
if (oos != null)
{
try
{
oos.close();
} catch (IOException e)
{
}
}
}
}
protected void calcAndStoreMic(Message msg, MimeBodyPart mbp, boolean includeHeaders) throws Exception
{
// Calculate and get the original mic
// includeHeaders = (msg.getHistory().getItems().size() > 1);
String mdnOptions = msg.getPartnership().getAttributeOrProperty(Partnership.PA_AS2_MDN_OPTIONS, null);
if (mdnOptions == null || mdnOptions.length() < 1) {
throw new OpenAS2Exception("Partner attribute " + Partnership.PA_AS2_MDN_OPTIONS + "is required but can be set to \"none\"");
}
if ("none".equalsIgnoreCase(mdnOptions)) return;
DispositionOptions dispOptions = new DispositionOptions(mdnOptions);
msg.setCalculatedMIC(AS2Util.getCryptoHelper().calculateMIC(mbp, dispOptions.getMicalg()
, includeHeaders, msg.getPartnership().isPreventCanonicalization()));
if (logger.isTraceEnabled())
{
// Generate some alternative MIC's to see if the partner is somehow using a different default
String tmic = AS2Util.getCryptoHelper().calculateMIC(mbp, dispOptions.getMicalg()
, includeHeaders, !msg.getPartnership().isPreventCanonicalization());
logger.trace("MIC outbound with forced reversed prevent canocalization: " + tmic + msg.getLogMsgID());
tmic = AS2Util.getCryptoHelper().calculateMIC(msg.getData(), dispOptions.getMicalg(),
false, msg.getPartnership().isPreventCanonicalization());
logger.trace("MIC outbound with forced exclude headers flag: " + tmic + msg.getLogMsgID());
}
}
protected void detectFailedSentMessages() {
String dir;
try {
dir = (String) getSession().getProcessor().getParameters().get("pendingmdninfo");
} catch (ComponentNotFoundException e) {
logger.warn(
"Failed to retrieve the name of the pending info folder for sent messages in trying to run the failed message detection method.",
e);
return;
}
File pendingDir;
try {
pendingDir = IOUtil.getDirectoryFile(dir);
} catch (IOException e) {
logger.warn(
"Failed to open the pending info folder for sent messages in trying to run the failed message detection method.",
e);
return;
}
// We are interested in files older than one day
int maxWaitMdnResponseSecs = Integer
.parseInt(Properties.getProperty(Properties.AS2_MDN_RESP_MAX_WAIT_SECS, "4560"));
long cutoff = System.currentTimeMillis() - (maxWaitMdnResponseSecs * 1000);
String[] files = pendingDir.list(new AgeFileFilter(cutoff));
for (int i = 0; i < files.length; i++) {
File inFile = new File(pendingDir + File.separator + files[i]);
AS2Message msg = new AS2Message();
try {
AS2Util.getMetaData(msg, inFile);
String msgStr = "Pending information file detected that is past max wait time due to unknown failure: " + inFile.getAbsolutePath();
msg.setLogMsg(msgStr);
logger.error(msg, null);
AS2Util.cleanupFiles(msg, true);
} catch (Exception e) {
logger.warn(
"Failed to process the pending info folder for sent messages in trying to run the failed message detection method.",
e);
}
}
}
@Override
public void schedule(ScheduledExecutorService executor) {
String delayStr = Properties.getProperty(Properties.AS2_MDN_RESP_MAX_WAIT_SECS, "4560");
Long delay = Long.parseLong(delayStr)/4*1000;
executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run()
{
detectFailedSentMessages();
}
}, delay, delay, TimeUnit.MILLISECONDS);
}
}