Skip to Content

In my previous blogOptimizing BLS Performance for XML Handling in SAP MII I explored different methods for performance optimization for large XML parsing in BLS transaction in SAP MII. It is often required in such scenarios to compress the data (which may be XML message or any data) before sending to external systems via HTTP or SOAP web service calls. This is because the original message size may be fairly large (few MBs) which makes it difficult to send via HTTP. Unfortunately there is no standard action block or mechanism available in SAP MII for data compression except the ZIP Archive action which creates zip files directly from existing files and saves into a zip file as well, which needs to be saved into the filesystem. Reading from and writing into file may be unnecessary here as the scenario demands simply compressing the data stored in Local or Transaction property and directly assign it to the XML element of the request message for the HTTP or Web Service call which accepts the compressed binary data. Also the standard action block may not support GZIP compression which is a better method for streaming data compression. Also there is no action block available for decompression or unzipping of compressed data which may be received by MII from external systems in opposite scenarios.

In this situation there are hardly any choice other than creating a new custom action block for GZIP compression and decompression which can work on input and output of string and binary data without writing into filesystem. It is one of the most strong and useful feature of SAP MII providing the Java APIs to write custom actions thus in a way enhancing the MII platform itself. The SDK for custom action is available directly for download in SAP MII server in the Custom Actions menu. To write custom action you need to write the Java classes using the APIs provided in the Custom Action SDK and finally compile and create a Java archive which can be deployed in the server, after which it will be available as an action block in BLS editor in MII workbench which can be used in any BLS transactions. You can refer the Custom Action Development Guide  available in SMP for complete technical reference on how to develop a custom action in MII 12.1/12.2.

Typically the custom action in MII requires two Java classes – Handler class (for the action and parameter definitions and implementation) and Dialog class (for the configuration dialog layout and implementation). In this case we do not need the configuration dialog of the action block as the input and output should be linked by the Link Configuration only. The zip action should have one input parameter (StringData) to pass the data to be compressed which can be text or XML or any other data type as required. The output of the action block should be the compressed (zipped) output, along with the status and status message text for the operation. Using the GZIP compression library in Java the data input stream can be compressed or decompressed. As there will be no configuration dialog for this action we don’t need to create the configuration dialog class and just need one handler class in the DataZipHandler.java to define and implement the action as below.

package com.mii.customactions.datazip;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPOutputStream;
import com.sap.lhcommon.common.LogLevel;
import com.sap.lhcommon.common.VariantDataTypes;
import com.sap.lhcommon.exceptions.DataConversionException;
import com.sap.lhcommon.util.Base64Util;
import com.sap.xmii.bls.sdk.Action;
import com.sap.xmii.bls.sdk.IActionInstance;
import com.sap.xmii.bls.sdk.Input;
import com.sap.xmii.bls.sdk.InvalidVariableException;
import com.sap.xmii.bls.sdk.Outputs;
public class DataZipHandler
{
     /**
To ensure the parameters are always named the same, it is good practice
to create a static final string with the parameters name. This also make
it easy to use across actions.
      */
     private static final String PARAM_OUTPUT_DATAZIP = "Output";
     private static final String PARAM_OUTPUT_MESSAGE = "DataZipReturnMessage";
     private static final String PARAM_OUTPUT_SUCCESS = "DataZipSuccess";
    @Action(name = "DataZip", icon = "/com/mii/customactions/datazip/icons/zip.jpg" ) // This annotation tells the engine that this is an
                                   // action available to execute.
    @Outputs(names = {PARAM_OUTPUT_DATAZIP, PARAM_OUTPUT_MESSAGE, PARAM_OUTPUT_SUCCESS },
                   types = { VariantDataTypes.STRING, VariantDataTypes.STRING, VariantDataTypes.BOOLEAN })
                        // This annotation tells the engine the output parameters
                         // to be returned.
                   public static void DataZip(
                             IActionInstance instance, // Besides the basic types,
                                                            // the IActionInstance interface is
                                                            // the only
                                                           // other type allowed to be
                                                            // defined in
                                                           // parameter list of a custom
                                                            // action.
                             @Input(name = "StringData") String stringData)
throws InvalidVariableException {// Exceptions can be thrown directly from
// the actions without causing critical
// execution failures. These exceptions
// will be caught and logged by the engine
// and will cause the Success flag to be
// set to false.
// The following code describes how to set an actions output.
         try{
                   //define the output stream
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              GZIPOutputStream gzip = new GZIPOutputStream(out);
              //compress and write the data
              gzip.write(stringData.getBytes());
              gzip.close();
              //encode the binary data into Base64 and pass to the output as string
              String outputString = new String(Base64Util.encode(out.toByteArray()), "UTF-8");
               instance.setActionResult(PARAM_OUTPUT_SUCCESS, true);
              instance.setActionResult(PARAM_OUTPUT_MESSAGE, "Zip Successful");
              instance.setActionResult(PARAM_OUTPUT_DATAZIP, outputString);
       } catch (Exception ex) {
            try{
            instance.log(LogLevel.ERROR, ex.getMessage());
            instance.setActionResult(PARAM_OUTPUT_SUCCESS, false);
            instance.setActionResult(PARAM_OUTPUT_MESSAGE, "Error on zip operation:"+ex.getMessage());
            }
            catch (DataConversionException e) {
                 instance.log(LogLevel.ERROR, e.getMessage());
            }
       }
    } 
}

Note that in this class the input and output parameters of the action block are defined along with with the action implementation method DataZip. After creating the binary compressed datastream from the StringData input property using the GZIP library it is encoded as Base64 encoded string to pass as binary string data inOutput property. Similary another handler class called DataUnzipHandler is created for the unzipping/decompressing the zipped/compressed data.

package com.mii.customactions.datazip;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPInputStream;
import com.sap.lhcommon.common.LogLevel;
import com.sap.lhcommon.common.VariantDataTypes;
import com.sap.lhcommon.exceptions.DataConversionException;
import com.sap.lhcommon.util.Base64Util;
import com.sap.xmii.bls.sdk.Action;
import com.sap.xmii.bls.sdk.IActionInstance;
import com.sap.xmii.bls.sdk.Input;
import com.sap.xmii.bls.sdk.InvalidVariableException;
import com.sap.xmii.bls.sdk.Outputs;
public class DataUnZipHandler
{
     /**
To ensure the parameters are always named the same, it is good practice
to create a static final string with the parameters name. This also make
it easy to use across actions.
      */
     private static final String PARAM_OUTPUT_DATA = "Output";
     private static final String PARAM_OUTPUT_MESSAGE = "DataUnZipReturnMessage";
     private static final String PARAM_OUTPUT_SUCCESS = "DataUnZipSuccess";
    @Action(name = "DataUnZip", icon = "/com/mii/customactions/datazip/icons/unzip.jpg" ) // This annotation tells the engine that this is an
                                   // action available to execute.
    @Outputs(names = {PARAM_OUTPUT_DATA, PARAM_OUTPUT_MESSAGE, PARAM_OUTPUT_SUCCESS },
                   types = { VariantDataTypes.STRING, VariantDataTypes.STRING, VariantDataTypes.BOOLEAN })
                        // This annotation tells the engine the output parameters
                         // to be returned.
                   public static void DataUnZip(
                             IActionInstance instance, // Besides the basic types,
                                                            // the IActionInstance interface is
                                                            // the only
                                                           // other type allowed to be
                                                            // defined in
                                                           // parameter list of a custom
                                                            // action.
                             @Input(name = "ZippedData") String zippedData)
throws InvalidVariableException {// Exceptions can be thrown directly from
// the actions without causing critical
// execution failures. These exceptions
// will be caught and logged by the engine
// and will cause the Success flag to be
// set to false.
// The following code describes how to set an actions output.
         try{
                //define the input and output streams
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                  byte[] buf = new byte[1024];
                  ByteArrayInputStream in = new ByteArrayInputStream(Base64Util.decode(zippedData));
                  GZIPInputStream gzip = new GZIPInputStream(in);
                  int len;
                  //read the gzip/compressed data and write as the uncompressed data stream
                  while ((len = gzip.read(buf)) > 0) {
                  out.write(buf, 0, len);
                  }
                  //assign output stream to the output parameter
                  String outputString = out.toString();
                   instance.setActionResult(PARAM_OUTPUT_SUCCESS, true);
                  instance.setActionResult(PARAM_OUTPUT_MESSAGE, "UnZip Successful");
                  instance.setActionResult(PARAM_OUTPUT_DATA, outputString);
       } catch (Exception ex) {
            try{
            instance.log(LogLevel.ERROR, ex.getMessage());
            instance.setActionResult(PARAM_OUTPUT_SUCCESS, false);
            instance.setActionResult(PARAM_OUTPUT_MESSAGE, "Error on unzip operation:"+ex.getMessage());
            }
            catch (DataConversionException e) {
                 instance.log(LogLevel.ERROR, e.getMessage());
            }
       }
    } 
}

Once the classes are compiled create a JAR package of the action block using the following command:

jar -cf DataZip.jar catalog.xml ./com/mii/customactions/datazip/

This will create a JAR file to be uploaded as the assembly file for custom action in MII.
Open System Management->Custom Actions (in MII 12.1) or System Resources-> Custom Actions (in MII 12.2) and upload the JAR file and deploy.

Open MII workbench after deleting the temporary applet files from Java Control Panel. The custom actions will be available under DataZip category.

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Michelle Crapo
    The information here is very valuable for someone that gets into MII rarely.  I support it now.  I am the only one who knows anything about MII here.  And my main job is programming in ABAP.  So I’m book marking this page in case I need the information.

    Now a suggestion – Keep in mind that I found the blog useful.  It would be a big help to put the code in a code box.  It just a nice way of breaking the code out from your blog.  Pictures are always good.  Although I don’t really think they are needed here.   Text changes help too.  Some bold text between points maybe?

    Just my suggestions for your next blog.  I hope you keep them coming.  This one was great!

    Michelle

    (0) 
    1. Dipankar Saha Post author
      Thanks Michelle!
      Usually I put codes in textarea in my blogs but somehow I missed it this time. Thanks for pointing it out. Actually I guess there should be format control in the RTF blog editor for code which will make it easier rather than editing the HTML
      Dipankar
      (0) 
      1. Michelle Crapo
        YES!!!  Now I can copy the code easily.  Yes, I really hate some of the blog formatting.  It is so frustrating. 

        However, it is changing!  I’m really excited about it. I have no idea when or what it will change to.  But we have been promised better much better.

        Thank you!

        Michelle

        (0) 

Leave a Reply