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

With the recent release of SAP HANA SPS6 (Revision 60), developers working on SAP HANA can expect a wide variety of new and improved features.  In this blog I would like to highlight a few of the most prominent features for developers building SAP HANA native applications.

Development Workflow

First, we have various improvements to the overall development workflow; most of these improvements focused on the SAP HANA Studio. For example SAP HANA Studio in SPS6 brings in Eclipse 4.2, introduces new object search dialogs, better integration of the view modeling tools into the Repository view and Project Explorer, keyboard shortcuts and toolbar icons for check and activate, improved error display, and numerous other small enhancements. 

One of the enhancements to the workflow actually comes outside of the HANA Studio.  There is a new Application Lifecycle Manager web application which is part of HANA. For developers one of the biggest advantages of this new tool is an application creation wizard.  From this tool, a developer can quickly create a Schema, .xsapp, .xsaccess and even a local developer role which already has the necessary package and schema access rights. 

For a quick overview of these various tool improvements and how they streamline the development process, have a look at the following video.

Browser Based IDEs

One of the new features of SAP HANA SPS6 is the inclusion of new browser based development tools for SAP HANA native application development. These tools make it fast and easy to get started creating html, server side JavaScript, and OData services directly within SAP HANA. 

No longer are you required to install the SAP HANA Studio and Client if you only need to do some basic development object creation or editing in the SAP HANA Repository. This means you can be coding your first application within seconds of launching a SAP HANA instance.

The usage of such browser based development tools is particularly appealing to cloud-based SAP HANA development scenarios, like SAP HANA one.  You only need access to the HTTP/HTTPS ports of the SAP HANA server and avoid the need for any additional client side software installation. A browser pointed at the SAP HANA server is all you need to begin development.

On the openSAP forums, I was recently asked why SAP decided to invest in browser based tools when we already have SAP 

HANA Studio. I'm going to plagiarize myself and repost the response here:

Web IDEs require no installation on the client side.  So if you are using a MacOS device today, you can't use HANA Studio for development tasks as it isn't supported (has no REGI / HANA client). Therefore the Web IDE offers an alternative if your OS doesn't support Studio.

Web IDEs run on the HTTP port. Especially in the cloud usage scenario, it’s probably easier to get this port open rather than the various ports used by the Studio. 

Web IDEs work on mobile devices. No you probably don't want to develop day to day on a mobile device, but what if you get a support call in the middle of the night?  It’s a great way to quickly check on something. I've used it to fix a demo in the middle of an event from my phone.  

Web IDEs are good for system admins that might want to only change a xsaccess file.  Even if they have HANA Studio, they will probably feel more comfortable with the direct edit/save workflow of the Web IDEs rather than the project check out /commit /activate of the HANA Studio. 

The Web IDEs allow you to delete even the .project file and .settings folders from repository packages. HANA Studio can't do this and commit back to the repository because these are key files to the project itself.  Therefore the Web IDEs can be used to clean up a package and make it ready for complete deletion.

Core Data Services (CDS) / HDBDD

Core data services (CDS) is a new infrastructure for defining and consuming semantically rich data models in SAP HANA. Using a a data definition language (DDL), a query language (QL), and an expression language (EL), CDS is envisioned to encompass write operations, transaction semantics, constraints, and more.

A first step toward this ultimate vision for CDS is the introduction of the hdbdd development object in SPS6. This new development object utilizes the Data Definition Language of CDS to define tables and structures. It can therefore be consider an alternative to hdbtable and hdbstructure. In the following video we explorer the creation of several tables within a single hdbdd design time artifact. 

Server Side JavaScript Outbound Connectivity

Server Side JavaScript (XSJS) was already the cornerstone of creating custom services within HANA to expose data to the outside world. In SPS6, the primary programming model of the HANA Extended Application Services gets outbound connectivity support as well.  Now developers can  call out of the HANA system and consume services via HTTP/HTTPS from other systems. This can be a way of combining real-time data from multiple sources or gathering data to store into SAP HANA.

In the following video we show two scenarios - calling to an ABAP system to get additional user details and consuming a Library of Congress image search services.  These are just two simple examples of the power of this new capability.

XSODATA Create/Update/Delete

Similar to the role of XSJS in SPS5, XSODATA services are already established as the quickest and easiest way to generate read-only REST services from existing tables and views within HANA. In SPS6, those capabilities are complemented with new support for Create, Update, and Delete operations as well.  No longer are you required to create a custom service in XSJS if you only need to add simple update capabilities to your service. Furthermore, if you need some simple validation or other update logic; it can be coded in a SQLScript exit mechanism from within the generic XSODATA service framework.

This video demonstrates these new Create/Update/Delete capabilities, a useful test tool for Chrome called Postman, and even how to use the SQLScript extension technique.

Source Code

Here are the various source code segments if you wish to study them further

The USER.hdbdd CDS example:

namespace sp6.data;
@Schema: 'SP6DEMO'
context USER {
           type SString : String(40);
           type LString : String(255);
   @Catalog.tableType: #COLUMN
     Entity Details {
               key PERS_NO: String(10);
               FIRSTNAME: SString;
               LASTNAME: SString;
               E_MAIL: LString;
     };
};

The popUsers.xsjs:

function insertRecord(userDet){
          var conn = $.db.getConnection();
          var pstmt;
          var query =
          'UPSERT "sp6.data::USER.Details" ' +
            'VALUES(?, ?, ?, ?) ' +
             'WHERE PERS_NO   = ? ';
          pstmt = conn.prepareStatement(query);
          pstmt.setString(1, userDet.ITAB.PERS_NO);
          pstmt.setString(2, userDet.ITAB.FIRSTNAME);
          pstmt.setString(3, userDet.ITAB.LASTNAME);
          pstmt.setString(4, userDet.ITAB.E_MAIL);
          pstmt.setString(5, userDet.ITAB.PERS_NO);
          pstmt.executeUpdate();
          pstmt.close();
          conn.commit();
          conn.close();
}
function populateUserDetails(){
          var user = $.request.parameters.get("username");
          var dest = $.net.http.readDestination("sp6.services", "user");
          var client = new $.net.http.Client();
          var req = new $.web.WebRequest($.net.http.GET, user);
          client.request(req, dest);
          var response = client.getResponse();
          var body;
          if(response.body){body = response.body.asString(); }
          $.response.status = response.status;
          $.response.contentType = "application/json";
          if(response.status === $.net.http.INTERNAL_SERVER_ERROR){
                    var error = JSON.parse(body);
                    $.response.setBody(error.ITAB[0].MESSAGE);
          }
          else{
                    var userDet = JSON.parse(body);
                    insertRecord(userDet);
                    $.response.setBody('User ' + userDet.ITAB.FULLNAME + ' Personel Number: ' + userDet.ITAB.PERS_NO + ' has been saved' );
          }
}
populateUserDetails();

The searchImages.xsjs:

function searchImages(){
          var search = $.request.parameters.get("search");
          var index = $.request.parameters.get("index");
          if(index === undefined){
                    index = 0;
          }
          var dest = $.net.http.readDestination("sp6.services", "images");
          var client = new $.net.http.Client();
          var req = new $.web.WebRequest($.net.http.GET, search);
          client.request(req, dest);
          var response = client.getResponse();
  var body;
          if(response.body){body = response.body.asString(); }
          $.response.status = response.status;
          if(response.status === $.net.http.INTERNAL_SERVER_ERROR){
                    $.response.contentType = "application/json";
                    $.response.setBody('body');
          }
          else{
                    $.response.contentType = "text/html";
                    var searchDet = JSON.parse(body);
                    var outBody =
                              'First Result of ' + searchDet.search.hits + '</br>'+
                              '<img src="' + searchDet.results[index].image.full + '">';
                    $.response.setBody( outBody );
          }
}
searchImages();

The user.xsodata:

service namespace "sp6.services" {
   "sp6.data::USER.Details" as "Users"
      create using "sp6.procedures::usersCreateMethod";
}

The usersCreateMethod.procedure:

CREATE PROCEDURE _SYS_BIC.usersCreateMethod(IN row SYSTEM."sp6.data::USER.Details", OUT error tt_error)
          LANGUAGE SQLSCRIPT
          SQL SECURITY INVOKER AS
BEGIN
/*****************************
          Write your procedure logic
*****************************/
declare lv_pers_no string;
declare lv_firstname string;
declare lv_lastname string;
declare lv_e_mail string;
select PERS_NO, FIRSTNAME, LASTNAME, E_MAIL
     into lv_pers_no, lv_firstname,
          lv_lastname, lv_e_mail
                      from :row;
if :lv_e_mail = ' ' then
  error = select 400 as http_status_code,
               'invalid email' as error_message,
                     'No Way! E-Mail field can not be empty' as detail from dummy;
else
  insert into "sp6.data::USER.Details"
             values (lv_pers_no, lv_firstname,
                     lv_lastname, lv_e_mail);
end if;
END;

ABAP Service Implementation:

TRY.
     DATA(lr_writer) = cl_sxml_string_writer=>create(
         type = if_sxml=>co_xt_json ).
     DATA(ls_address) = zcl_user=>get_details( iv_username = to_upper( me->username ) ).
     CALL TRANSFORMATION id SOURCE itab = ls_address
                                RESULT XML lr_writer.
     lv_json = cl_abap_codepage=>convert_from( CAST cl_sxml_string_writer( lr_writer )->get_output( ) ).
   CATCH zcx_user_error INTO DATA(lx_user_error).
     CALL TRANSFORMATION id SOURCE itab = lx_user_error->return
                              RESULT XML lr_writer.
     lv_json = cl_abap_codepage=>convert_from( CAST cl_sxml_string_writer( lr_writer )->get_output( ) ).
     response->set_status( code = 500
                           reason = CONV #( lx_user_error->return[ 1 ]-message ) ).
ENDTRY.

ABAP Class ZCL_USER:

class ZCL_USER definition
  public
  create public .
  public section.
    class-methods GET_DETAILS
    importing
      !IV_USERNAME type BAPIBNAME-BAPIBNAME
    returning
      value(RS_ADDRESS) type BAPIADDR3
    raising
      ZCX_USER_ERROR .
protected section.
private section.
ENDCLASS.
CLASS ZCL_USER IMPLEMENTATION.
  METHOD get_details.
    DATA lt_return TYPE STANDARD TABLE OF bapiret2.
    CALL FUNCTION 'BAPI_USER_GET_DETAIL'
      EXPORTING
        username = iv_username    " User Name
      IMPORTING
        address  = rs_address    " Address Data
      TABLES
        return   = lt_return.    " Return Structure
    IF lt_return IS NOT INITIAL.
      RAISE EXCEPTION TYPE zcx_user_error
        EXPORTING
          return = lt_return.
    ENDIF.
  ENDMETHOD.
ENDCLASS.
71 Comments