Sending and Receiving AS2 Messages

Sample Number 352

Level

Intermediary

Description

Demonstrates Receiving and Sending of AS2 Messages and the sample configuration and setup for AS2 is explained

AS2 (Applicability Statement 2) is a specification [RFC4130] on how to transport data securely and reliably over the Internet. Security is achieved by using digital certificates and encryption. This articles explains a sample configuration that demonstrates sending and receiving of messages over AS2 with the UltraESB. To make this sample simple, we will first only concentrate on the receipt and sending of files, without mediation or transport/data format conversions.

Example  # 351 shows the use of the Smooks framework to transform EDI message into XML with AS2.

Note
This documentation applies to the v1.7.0 onwards of the UltraESB, and will not work against the AS2 support of the previous versions which are now deprecated.

Sample Use Case - AS2 to/from FTP Gateway

The sample # 352 configuration shows a AS2 to ftp bridge implementation to illustrate the basic AS2 support of the UltraESB. Multiple AS2 trading partners would require sending AS2 messages to associates that does not have an in-house AS2 capability. Hence a hosted AS2/FTP Gateway implementation performs the message conversions to facilitate effective communication among these parties.

One or more AS2 trading partners would send AS2 payload messages to the "as2-receiver" proxy service, which maps to http://localhost:8280/service/as2-receiver in this example.

as2 ftp gateway

Receiving Messages from a Trading Partner

The "as2-receiver" proxy service will perform the AS2 level message processing, and close the HTTP level transport with the trading partner as required per AS2 semantics. This could mean that the requested MDN would be sent back to the partner - if a synchronous MDN was requested. If an asynchronous MDN was requested, the mediation logic must take the necessary steps to send it across. This is performed by passing the HTTP level message received at the inSequence of the proxy service to the processIncomingAS2Message() method of the AS2Manager. The return value of this API call will indicate the result of the processing, and the AS2 level information of the message received.

If processing was successful, we pick the FTP user box (in this example by using a random pick from the available FTP users - ftpuser1, ftpuser2, ftpuser3), and then save the payload to the "incoming" directory of the selected FTP associate. We also prefix the file name with a timestamp to prevent duplicate files from conflicting. The FTP associate is now expected to process files appearing in its "incoming" directory, process them and then move them to the "incoming/processed" directory

AS2 Message Receipt

<!--Handles incoming AS2 messages and sends sync MDNs or the transport closure-->
<u:proxy id="as2-receiver">
  <u:transport id="http-8280"/>
  <u:target>
    <u:inSequence>
      <u:java import="org.adroitlogic.as2.api.*; org.adroitlogic.ultraesb.api.transport.file.*; org.apache.commons.lang.time.*;"><![CDATA[
          ReceiveInfo rcvInfo = mediation.getAS2Manager().processIncomingAS2Message(msg);
          if (rcvInfo.isAsyncMDNRequested() && !rcvInfo.isMDNSent()) {
              Message asyncMdn = msg.cloneMessage();
              mediation.getAS2Manager().prepareAsyncMDNForSend(asyncMdn, rcvInfo);
              msg.addMessageProperty("async-mdn", true);
              mediation.sendToEndpoint(asyncMdn, "async-mdn-ep");
          }
          if (!rcvInfo.processingFailed()) {
              // recognize the target ftp user account to place file (use a random number)
              String username = "ftpuser" + (1 + new java.util.Random(System.currentTimeMillis()).nextInt(3));
              String fileName = "/tmp/" + username + "/inbox/" +
                  FastDateFormat.getInstance("yyyy_MM_dd_'T'HH_mm_ss.SSSS-").
                  format(System.currentTimeMillis()) +
                  msg.getMessageProperty(FileConstants.ORIGINAL_NAME);
              // make sure the target path exists, and then save to file at FTP path
              new java.io.File(fileName).getParentFile().mkdirs();
              mediation.savePayloadToFile(msg, fileName);
              logger.info("Received from : " + rcvInfo.getFrom() + " for FTP user : " + username + " saved to : " + fileName);
          } else {
              logger.warn("Message received from : {} failed : {}", rcvInfo.getFrom(), rcvInfo.getErrorInfoList());
          }
      ]]></u:java>
    </u:inSequence>
    <u:outSequence>
      <u:java import="org.adroitlogic.as2.api.*; org.adroitlogic.ultraesb.api.transport.http.*;"><![CDATA[
          logger.debug("A/MDN response received : " + msg.getMessageProperty(HttpConstants.RESPONSE_STATUS_CODE));
          mediation.getAS2Manager().processAsyncMDNSendResult(msg);
      ]]></u:java>
    </u:outSequence>
    <u:errorSequence>
      <u:java import="org.adroitlogic.as2.api.*; org.adroitlogic.ultraesb.api.transport.file.*;"><![CDATA[
          if (msg.isResponse()) {
              // response received for an async mdn
              logger.warn("Did not receive a successful confirmation for the async MDN : {}", msg.getLastException());
              mediation.getAS2Manager().recordSendFailure(msg);
          } else if (msg.getMessageProperty("async-mdn") != null) {
              // the attempt to send the async-mdn has a failure before being sent
              logger.warn("Unexpected error during async MDN send attempt : {}", msg.getLastException());
          } else {
              logger.warn("Unexpected error {}", msg.getLastException().getException());
          }
       ]]></u:java>
     </u:errorSequence>
   </u:target>
</u:proxy>

<!--Endpoint to send out async MDNs to trading partners to any URL specified by them in the message-->
<u:endpoint id="async-mdn-ep">
  <u:address type="default"/>
</u:endpoint>
Sending Async MDNs when requested

If the AS2 trading partner has requested for an async MDN, we clone the message to prepare the async MDN response, and call the prepareAsyncMDNForSend() method to populate it as required. We then send it via the "async-mdn-ep" endpoint which sends it to the "default" address already set on the message - which is the async MDN URL picked up from the AS2 message. If we encounter an error response for the async MDN, we notify the AS2 manager of the outcome via the recordSendFailure() API call.

Sending out messages to AS2 Trading Partners

We use the "xml-file-poller" proxy service which will poll the URL "file:///tmp/" with a path pattern of "ftpuser*/outbox/*.xml" - i.e. will look for any *.xml files in any FTP associates outboxes. These files will then be sent to the appropriate AS2 partner (code omitted - but maybe by looking at some XPath expression over the payload etc). The prepareAS2MessageForSend() API call is used to prepare the message as an AS2 message - and this step also sets the AS2 URL of the target partner as the default destination of the message. Hence, the inDestination which specifies a "default" type address now sends the message to the selected AS2 partner. The outSequence will process the synchronous response sent by the AS2 partner via the processSyncResponse() call, while a send error will be notified to the AS2 manager via the recordSendFailure() API call. Once processed, the original file is moved to the "ultra.file.move_after_process" directory - which in this case is a relative directory. Hence a file at "/tmp/ftpuser3/outbox" will be moved to "/tmp/ftpuser3/outbox/processed" for example. Similarly failed messages are moved to an error folder.

Sending out AS2 Messages

<!--A directory polling service which sends out the files to AS2 partners-->
<u:proxy id="xml-file-poller">
  <u:transport id="file-rcv">
    <u:property name="ultra.transport.url" value="file:///tmp/"/>
    <u:property name="ultra.file.path_pattern" value="ftpuser*/outbox/*.xml"/>
    <u:property name="ultra.polling.start_delay" value="1000"/>
    <u:property name="ultra.polling.repeat_interval" value="2000"/>
    <!--<u:property name="ultra.polling.concurrent" value="true"/>-->
    <!--<u:property name="ultra.polling.cron_expression" value="0/20/40 * * ? * MON-FRI"/>-->
    <u:property name="ultra.file.move_after_process" value="processed"/>
    <u:property name="ultra.file.move_after_failure" value="error"/>
    <u:property name="ultra.file.move_timestamp_format" value="yyyy_MM_dd_'T'HH_mm_ss.SSSSZ"/>
  </u:transport>
  <u:target>
    <u:inSequence>
    <u:java import="org.adroitlogic.as2.api.*; org.adroitlogic.ultraesb.api.transport.file.*;"><![CDATA[
        msg.addResponseCorrelation("file-path", msg.getFirstTransportHeader(FileConstants.PATH));

        // detect target AS2 partner ID by reading XML element etc, and look it up from the persistence
        // database by AS2 id
        // msg.addResponseCorrelation("partner", "mendelson");
        // mediation.getAS2Manager().prepareAS2MessageForSend(msg, "mendelson");

        Partner partner = new Partner("mendelson");
        partner.setEncryptCertAlias("server2");
        partner.setRequestMDN(true);
        partner.setRequestSignedMDN(true);
        partner.setSignMessages(true);
        partner.setEncryptMessages(true);
        partner.setUrl("http://localhost:8080/as2/HttpReceiver");
        // to request for an async MDN
        // partner.setRequestAsyncMDN(true);
        // partner.setAsyncReceiptURL("http://localhost:8280/service/AS2Receiver");
        msg.addResponseCorrelation("partner", "mendelson");
        mediation.getAS2Manager().prepareAS2MessageForSend(msg, partner);
     ]]></u:java>
   </u:inSequence>
   <u:inDestination>
     <u:address type="default"/>
   </u:inDestination>
   <u:outSequence>
     <u:java import="org.adroitlogic.as2.api.*;"><![CDATA[
         SyncResponseInfo srInfo = mediation.getAS2Manager().processSyncResponse(msg);
         logger.debug("Sync response received : {}", srInfo);
         logger.info("Completed sending file : " + msg.getMessageProperty("file-path") + " to " + msg.getMessageProperty("partner"));
     ]]></u:java>
   </u:outSequence>
   <u:errorSequence>
     <u:java><![CDATA[
         logger.error("AS2 send failed : {}", msg.getLastException(), msg.getLastException().getException());
         if (msg.isResponse()) {
             mediation.getAS2Manager().recordSendFailure(msg);
         }
         msg.setMarkedAsFailed(true);
     ]]></u:java>
    </u:errorSequence>
  </u:target>
</u:proxy>

<!--A service to receive async MDNs - if used/expected -->
<u:proxy id="as2-async-mdn-receiver">
  <u:transport id="http-8280"/>
  <u:target>
    <u:inSequence>
      <u:java><![CDATA[
          org.adroitlogic.as2.api.AsyncMDNInfo mdnInfo = mediation.getAS2Manager().processAsyncMDN(msg);
          logger.info("Async MDN received : {}", mdnInfo);
      ]]></u:java>
    </u:inSequence>
  </u:target>
</u:proxy>
Handling Async MDNs

Some AS2 partners maybe configured for async MDNs, in which case, we need to use the "as2-async-mdn-receiver" proxy service to process such async MDNs. The URL of this endpoint should also be configured on the AS2Manager bean via the "asyncURLForMDN" property, and then will be injected to each message where an async MDN is expected. The proxy service handling an async MDN received simply should invoke the processAsyncMDN() API call to notify of the event to the AS2 manager.

Executing the sample

To execute the sample, first refer to the Setting up a Sample AS2 Trading Partner guide. Start the sample #352 as "./ultraesb.sh -sample 352" from the UltraESB bin directory. From mendelson AS2, send a file to the AS2 partner "adroitlogic" via File → Send File to Partner. You should see the file received at the UltraESB and placed into a random FTP box

2012-01-12 20:06:07,833 [-] [primary-1] INFO as2-receiver-inSequence Received from : mendelson for FTP user : ftpuser2 saved to : /tmp/ftpuser2/inbox/2012_01_12_T20_06_07.0832-AAAA.edi

Next, copy an XML file into the "outbox" directory of any FTP account (e.g. /tmp/ftpuser1/outbox) and you should see the file proxy picking it up, sending it to the partner "mendelson" and writing the conformation log as shown below

2012-01-13 01:47:14,437 [-] [primary-2] DEBUG xml-file-poller-outSequence Sync response received : SyncResponseInfo{messageUUID=273fd757-8186-49c5-a9e8-c401accd194d, version='1.2', to='adroitlogic', from='mendelson', msgID='<mendelson_opensource_AS2-1326399434119-39@mendelson_adroitlogic>', originalMessageID='<2018414300.5.1326399433410.JavaMail.asankha@asankha>', errorInfo=null, mic='CROdIf/sLUHOIe7Uq5uY/ak5JmE=, sha1', failed=false, signed=true, disposition='automatic-action/MDN-sent-automatically; processed', containingSyncMDN=true, containingTransportResponse=false, transportStatusCode=0}
2012-01-13 01:47:14,438 [-] [primary-2] INFO xml-file-poller-outSequence Completed sending file : /tmp/ftpuser1/outbox/Noname1.xml to mendelson
In this topic
In this topic
Contact Us