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: 
former_member203148
Discoverer

Introduction

As the server side Javascript is emerging, we decided to have a look on different Javascript server platforms. RingoJS is Rhino/JVM based Javascript engine which is very easy to integrate with Java. More about Ringo can be found here - http://ringojs.org/ . In contrast to some other engines, RingoJS script can directly use Java objects, so a Ringo Javascript could reuse any Java program/library. The opposite is also true - a Java program can execute a Javascript using Ringo. In the context of SAP Netweaver Cloud, it means that a RingoJS Javascript application is able to:

  • Run on SAP Netweaver Cloud as a normal web application
  • Use JDBC interface to connect to the database
  • Use any Java service available in SAP NetWeaver Cloud
  • Use SAP HANA

This blog will show you how to run a RingoJS Javascript application on top of SAP NetWeaver Cloud and connect to HANA.

The RingoJS Application

Let's start with a simple stand-alone RingoJS web application which is connecting to a database, executes a select statement and returns the result in JSON format. The main.js is a standard RingoJS web application skeleton, so I am not showing it here, you could find it under <Ringo Install Folder>\tools\admin\skeletons\app\main.js. It loads the actions.js, where we implement the request processing logic. The function "getConnection" is connecting to HANA Database using HANA JDBC driver. The driver jar must be part of RingoJS classpath, of course.

actions.js

var response = require('ringo/jsgi/response');

java.lang.Class.forName("com.sap.db.jdbc.Driver");

function getConnection( req )

{

     var hana_conn = java.sql.DriverManager.getConnection(

                                 "jdbc:sap://MyHost:30115?SID=MySID",

                                 "User",

                                 "Password");

     return hana_conn;

}

exports.index = function (req) {

     var resource = getResource("./index.html");

     return response.static( resource );

};

exports.persons = function( req ){

     var result_array = [];

     var conn = getConnection( req );    

     var result = conn.createStatement().executeQuery("select * from PERSON");

     while( result.next() )

     {

          var row = {};

          row.number = result.getString(1);

          row.name = result.getString(2);    

          result_array.push( row );

     }

     return response.json(result_array);

};

Adapting the application for the SAP Netweaver Cloud

We cannot run the stand-alone Ringo application directly in the cloud. There are several obstacles.

  • HTTP Server Layer
    • We would need to plug-in our script into the java server web container, instead of starting own HTTP layer as in the stand-alone case.
  • Database Connection
    • In the cloud we cannot use the low-level DriverManager interface, we do not know the connection details. Instead, we must lookup a DataSource object from JNDI.

1. RingoServlet

Ringo is offering an adapter for the web container environment, called JsgiServlet(org.ringojs.jsgi.JsgiServlet). One could describe and map that servlet directly in the web.xml. In our case we want HANA access, so we have to extend the JsgIServlet functionality a bit and provide a DataSource.That servlet is registered in the web container and accessible from the javascript program. The DataSource object is retreived during the init() method,note that it is not possible to lookup in the getDataSource() method since it is plain Java method, without any container managed context.

RingoServlet.java

public class RingoServlet extends org.ringojs.jsgi.JsgiServlet

{

          public DataSource dataSource;

    @Override

    public void init() throws ServletException

    {          ....

              dataSource = (DataSource)new InitialContext().lookup("java:comp/env/jdbc/DefaultDB");

                    ....

    }

    public Object getDataSource() throws Exception

    {

              return dataSource;

    }

}

The RingoServlet must be described and mapped in the web.xml. In order to give full control to the Javascript application, RingoServlet is mapped to all URLs, e.g. /*

web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

  <display-name>ringo4neo</display-name>

  <servlet>

    <description></description>

    <display-name>RingoServlet</display-name>

    <servlet-name>RingoServlet</servlet-name>

    <servlet-class>com.sap.cloud.ringo.RingoServlet</servlet-class>

    <init-param>

        <param-name>optlevel</param-name>

        <param-value>0</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>   

  </servlet>

  <servlet-mapping>

    <servlet-name>RingoServlet</servlet-name>

    <url-pattern>/*</url-pattern>

  </servlet-mapping> 

</web-app>

2. Database connection

As I already mentioned, we must adapt database connectivity since we do not know connection details in the cloud but use the DataSource interface instead. The DataSource is retrieved from the servlet and the later is accessible from the Ringo request environment - req.env.servlet  . Let's see how to adapt the getConnection() method in the actions.js

actions.js

..........

function getConnection( req )

{

   var dataSource = req.env.servlet.getDataSource();

   if(dataSource!=null)

   {

       return dataSource.getConnection();

   }

}

................

We have to declare the DataSource resource in the META-INF/context.xml

context.xml

<?xml version='1.0' encoding='utf-8'?>

<Context>

  <Resource name="jdbc/DefaultDB"

            auth="Container"

            type="javax.sql.DataSource"

            factory="com.sap.jpaas.service.persistence.core.JNDIDataSourceFactory"

  />

</Context>

3. Dependant libraries and modules

The following libraries must be copied from your Ringo installation into the WebContent/WEB-INF/lib folder.

  • lib/ringo-core.jar
  • lib/ivy/rhino-***.jar
  • lib/ivy/jaffl-***.jar
  • lib/ivy/jnr-posix-***.jar

Ringo modules folder must be copied under WebContent/WEB-INF

4. Structuring the web application

The file structure is listed below.  

Application - file structure

Follow an Application Request

  1. A client is requesting https://appurl/persons
  2. The web container dispatches the request to the RingoServlet
  3. RingoServlet forwards to the main.js program - more specifically the app() function
  4. The app() function gets the first path element "persons" and checks if there is an exported function persons() in actions.js
  5. The function persons() is found and called
  6. The function selects some data from HANA and sends back a JSON response

Try it out

  1. Download & install RingoJS from http://ringojs.org/
  2. Create a web application project in Eclipse
  3. Copy Ringo libraries and modules into WebContent/WEB-INF/lib and WebContent/WEB-INF/modules respectively
  4. Develop the adapter servlet in your source folder, then describe it in the web.xml
  5. Put the javascript sources and static resources under WebContent/WEB-INF/app
  6. Put context.xml under META-INF
  7. Deploy it to SAP NetWeaver Cloud