Skip to Content

This blog is part of the larger series on all new developer features in SAP HANA SPS 09:http://scn.sap.com/community/developer-center/hana/blog/2014/12/02/sap-hana-sps-09-new-developer-features

Already we have looked at the new XSJS Database Interface in SPS 09. However this isn’t the only new core XSJS API in SPS 09.  There are several other new core XSJS APIs which we will now explore further in this blog.

SMTP

Probably the most requested API was an SMTP one.  Sending email from XSJS is an obviously valuable feature and therefore we had it on our backlog since SPS 06. For one reason or another it never quite shipped – until now.  With SPS 09 we add a rather full featured SMTP implementation as its own XSJS API. This includes multi-part, attachments, secure sockets, etc.

Here is a simple example of the API:


//create email from JS Object and send
var mail = new $.net.Mail({
    sender: {address: "demo@sap.com"},
    to: [{ address: "demo@sap.com"}],
    subject: "XSJS Email Test",
    parts: [ new $.net.Mail.Part({
        type: $.net.Mail.Part.TYPE_TEXT,
        text: "The body of the mail.",
        contentType: "text/plain"
    })]
});
var returnValue = mail.send();
var response = "MessageId = " + returnValue.messageId + ", final reply = " + returnValue.finalReply;



Here is a slightly more complex example that also processes a file attachment:


//create email from JS Object and send
var mail = new $.net.Mail({
    sender: {address: "demo@sap.com"},
    to: [{ address: "demo@sap.com"}],
    subject: "XSJS Email Test",
    parts: [ new $.net.Mail.Part({
        type: $.net.Mail.Part.TYPE_TEXT,
        text: "Atachement Test",
        contentType: "text/plain"
    })]
});
mail.parts.push(new $.net.Mail.Part({
  type: $.net.Mail.Part.TYPE_ATTACHMENT,
  data: getImage(),
    contentType: "image/jpg",
    fileName: "myPicture.jpg"}));
var returnValue = mail.send();
var response = "MessageId = " + returnValue.messageId +
               ", final reply = " + returnValue.finalReply;



And of course there is a new configuration screen as part of the XS Admin tool to setup your SMTP server connection. This also gives you some idea of the many authentication types and SMTP settings we support.

HANABlog4.png

ZIP

On the subject of often requested features, another very popular request was for ZIP/GZIP support.  SPS 09, thankfully, also delivers this feature.  We add an XSJS API which allows you to create/read/process ZIP and GZIP archives. It also has some nice optimizations which allow you to process a large ZIP from within the database result set or the request body without having to copy the byte array into the JavaScript. The actual ZIP processing will take place in the kernel and only a single part/object can be returned and materialized in the JavaScript VM.  This helps to process larger ZIP archives without needing to extend the memory allocation of the JavaScript VM.

In this simple example you can see how the $.util.Zip library works.  You can create folder structures in the ZIP simply by specifying the full path as you create the content:


var zip = new $.util.Zip();
zip["folder1/demo1.txt"] = "This is the new ZIP Processing in XSJS";
zip["demo2.txt"] = "This is also the new ZIP Processing in XSJS";
$.response.status = $.net.http.OK;
$.response.contentType = "application/zip";
$.response.headers.set('Content-Disposition', "attachment; filename = 'ZipExample.zip'");
$.response.setBody(zip.asArrayBuffer());



But we can also process the ZIP directly from the Web Request object; avoiding any materialization of the content into the JavaScript VM.


var zip = new $.util.Zip($.request.body);



Similarly we can directly access the ZIP contents from within the Result Set object of a database query:


statement = conn.prepareStatement("select data from EXAMPLETABLE where id=1");
var rs = statement.executeQuery();
if (rs) {
 while (rs.next()) {
        //Load Zip From ResultSet
        var loadedZip = new $.util.Zip(rs, 1);
 }



XML

Another new API in SPS 09 is an expat-based SAX XML Parser.  It supports parsing from a JavaScript String, JavaScript Array Buffer (with encodings in US-ASCII, UTF-8, and UTF-16), external entity, or $.web.Body object. Like the ZIP API, this allows for XML processing without always having to transfer the source XML into the JavaScript VM when working from the web body, external entity, or database result set.

Here is a simple example where we parse from a hard coded JavaScript string. This examples shows how you can register JavaScript functions as callback handlers for parsing events.


//create a new $.util.SAXParser object
var parser = new $.util.SAXParser();
//parse XML from String
var parser = new $.util.SAXParser();
var xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
          '<!-- this is a note -->\n'+
           '<note noteName="NoteName">'+
               '<to>To</to>'+
               '<from>From</from>'+
               '<heading>Note heading</heading>'+
               '<body>Note body</body>'+
           '</note>';
var startElementHandlerConcat = "";
var endElementHandlerConcat = "";
var characterDataHandlerConcat = "";
parser.startElementHandler = function(name, atts) {
    startElementHandlerConcat += name;
    if (name === "note") {
        startElementHandlerConcat += " noteName = '" + atts.noteName + "'";
    }
    startElementHandlerConcat += "\n";
};
parser.endElementHandler = function(name) {
    endElementHandlerConcat += name + "\n";
};
parser.characterDataHandler = function(s) {
    characterDataHandlerConcat += s;
};
parser.parse(xml);
var body = 'Start: ' + startElementHandlerConcat + '</br>' +
           'End: ' + endElementHandlerConcat + '</br>' +
           'Charcter: ' + characterDataHandlerConcat + '</br>';
$.response.status = $.net.http.OK;
$.response.contentType = "text/html";
$.response.setBody(body);



Improved Multi-Part support

When working with complex HTTP Request or Response objects, often multi-part objects are used. For example in OData batch request, each record in the batch is a separate part in the request object.  The existing entity object APIs in XSJS have been extended in SPS 09 to help with the processing of multi-part entities.


// Handling of multipart requests and responses in xsjs files:
var i;
var n = $.request.entities.length;
var client = new $.net.http.Client();
for (i = 0; i < n; ++i) {
   var childRequest = $.request.entities[i].body.asWebRequest();
   client.request(childRequest, childRequest.headers.get("Host") + childRequest.path);
   var childResponse = client.getResponse();
   var responseEntity =  $.response.entities.create();
   responseEntity.setBody(childResponse);
}



Asynchronous Request Completion

In SPS09 we added a new field to the response object where a follow-up JavaScript function event handler can be registered for additional processing upon request completion.


$.response.contentType = "text/html";
var output = "Hello, World! <br><br>";
var conn = $.db.getConnection();
var pstmt = conn.prepareStatement( 'select * from DUMMY' );
var rs = pstmt.executeQuery();
if (!rs.next()){
  $.response.setBody( "Failed to retieve data");
  $.response.status = $.net.http.INTERNAL_SERVER_ERROR;
}
else {
  output += "This is the response from my SQL: " +
            rs.getString(1);
  $.response.setBody(output);
  $.response.followUp({
     uri : "playground.sp9.followUp:other.xsjs",
     functionName : "doSomething",
     parameter : {
         a : "b"
     }
  });
}



Text Access API

Translatable text objects are defined in the HANA Repository as design time objects called HDBTEXTBUNDLE. We already have an API in SAPUI5 to access these text objects from the client and the stored procedure, TEXT_ACCESSOR, for SQL/SQLScript access. In SPS09 we offer the same functionality now as an XSJS API.  Please note that unlike all the other APIs listed in this blog, this is not implemented in the $ namespace. Instead it is written as an XSJSLIB available from sap.hana.xs.i18n package.


var textAccess = $.import("sap.hana.xs.i18n","text");
var bundle = textAccess.loadBundle("playground.sp9.textAccess","demo1");
var singleText = bundle.getText("demo");
var replaceText = bundle.getText("demo2",['1001']);
var oAllTexts = bundle.getTexts();
//$.response.setBody(singleText);
$.response.setBody(replaceText);
//$.response.setBody(JSON.stringify(oAllTexts));



Support for Virus Scan Interface (VSI) for applications

New XSJS API ($.security.AntiVirus) to access and use the SAP Virus Scan Interface from your server side JavaScript coding.

  • The scan needs a Virus Scan Adapter (VSA) to be installed on the host
  • The setup and configuration is available with SAP note 2081108
  • This class uses the SAP certified interface NW-VSI 2.00 (see SAP note 1883424)
  • For a list of the AV products supported, see SAP note 1494278

Code Sample for using the new Virus Scan Interface from XSJS:


try {
  //create a new $.security.AntiVirus object using the default profile
  var av = new $.security.AntiVirus();
  av.scan($.request.body);
} catch (e) {
  $.response.setBody(e.toString());
}



Secure Store

The secure store API can be used to securely store data in name/value form. Applications can define a secure store object file and refer to this design time object in the application coding. The XSEngine takes care of the encryption and decryption and also provides the persistence for the data. There are two visibility options for the data a) Visible application wide All users of the application share the same data and can decrypt/encrypt data e.g.: passwords for a remote system b) Visible application wide but with a separation on user level: Every user of the application can have it’s own encrypted data which can only be decrypted by the user himself e.g. credit card numbers/pin codes etc.



function store() {
  var config = {
    name: "foo",
    value: "bar"
  };
  var aStore = new $.security.Store("localStore.xssecurestore");
  aStore.store(config);
}
function read() {
  var config = {
    name: "foo"
  };
  try {
    var store = new $.security.Store("localStore.xssecurestore");
    var value = store.read(config);
  }
  catch(ex) {
    //do some error handling
  }
}
var aCmd = $.request.parameters.get('cmd');
switch (aCmd) {
case "store":
  store();
  break;
case "read":
  read();
  break;
default:
  $.response.status = $.net.http.INTERNAL_SERVER_ERROR;
  $.response.setBody('Invalid Command');
}



To report this post you need to login first.

46 Comments

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

  1. Sergio Guerrero

    great features. I totally agree on the SMTP feature that it is one of the most request ones. Every client I have talked to, they have been asking for the past 18 months. I think that the secure store and the zip features are also great. You didn’t mention in this post, but I am personally exited to start using the XSDS API. I have a lot of colleagues from MSFT background and they always ask for LINQ, which now we have something very similar. Thank you and your team for the much effort on delivering these features.

    (0) 
    1. Thomas Jung Post author

      I do have a separate blog post coming on XSDS in a few days.  There was so much new functionality that I had to break things down into multiple blogs.

      (0) 
  2. Eric Zhou

    Hi Thomas,

    As you mentioned in ‘Improved Multi-Part support’, i don’t know how to call xsjslib function from OData Batch Request.

    I tried many times ,but always failed because did not excute the function in xsjslib. And i add the break point at the first line in this xsjslib function, also failed ,actually it did not go to the break point.

    If i used procedure instead of xsjslib function, it works well.

    Can you help me to solve this problem , thanks so much!

    (0) 
    1. Thomas Jung Post author

      You haven’t provided any details on what you are doing, making it very difficult to respond.  I can say that the XSJS exits in OData are very common. You will find examples in the online help and in the SHINE content.  Perhaps studying the examples will help you find your problem. 

      (0) 
  3. Michael Berres

    Hi Thomas,

    I’m running a HANA XS application on a HANA Trial instance and want to use the new features (especially the SMTP feature), but when I try to create a new $.net.Mail.Part-Object, execution fails with error “reference to undefined property $.net.Mail.Part”.

    However, creating a $.net.Mail-Object perfectly works. Unfortunately, without the …Part-Object sending mails is not possible. Can you help me with this issue?

    Thank you!

    (0) 
    1. Thomas Jung Post author

      As far as I know none of the Trial instances have been upgraded to SPS09 yet.  This would be why the APIs aren’t complete and don’t work.  Check the release level of your system but if its the HCP Trial I believe those are still on SPS07. The Developer Edition of HANA is SPS08.

      (0) 
      1. Michael Berres

        Thank you for the fast answer! I have two more questions:

        – Where can I check the release level of my system? I can’t find such an option in my HANA Cloud Platform Cockpit.

        – I’m an SAP internal. Is is possible for me to get access to a system with SP9? If yes, who can I ask?

        (0) 
        1. Thomas Jung Post author

          > Where can I check the release level of my system? I can’t find such an option in my HANA Cloud Platform Cockpit.

          From the HANA Studio right mouse click on System entry and choose Configuration and Monitoring -> Open Administration.  From the Overview tab you will see the version information of your server.

          >I’m an SAP internal. Is is possible for me to get access to a system with SP9? If yes, who can I ask?

          Of course.  Check the system images available in the CAL. Or of course you have access to the installers and can just install on your own hardware.

          (0) 
  4. Johannes Jagst

    Hi Thomas,

    thank you for sharing !

    When I try your exapmle xsjs, I always get this error message:

    Error: error coulnd’t establish connection

    Or if I try with SSL/TLS I get this error:

    Error: error coulnd’t get certificate

    For SMTP Settings I tried my gmail account:

    Host: smtp.gmail.com

    Port: 465 (or 587)

    and no Transport Security Settings

    For Authentication Type: Auto (with my mail adress and pw)

    Any suggestions?

    How is it possible to set the port for SSL and TSL in the one “PORT” field ?

    Thank you !

    (GMAIL Settings: SMTP-Einstellungen für Google Apps zum Senden von Mails über einen Drucker, Scanner oder eine Anwendung – Google Apps-Hi…)

    (0) 
    1. Thomas Jung Post author

      I don’t have specific experience with using GMail for this. We used Microsoft Exchange within SAP and it was as simple as supplying the port and address.  I would think that for Gmail you would have to look into Google’s SMTP relay service. 

      (0) 
      1. Kannan Kumar

        Hi Thomas,

        It would be helpful if you could share what is the port and address you specified for Microsoft Exchange within SAP.
        Also what other SMTP configurations are required to use the $.net.Mail( ) API.

        Thanks a ton.

        (0) 
        1. Thomas Jung Post author

          Why would I share SAP’s internal configuration on a public blog? That makes no sense.

          >Also what other SMTP configurations are required to use the $.net.Mail( ) API.

          You need whatever SMTP configuration matches your SMTP server requirements. You should discuss this with whomever runs your SMTP server to match up to those requirements.

          (0) 
  5. Jaqueline Pollak

    Hi Thomas,

    I have some trouble using the new Asynchronous Request Completion feature.

    I created a file with the example code. When I run the example code, the method (which is given in the followUp method) is not called.

    Are there any requirements/limitations for the method I registered in the follow up?

    Do I have to enable the usage of asynchronous request completion?

    Thank you.

    (0) 
    1. Thomas Jung Post author

      >Are there any requirements/limitations for the method I registered in the follow up?

      None that I’m aware of.

      >Do I have to enable the usage of asynchronous request completion?

      No.

      If you have issues recreating the example of the online documentation, then I would suggest opening a support ticket.

      (0) 
  6. Selvaguru Manokaran

    Hi Thomas,

         Really helpful blog! I’m trying to create zip file in the server. Zip file will contain XML files.

    zip[“test.xml”]=”<xml …> <body>……..</body></xml>”;

    $.response.status = $.net.http.OK;

    $.response.contentType = “application/zip”;

    $.response.headers.set(‘Content-Disposition’, ‘attachment; filename = \’Encrypted.zip\”);

    $.response.setBody(zip.asArrayBuffer());

    On client side, I receive some encrypted content in the response. Not sure how to save the file in the client side.. your help would be appreciated .

    (0) 
    1. Thomas Jung Post author

      I’m not sure what problem you are having exactly. I cut and pasted your code you posted into an XSJS service in my system, ran it, and it downloaded a zip file exactly as it should.

      (0) 
      1. Selvaguru Manokaran

        Hi Thomas,

        This is the client ajax call.

        $.ajax({

                    url: “/test/server/test.xsjs”,

                    type: “POST”,

                    data: JSON.stringify(data),

                    dataType: “json”,

                    beforeSend: function(xhr) {

                        xhr.setRequestHeader(“X-CSRF-Token”, CSRFToken);

                    },

                    success: function(ret) {

                        var link = document.createElement(“a”);

                        link.href = ‘data:application/zip;charset=utf-8,’ + ret;

                        link.click();

                    },

                    error: function(body, status, xhr) {

                        //Error handling goes here

                    }

                });

        “ret” is the response content.

        It downloads the zip file. But it’s corrupted. Am I handling the response wrong ?

        (0) 
        1. Thomas Jung Post author

          First, just test the service standalone.  Does it download the content correctly?  Second, when call this service with the download from the UI, I think you are making things too complicated.  Just open the call to the service in a new window.  There is an example of this in the POWorklist app in SHINE.  Here is the code snippet from that part:

          /Zip Functionality

            if (oEvent.getSource().getId()==”btnZip”){

            // xsjs will handle the content type and download will trigger automatically

            window.open(“/sap/hana/democontent/epmNext/services/poWorklistQuery.xsjs?cmd=Zip”);

            return;

            }

          (0) 
          1. Selvaguru Manokaran

            Thomas,

                 The solution you’ve mentioned will fire a GET call. In our case, it should be a POST call. Also, I have to pass CSRF token before the POST call.

            (0) 
                1. Thomas Jung Post author

                  CSRF tokens shouldn’t be required for GET requests. Only non-GET requests require them when the XSRF protection is turned on.

                  (0) 
  7. Sakthivel Elango

    Regarding the async request completion feature, Is it the same if we call a xsjs function manually once the response body is set ?

    $.response.setBody(“<content>”);

    playground.sp9.followUp:other.xsjs.doSomething() // call the xsjs function manually

            

                   vs

    $.response.setBody(output);

    $.response.followUp({

         uri : “playground.sp9.followUp:other.xsjs”,

         functionName : “doSomething”

    });

    (0) 
    1. Thomas Jung Post author

      No.  In your first example the doSomething is executed before the response is even sent. In the second example the followUp is executed upon acceptance of the response by the other party.

      (0) 
  8. Denis Descause

    Hi Thomas,

    thanks for sharing that.

    I was surprised to see all emails were empty when we moved to SP9 from SP8, but i now understand why, since it was not using parts before but just a text body.

    How can i configure smtp within XS, in the new interface the SMTP configuration menu line is inactive . What do we need to do to activate it ?

    thanks

    best

    Denis

    EDIT:

    never mind , i found the answer . i was missing the right roles even in SYSTEM account.

    once i added

    • sap.hana.xs.admin.roles::RuntimeConfAdministrator
    • sap.hana.xs.admin.roles::SMTPDestAdministrator

    i can now see the SMTP

    (0) 
  9. Denis Descause

    How do you provide the ‘Display name’ of the from email adress

    something like when user red the email it looks like

    From: Fancy Name <noreply@myserver.com>

    where and how do i specify the “Fancy name” string

    thanks,

    best

    DEnis

    (0) 
    1. Thomas Jung Post author

      Just use the name part of the sender interface:

      JSDoc: Class: Mail

      sender :object

      Property used for initializing “sender” property of the mail. The object contains three properties. The first one is the name of the person, the second one is the actual mail address, the third one is the name encoding ex. {name: “John Doe”, address: “john.doe@sap.com“, nameEncoding: “UTF-8”} or {name: “John Doe”, address: “john.doe@sap.com“} or {address: “john.doe@sap.com“} or just “john.doe@sap.com“. The name and the name encoding of the person can be skipped, but the mail address cannot. If ‘address’ property is not set, the mail won’t be send at all. The “sender” property is a mandatory field, if it is not set or is not set properly the mail won’t be sent.

      (0) 
  10. Ulrich Wolf

    Hi Thomas,

    is there a way to use the mail functionality through Stored Procedures or do we keep using sys.statisticsserver_sendmail_dev for that?

    Thanks in advance!

    Best Regards,

    Uli

    (0) 
  11. Lukas Heimann

    Hi Thomas,

    I’m struggeling to unzip a file uploaded by the user with the ZIP API. How must the upload be handled at the client to make the your snippet (at the server) work?

    var zip = new $.util.Zip($.request.body);


    I tried to upload the file via a standard HTML-file-upload with method="post" enctype="multipart/form-data", but then your snippet crashes, as body seems to be undefined. Is there another alternative to upload the file?


    Thank you for your help!


    Best Regards,

    Lukas

    (0) 
    1. Thomas Jung Post author

      So that $.request.body would expect the ZIP content directly in the request probably via AJAX. Something like this where we pull the form data out of the client side element send it directly:

      var formEle = jQuery.sap.domById(“app–fileUploader”);

               var form = $(formEle).find(“form”)[0];

               var fd = new FormData(form);

                          var ajaxCall = jQuery.ajax({

                             url : url,

                             type : ‘PUT’,

                             processData: false,

                             contentType: false,

                             data : fd,

                 

                             headers : {

                                    “SapBackPack” : sapBackPack,

                                    “X-CSRF-Token” : getCSRFToken()

                             },

                             async: false

                      });

      Otherwise you will need to process the multi-part request on the server side to access the part with the attachment. Something like this:

      // read request body

           var content = $.request.entities[0].body

      (0) 
      1. Lukas Heimann

        Thank you very much for your answer. The upload seems to work now, as I guess from what is send over HTTP. However, I still encounter a strange problem.

        The ZIP-archive I try to upload consists of one single HTML-document. When handing it over to var zip = new $.util.Zip($.request.body), I catch an exception saying Failed to locate the first file in zip archive. Initial loading seems to work though, since when I upload a non-zip file, I catch an exception saying Failed to open zip archive. Do you know, what the problem might be? Is there some pre-processing of the zip to be done?

        Not focussing on these details: All in all, I want to process data from XLSX-files, that are basically zipped xmls. So I try unzipping them, opening the required xml an parse it via SAX. Is there a more simple approach to achieve the xlsx-processing, or is my general approach valid?

        (0) 
  12. Shyam Uthaman

    Hi Thomas,

    I have a webservice which I tested in SOAP UI by sending an XML with some parameters and receiving the corresponding XML output as well. But unfortunately, it doesn’t work from HANA (400: bad request error) . Below is the code – I have 3 questions based on this.

    var destination = $.net.http.readDestination(“<package_path”,”<Service_name>”);

    var client = new $.net.http.Client();

    var request = new $.net.http.Request($.net.http.GET, “<service_path>”);

    var data = <the xml code>

    try{      

              request.setBody(data);

              client.request(request, destination);

            

              var response = client.getResponse();

              

              var resBody = response.body.asString();

            $.response.contentType = “text/xml”;

                $.response.status = $.net.http.OK;

                $.response.setBody(resBody);

    }

    catch(errObj){

        $.response.setBody(errObj.message);

    Questions:

    1. Does the code look ok to you?

    2. The webservice link where i need to send this looks linke abcd.xyz1.com:443/GHJ/YUI/123.

    The xshttpdest file I have maintained looks like:

    host = “abcd.xyz1.com“;

    port = 443;

    description = “WebService”;

    useSSL = true;

    proxyType = none;

    sslAuth = client;

    useProxy = false;

    proxyHost = “”;

    timeout = 0;

    authType = none;

    Now in the XSJS, I have put the last part of the link:

    var request = new $.net.http.Request($.net.http.GET, “GHJ/YUI/123“);


    Is this the correct way?

    3. I read in Web Service Consumer: HTTP Code 400  : Bad Request question that the request can also get compressed as it leaves the DB and this can also cause problems. This link is for a non-HANA scenario though. How would I make sure no compressed requests are sent in a native HANA development setup?



    Am stuck with this webservice problem for quite a while now.

    Please suggest your views on this..


    Thanks,

    Shyam

    (0) 
  13. Vevek Pj

    Hi Thomas, Is there any possible way to include approve/reject button to the email generated from our xsjs service. Can you please share your views in this. Thanks, Vevek

    (0) 
    1. Thomas Jung Post author

      That doesn’t really have anything to with the XSJS APIs for sending. One way you could have voting buttons is to send an HTML based email and in the actions for the buttons call REST services to process the vote.

      (0) 
      1. Vevek Pj

        Thanks for the quick response.I am new to xsjs. Can you please provide html code snippet to add voting button in xsjs code. Is it something to add in mail parts?. -Vevek

        (0) 
      2. Thomas Matecki

        Hi Thomas, I’m considering using xsjs for my website. Does it support flash animation?


        Just kidding.


        Has SAP ever considered implementing a point system similiar Stack overflow? Restricting the actions users are allowed to perform could be contingent upon the number of points that earned which may improve the quality of post.

        (0) 
  14. Gabor Csernak

    Hi Thomas,

    I have an issue with SMTP settings. I have an SMTP AUTH account and I downloaded the required certificate object. After I imported it to my landscape I changed my SMTP settings and saved, it said everything is ok, but after I reloaded the page Trust Store field become empty again. Do you have any idea what is the problem?

    (0) 
    1. Thomas Jung Post author

      There is nothing built into the Job Scheduler itself to do. You could certainly add that to the logic of the service which we you are scheduling however.

      (0) 

Leave a Reply