Skip to end of metadata
Go to start of metadata

Two types of enterprise search UI on HANA

  • You can run it directly in Fiori Launchpad. There is a global search in Fiori Launchpad, which is provided by enterprise search. By default, this global search uses ABAP adapter. But it can be configured by using URL parameter, to switch this search UI to HANA adapter. Besides, Fiori Launchpad server must be configured, to proxy the search request to HANA database.
  • Another way is using the standalone version. Standalone version can be deployed on any application servers. Different from Fiori Launchpad version, you must deploy the Javascript library to your application server, and write your own proxy program for certain application server, which provide certain redirect rules. Different application server has different ways to configure these redirect functions. Mostly certain application use a primary programing language, for example, Tomcat server uses Java.

Requirements for using standalone search UI

  1. Javascript library
    Javascript library of Standalone Search UI is in SAPUI5 ushell package. Package version must be higher than 1.67. It can be found from CDN.
  2. HANA database
    In HANA, the search model must be created. ESH_CONFIG can be used as a tool for creating the search model.
  3. Search call
    Search UI requires a small proxy to pass the search request to HANA by calling SQL procedure ESH_SEARCH.

Quick start with Standalone Search UI using Tomcat

  • adapt the constants in the head-section of the servlet shown below and deploy the compiled servlet in e.g. tomcat8 into webapps/ROOT/WEB-INF/classes/com/sap/esh/rest/
  • From HANA hdbclient installation copy ngdbc.jar file (in linux found in /usr/sap/hdbclient/ngdbc.jar) into webapps/ROOT/WEB-INF/lib/
  • Place web.xml from below file into webapps/ROOT/WEB-INF/
  • After restart of tomcat
    • Open UI and start a search: {http_schema}://{tomcat_host}:{tomcat_port}/resources/sap/ushell/renderers/fiori2/search/container/index.html?sinaProvider=hana_odata

    • To check the db connection is working, open {http_schema}://{tomcat_host}:{tomcat_port}/sap/es/odata/$metadata

    • To check the UI CDN connection is working, open {http_schema}://{tomcat_host}:{tomcat_port}/sap/bc/resources/sap-ui-core.js

 

EshHanaProcedure.java
package com.sap.esh.rest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class EshHanaProcedure extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // ---------------------------------------------------------------------------
    // --- adapt as required (or provide env-vars via 'setenv.sh' with 
    // ---   similar name but "DEFAULT_" removed):
    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    
    public static final String DEFAULT_ESH_DB_USER = "myuser";
    public static final String DEFAULT_ESH_DB_PASSWORD = "mypassword";
    public static final String DEFAULT_ESH_DB_HOST = "myhost.com";
    public static final String DEFAULT_ESH_DB_INSTANCE = "0";
    public static final String DEFAULT_ESH_DB_APPLICATION_USER = "myapplicationuser";

    public static final String DEFAULT_CDN_BASE_URL = "https://sapui5.hana.ondemand.com";
    
    public static final String DEFAULT_CDN_PROXY = null; // e.g. "http://myproxy.corp:8080";

    
    
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // --- 
    public static final String DEFAULT_ESH_DB_PORT_LAST_TWO_DIGITS = "15"; // e.g. 30215, req. for multi-container HANA

    static String ESH_DB_USER = System.getenv().getOrDefault("ESH_DB_USER", DEFAULT_ESH_DB_USER);
    static String ESH_DB_PASSWORD = System.getenv().getOrDefault("ESH_DB_PASSWORD", DEFAULT_ESH_DB_PASSWORD);
    static String ESH_DB_HOST= System.getenv().getOrDefault("ESH_DB_HOST", DEFAULT_ESH_DB_HOST);
    static String ESH_DB_INSTANCE = System.getenv().getOrDefault("ESH_DB_INSTANCE", DEFAULT_ESH_DB_INSTANCE);
    static String ESH_DB_PORT_LAST_TWO_DIGITS = System.getenv().getOrDefault("ESH_DB_PORT_LAST_TWO_DIGITS", DEFAULT_ESH_DB_PORT_LAST_TWO_DIGITS);
    static String ESH_DB_APPLICATION_USER = System.getenv().getOrDefault("ESH_DB_APPLICATION_USER", DEFAULT_ESH_DB_APPLICATION_USER);
    
    static String CDN_BASE_URL = System.getenv().getOrDefault("CDN_BASE_URL", DEFAULT_CDN_BASE_URL);
    static Proxy proxy = Proxy.NO_PROXY;
    static {
        // check proxy is given
        String cdn_proxy = System.getenv().getOrDefault("CDN_PROXY", DEFAULT_CDN_PROXY);
        
        if (cdn_proxy != null) {
            // custom proxy, split into parts
            String[] cdn_proxy_parts = cdn_proxy.split(":");

            String cdn_proxy_protocol = cdn_proxy_parts[0];                // http
            String cdn_proxy_host = cdn_proxy_parts[1].substring(2);    // myproxy.corp
            int cdn_proxy_port = Integer.valueOf(cdn_proxy_parts[2]);    // 8080
            
            EshHanaProcedure.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(cdn_proxy_host, cdn_proxy_port));
        }
        System.out.println("Startup done:\n - ESH_DB_USER: " + ESH_DB_USER);
        System.out.println(" - ESH_DB_PASSWORD: " + ESH_DB_PASSWORD);
        System.out.println(" - ESH_DB_HOST: " + ESH_DB_HOST);
        System.out.println(" - ESH_DB_INSTANCE: " + ESH_DB_INSTANCE);
        System.out.println(" - ESH_DB_APPLICATION_USER: " + ESH_DB_APPLICATION_USER);
        System.out.println(" - ESH_DB_PORT_LAST_TWO_DIGITS: " + ESH_DB_PORT_LAST_TWO_DIGITS);
        
        System.out.println(" - CDN_BASE_URL: " + CDN_BASE_URL);
        System.out.println(" - CDN_PROXY: " + proxy);
    }
    
    public EshHanaProcedure() {
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestUri = URLDecoder.decode(request.getRequestURI(), "UTF-8");

        if (requestUri.contains("$all")) {
            // search query
            response.setContentType("application/json");
            
            String searchQuery = "[\"/" + getVersion(requestUri) + "/" + requestUri.substring(requestUri.indexOf("$all"));
            String queryParams = request.getQueryString();
            if (queryParams != null) {
                searchQuery = searchQuery + "?" + queryParams;
            }
            searchQuery = searchQuery + "\"]";
            response.getWriter().append(search(searchQuery));
            
        } else if (requestUri.endsWith("$metadata")) {
            // metadata query
            response.setContentType("application/xml");
            response.getWriter().append(search("[\"/" + getVersion(requestUri) + "/$metadata\"]"));

        } else {
            String contextPath = URLDecoder.decode(request.getContextPath(), "UTF-8");
            // check deployment is correct
            if (!"".equals(contextPath) && !"/sap".equals(contextPath)) {
                response.getWriter().write("Servlet must be deployed to 'ROOT' or 'sap' context path");
                return;
            }
            // remove contextPath
            requestUri = requestUri.substring(contextPath.length());

            // other urls fetch from CDN
            URLConnection connection_in;
            connection_in = new URL(CDN_BASE_URL + requestUri).openConnection(proxy);

            // copy from CDN to request
            InputStream in = connection_in.getInputStream();
            OutputStream out = response.getOutputStream();
            byte[] buffer = new byte[16384];
            int len;
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            out.close();
            in.close();
        }
    }

    private String getVersion(String url) {
        int termStart = url.indexOf("/$all");
        if (termStart < 0) {
            termStart = url.indexOf("/$metadata");
        }
        int versionStart = url.lastIndexOf("/", termStart - 1) + 1;
        if (versionStart <= 0) {
            return "";
        }
        String version = url.substring(versionStart, termStart);
        return version;

    }

    private String search(final String eshQuery) {
        Connection dbConnection = null;
        Statement stmt = null;
        try {
            Class.forName("com.sap.db.jdbc.Driver");
            String dbUrl = getDBUrl();

            dbConnection = DriverManager.getConnection(dbUrl, ESH_DB_USER, ESH_DB_PASSWORD);
            stmt = dbConnection.createStatement();
            final String querySetApplicationUser = getQuerySetApplicationUser();
            if (querySetApplicationUser != null) {
                stmt.execute(querySetApplicationUser);
            }
            return callStoredProcedureESHSearch(dbConnection, eshQuery);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (dbConnection != null && !dbConnection.isClosed()) {
                    dbConnection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private String callStoredProcedureESHSearch(Connection dbConnection, final String query) throws SQLException {
        CallableStatement callableStatement = null;
        String result = null;
        try {
            callableStatement = dbConnection.prepareCall("{call SYS.ESH_SEARCH(?, ?)}");
            callableStatement.setString(1, query);
            boolean hadResults = callableStatement.execute();
            while (hadResults) {
                ResultSet rs = callableStatement.getResultSet();

                while (rs.next()) {
                    result = rs.getString(1);
                }
                hadResults = callableStatement.getMoreResults();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (callableStatement != null) {
                callableStatement.close();
            }
        }
        return result;
    }

    private static String getDBUrl() {
        return String.format("jdbc:sap://%s:%d/", ESH_DB_HOST,
                     Integer.valueOf(ESH_DB_INSTANCE) * 100 + 30000 +
                     (Integer.valueOf(ESH_DB_PORT_LAST_TWO_DIGITS) % 100));
    }
    private static String getQuerySetApplicationUser() {
            return ESH_DB_APPLICATION_USER == null ? null : String.format("SET 'APPLICATIONUSER'='%s';", ESH_DB_APPLICATION_USER);
    }
}

web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee">
  <display-name>esh-search-proxy</display-name>
  <servlet>
    <description></description>
    <display-name>EshHanaProcedure</display-name>
    <servlet-name>EshHanaProcedure</servlet-name>
    <servlet-class>com.sap.esh.rest.EshHanaProcedure</servlet-class>
  </servlet>
  <servlet-mapping>
	<servlet-name>EshHanaProcedure</servlet-name>
 	<!-- deployment at ROOT: -->
  	<url-pattern>/sap/*</url-pattern>
  	<!-- deployment at /sap/: -->
    <url-pattern>/resources/*</url-pattern>
    <url-pattern>/es/odata/*</url-pattern>
  </servlet-mapping>
</web-app>
  • No labels