Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
thomas_jung
Developer Advocate
Developer Advocate
16,297

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-fea...

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.

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');
}



48 Comments