You are on page 1of 72

PUBLIC

SAP HANA Smart Data Integration and SAP HANA Smart Data Quality 2.0 SP03
Document Version: 1.0 – 2018-04-24

Data Provisioning Adapter SDK Guide


Content

1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1 Prerequisites. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5
1.2 Adapter Functionality. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5
1.3 Adapter Instances. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 Capabilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6
Description. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Three Levels of Capabilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Design Considerations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10
Capabilities Test Adapter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Key Capabilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.5 Process Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2 Install the Data Provisioning Feature. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3 Create a Custom Adapter Using a New Plug-in Project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Adapter Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

5 Building an Adapter by Extending the AdapterCDC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19


5.1 Reporting the Configuration of the Remote Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.2 Reporting the Capabilities of the Remote Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3 Beginning and Ending Communication with the Remote Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.4 Browsing a Remote Source's Metadata. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.5 Preparing to Create Virtual Tables in SAP HANA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.6 Executing Queries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.7 Real-Time Adapter Execution. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Real-Time Recovery Considerations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Adding and Removing Real-Time Subscriptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Starting and Stopping Real-Time Subscriptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Real-Time Execution Sequence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Real-Time Changed Rows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Row Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Real-Time Large-Object Columns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.8 Enterprise Semantic Services API Support. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
GET_REMOTE_SOURCE_TABLE_DEFINITIONS Procedure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
GET_REMOTE_SOURCE_OBJECTS_LIST Procedure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
GET_REMOTE_SOURCE_OBJECT_DESCRIPTIONS Procedure. . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
GET_REMOTE_SOURCE_COLUMN_DESCRIPTIONS Procedure. . . . . . . . . . . . . . . . . . . . . . . . . . . .41

Data Provisioning Adapter SDK Guide


2 PUBLIC Content
IS_TREE Property. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

6 Building an Adapter by Extending the BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44


6.1 Reporting the Configuration of the Remote Source Description for BaseAdapterClass. . . . . . . . . . . . . . 44
6.2 Reporting the Capabilities of the Remote Source for BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . 45
6.3 Beginning and Ending Communication with the Remote Source for BaseAdapterClass. . . . . . . . . . . . . .46
6.4 Browsing a Remote Source's Metadata for BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.5 Preparing to Create Virtual Tables in SAP HANA for BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . . 47
6.6 Executing Queries for BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.7 Real-Time Execution for BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.8 Real-Time Changed Rows for BaseAdapterClass. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .50

7 Building an Adapter Using Camel Adapter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52


7.1 Define Custom Adapter in adapters.xml. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Adapter Type, Display Name, and Description. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Remote Source Description. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Adapter Capabilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Camel Route Template. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.2 Use Camel Route to Implement Adapter Functionalities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .56
7.3 SDA Camel Route Defined within <camelContext> Tag. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.4 Process Metadata Browse and Import. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.5 Process SQL SELECT Statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.6 Process SQL INSERT/UPDATE/DELETE Statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
7.7 Dependent JAR Files. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

8 Debugging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.1 Import the Launch Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.2 Set up the Debug Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
8.3 Register the Debugging Agent and Adapters in SAP HANA. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .62

9 Deploying a Custom Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64


9.1 Export the Adapter as a Deployable Plug-in. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
9.2 Deploy and Register the Custom Adapter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.3 Use the Custom Adapter in SAP HANA Studio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

10 Additional Information. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
10.1 Data Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
10.2 Trace Logging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
10.3 Exception Handling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
10.4 AdapterFactory Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .68

Data Provisioning Adapter SDK Guide


Content PUBLIC 3
1 Introduction

This guide describes how to develop custom adapters to connect to a variety of remote sources from SAP HANA.

Adapters bridge the gap between your source system and the SAP HANA database so you can query the remote
system's data from SAP HANA. The adapter instances browse and import metadata as SAP HANA virtual tables.
With virtual tables, you can perform simple or complex SQL queries as you would with a SAP HANA native table.

You can also create real-time adapters that capture changed data.

Consider the following example. A machine sensor is connected to the Internet and provides two URLs to read
data:

● https://machine1/lastevents displays a list of all events that occurred in the last 24 hours
● https://machine1/eventstream displays new events, which are continually added

The goal is to make this data available in SAP HANA by writing a custom adapter. This adapter will be a Java
program that extends some of the classes of the adapter SDK and acts as an interface between the machine
sensor and SAP HANA. To determine which methods to implement, consider the following questions:

● What parameters are required to connect to the sensor? In this example, it could be the URL, a user name, and
user password.
● How should the data be presented in one or more tables? If the sensor returns the data in lines and each line
contains a timestamp, an event type, and event information, then those components suggest the table
structure.
● What happens when in SAP HANA you in execute the command SELECT * from machine-sensor-table?
The adapter needs to connect to the source URL, retrieve the data, and reformat the lines into rows of the
table.
● What happens when in SAP HANA you in execute the command CREATE REMOTE SUBSCRIPTION … using
(SELECT * from machine-sensor-table) targettable hanatable? It will connect to the event
stream URL and keep waiting for new lines indefinitely. Whenever a new line is received, it is parsed, converted
into a row and streamed to SAP HANA.

Related Information

Prerequisites [page 5]
Adapter Functionality [page 5]
Adapter Instances [page 6]
Capabilities [page 6]
Process Overview [page 15]

Data Provisioning Adapter SDK Guide


4 PUBLIC Introduction
1.1 Prerequisites

These knowledge and software prerequisites apply when creating custom adapters for SAP HANA.

Knowledge:

● Java programming experience


● Familiarity with SAP HANA SQL, Smart Data Access, and SAP HANA studio

Software:

Refer to the SAP HANA smart data integration Product Availability Matrix (PAM) for currently supported software
versions and other information.

● Java
To check your Java version, open a terminal or command prompt and type java -version.
● SAP HANA studio or Eclipse
● Installation of the Data Provisioning Agent

Related Information

SAP HANA Smart Data Integration and all its patches Product Availability Matrix (PAM) for SAP HANA SDI 1.0
SAP HANA Smart Data Integration and all its patches Product Availability Matrix (PAM) for SAP HANA SDI 2.0

1.2 Adapter Functionality

Adapters for SAP HANA smart data integration include these key functionalities.

Functionality Description

Adapter parameter configuration When deploying an adapter, some parameters might be required. These parameters are
configured by an administrator using the Data Provisioning Agent Configuration tool.

Remote source connection Using SAP HANA studio, Web-based Development Workbench, or the SQL command
CREATE REMOTE SOURCE, you configure the adapter with all the parameters required
to connect to the source system. You specify the parameters, hierarchy, and type. These
values are stored in SAP HANA and passed to the adapter at run time.

Remote source browsing Expanding the adapter's remote source node displays the available tables including any
relevant hierarchy.

Data Provisioning Adapter SDK Guide


Introduction PUBLIC 5
Functionality Description

Virtual tables You make remote tables available for querying by adding them as virtual tables using ei­
ther SAP HANA studio or Web-based Development Workbench. the CREATE VIRTUAL
TABLE command. A virtual table is like a database view but it points to a remote table and
contains all the metadata required for SAP HANA, descriptions, columns and their data
types, primary key information, and adapter-specific attributes.

SQL query execution When executing a SQL statement that involves a virtual table, the Data Federation Layer of
SAP HANA analyzes the statement and creates a SQL statement the adapter can inter­
pret. Everything beyond the adapter capabilities executes subsequently in SAP HANA. You
can control the supported operations using capabilities on adapter, table, and column lev­
els.

Realtime processing In addition to querying tables, adapters can provide the data in real time using a remote
subscription.

1.3 Adapter Instances

The Data Provisioning Server in SAP HANA gets the information about SAP HANA sessions and requests new
adapter instances from the Data Provisioning Agent.

For example, when you execute the first query against a table served by this adapter, a new adapter instance is
created. Other users executing commands in parallel would have their own adapter instances. When the session
terminates, the adapter instance closes. This makes writing adapters convenient because you implement the
adapter from the perspective of a single user, single command only. All real-time commands are yet another
adapter instance, but in this case there is one adapter instance for all real-time requests.

1.4 Capabilities

Source capabilities define functionality such as INNER JOIN or SUBSTRING operations. The SAP HANA optimizer
uses these capabilities to determine whether a SQL operation can be pushed down to the remote source, which
improves performance.

For adapter development, generally the more operations that execute in the source system, the better the
performance. For example, if a table has one million rows and the user selects just one, then it is best to push the
WHERE clause down to the adapter, and the adapter returns the requested row. The alternative for the optimizer
would be to return all rows and filter them in SAP HANA. If all adapters supported the full SAP HANA syntax, it
would be a burden to adapter developers. If the source is a simple database, then it might be possible to translate
each SAP HANA SQL command into source-specific SQL. But that would be a lot of work with all the various SQL
syntax options, and some databases might lack a function or a set operation or simply behave differently.

Data Provisioning Adapter SDK Guide


6 PUBLIC Introduction
The optimal situation is between the two extremes of supporting full SAP HANA syntax and supporting simple
table reads. Adapter capabilities let you configure this compromise with the adapter method
getCapabilities().

For the complete list of capabilities supported by this adapter SDK, see the Javadoc, which is installed with the
Data Provisioning Agent (default location is C:\usr\sap\dataprovagent\doc\javadoc). See the
com.sap.hana.dp.adapter.sdk.AdapterConstants class and the ENUMs for AdapterCapabilities, TableCapabilities,
and ColumnCapabilities.

Related Information

Description [page 7]
Three Levels of Capabilities [page 9]
Design Considerations [page 10]
Capabilities Test Adapter [page 11]
Key Capabilities [page 12]

1.4.1 Description

Description of how adapter capabilities work.

The method getCapabilities is invoked when registering or refreshing the adapter (CREATE ADAPTER and
ALTER ADAPTER REFRESH SQL commands). This method returns the set of available capabilities for the adapter.
When creating a virtual table, the method importMetadata returns a TableMetadata object. One of the
property sets of TableMetadata are the table-level capabilities, which are set by the method
TableMetadata.setCapabilities. Each table column object supports capabilities as well, which are set by the
method Column.setCapabilities.

For example, CAP_SELECT means the adapter itself supports selecting data from the tables. Without this
capability, no table can be read no matter what their individual settings are. On the table level,
CAP_TABLE_SELECT means you can select data from this table.

If the adapter has column-level capabilities enabled with CAP_COLUMN_CAP and the table does as well via
CAP_TABLE_COLUMN_CAP, then the capabilities of each column are evaluated. In such a scenario, an important
column capability would be CAP_COLUMN_SELECT; otherwise the column cannot be used in a SELECT clause.

This approach lets you control filter-related capabilities granularly. You can define the type of filters (CAP_WHERE,
CAP_SIMPLE_EXPR_IN_WHERE), or you can define supported comparisons (CAP_LIKE, CAP_IN,
CAP_NONEQUAL_COMPARISON, and so on) on the adapter level and some on the column level.

Example
The adapter supports WHERE column = 99 but not any other condition (WHERE column > 99 is not
supported).

Therefore, the necessary capability would be CAP_SIMPLE_EXPR_IN_WHERE.

Data Provisioning Adapter SDK Guide


Introduction PUBLIC 7
Example
The adapter supports WHERE column = 99 and other conditions such as WHERE column > 99. Operators
such as LIKE or IN are not supported.

Therefore, the necessary capabilities would be CAP_WHERE, CAP_SMPLE_EXPR_IN_WHERE, and


CAP_NONEQUAL_COMPARISON.

Another important capability is projection (CAP_PROJECT), which indicates that the columns can be selected
individually. Without this capability, all columns would be read from the adapter and deleted by the Data Federation
layer of SAP HANA. If a typical table of the adapter is very wide but only subsets of the columns are frequently
required, consider enabling projection. The BaseAdapterClass supports projections.

If possible, CAP_LIMIT should be present as well, enabling SQL commands in the form of SELECT top 100 *
from …. The BaseAdapterClass implements that by calling the getNext function only up to the specified
number of times.

Whenever custom data should persist in the table metadata using setAttribute, set the capability
CAP_METADATA_ATTRIBUTE.

There are many more capabilities such as whether the adapter supports ORDER BY, GROUP BY, joins, push-down
of functions in projection or in the WHERE clause, and so on.

Sample Code

SQL supported by the adapter Minimum required Capabilities

SELECT rownumber*4 FROM v_sqltable; CAP_SELECT

CAP_SIMPLE_EXPR_IN_PROJ

SELECT rownumber*rownumber FROM CAP_SELECT


v_sqltable;
CAP_EXPR_IN_PROJ

SELECT SUBSTR(sqltext,1,3) FROM CAP_SELECT


v_sqltable;
CAP_EXPR_IN_PROJ

CAP_NESTED_FUNC_IN_PROJ

SELECT * FROM v_sqltable WHERE CAP_SELECT


rownumber = 3;
CAP_SIMPLE_EXPR_IN_WHERE

SELECT * FROM v_sqltable WHERE CAP_SELECT


rownumber <> 3;
CAP_SIMPLE_EXPR_IN_WHERE

CAP_NONEQUAL_COMPARISON

SELECT * FROM v_sqltable WHERE CAP_SELECT


rownumber > 3;
CAP_SIMPLE_EXPR_IN_WHERE

CAP_NONEQUAL_COMPARISON

Data Provisioning Adapter SDK Guide


8 PUBLIC Introduction
SQL supported by the adapter Minimum required Capabilities

SELECT * FROM v_sqltable WHERE CAP_SELECT


rownumber > 3 and rownumber < 6;
CAP_SIMPLE_EXPR_IN_WHERE

CAP_NONEQUAL_COMPARISON

CAP_AND

SELECT * FROM v_sqltable WHERE CAP_SELECT


rownumber > 3 and sqltext > 'Z';
CAP_SIMPLE_EXPR_IN_WHERE

CAP_NONEQUAL_COMPARISON

CAP_AND_DIFFERENT_COLUMNS

SELECT rownumber, COUNT(*), CAP_GROUPBY


MIN(sqltext) FROM v_sqltable GROUP BY
rownumber; CAP_AGGREGATES, CAP_EXPR_IN_PROJ

SELECT rownumber, COUNT(DISTINCT CAP_GROUPBY


sqltext) FROM v_sqltable GROUP BY
rownumber; CAP_AGGREGATES

CAP_DIST_AGGREGATES, CAP_EXPR_IN_PROJ

1.4.2 Three Levels of Capabilities

Capabilities can be defined on three levels:

● Adapter-level capabilities are specific to the adapter. For example, Oracle supports full outer join, SAP ERP
supports left outer join, and so on.
● Table-level capabilities apply to a particular table. For example, an SFSF USER table supports reading, but not
loading.
● Column-level capabilities apply to a particular column of a table. For example, the SFSF USER.PASSWORD
column does not support reading its value.

Refer to AdapterConstants.AdapterCapabilities, AdapterConstants.TableCapability,


AdapterConstants.ColumnCapability.

For SQL functions like SUBSTR(), adapter support is available only on the adapter level. But an adapter might
provide different kinds of tables or columns, some of which only support one set of functions. Generally speaking,
adapter capabilities control everything in as much detail as possible. On table and column levels, you can enable or
disable specific options.

There is also dependency between capabilities. If a table capability like TableCapability.CAP_TABLE_INSERT is


enabled, it has no effect unless the AdapterCapability.CAP_INSERT is also enabled. For a table-level capability to
even be considered, the AdapterCapability.CAP_TABLE_CAP must be set (similarly with column-level capabilities).

Note that adapters might support different capabilities depending on the source version. For example, PostgreSQL
version 7.2 does not support a function, but version 9.0 does. Therefore, all capability-related calls provide the
version string from the remote source. For most adapters, this will be empty.

Data Provisioning Adapter SDK Guide


Introduction PUBLIC 9
1.4.3 Design Considerations

Concepts and limitations to consider when configuring capabilities for remote source adapters.

SAP HANA Optimizer

Push-down of SQL operations happens via the smart data access logic in the SAP HANA optimizer. When a SQL
statement executes in SAP HANA, if a virtual table is included, the optimizer rewrites the SAP HANA SQL into two.
One executes via the adapter according to its capabilities, and the remaining logic executes in SAP HANA.

This rewrite happens even in a simple SELECT * FROM <virtual_table> command, which will not be sent to
the adapter.

For example:

SELECT id, name FROM v_customer WHERE name between ‘A’ and ‘Z’;

Assume the following capabilities are supported by the adapter:

● CAP_SELECT: Yes, select statements are supported against that virtual table
● CAP_PROJECT: supports selecting individual columns
● CAP_WHERE: support filters in general
● CAP_BETWEEN: support the between clause

Then the optimizer would rewrite the statement to:

SELECT id, name FROM


(SELECT id, name FROM v_customer WHERE name BETWEEN ‘A’ and ‘Z’)

The inner SQL is sent to the adapter.

If the adapter does not support CAP_BETWEEN, then the inner SQL cannot contain the BETWEEN clause:

SELECT id, name FROM


(SELECT id, name FROM v_customer)
WHERE name BETWEEN ‘A’ and ‘Z’;

The expectation might be that both versions return the exact same data. What if the name was in lowercase or
included Unicode characters? SAP HANA does all string comparisons on a binary level; therefore, the BETWEEN
clause executed in SAP HANA would return ‘Bill’ and ‘Todd’ but not ‘frank’ or ‘Ätna’. Other systems might compare
on a lexicographical level without case sensitivity and therefore return all four values. It is not desirable for two
logically identical SQL commands to return different results. Therefore, ensure you set capabilities carefully.

Limitations

The assumption of capabilities is that they are static (set once by the adapter then never changed again). This
ensures the capability information is current in the SAP HANA data dictionary tables. For example, there is a
bitmap mask of all set adapter capabilities in the SYS.ADAPTER_CAPABILITIES_. CAPABILITIES column.

Data Provisioning Adapter SDK Guide


10 PUBLIC Introduction
Therefore, whenever the capabilities change, the ALTER ADAPTER command is required to reread the information.
For example:

ALTER ADAPTER <adaptername> refresh at location agent <agentname>;

In addition, the optimizer has a few more places to cache the execution plans and such. If table or column
capabilities are used, the information is part of the SAP HANA virtual table. Therefore, you should drop and
recreate the virtual tables as well. You must create a new SQL session to see the changes. For example, in SAP
HANA studio, select the database connection in the top tool palette of the SQL window.

1.4.4 Capabilities Test Adapter

To help you understand adapter capabilities, a capabilities test adapter has been provided and is available in
github: hana-native-adapters/CapabilitiesTestAdapter. This adapter has two tables: the SQLTEXT table
and the ADAPTERCAPS table. You enable and disable individual adapter capabilities using a statement such as:

UPDATE v_adaptercaps set isset = 'TRUE' where CAPABILITY =


'CAP_NONEQUAL_COMPARISON';

Then refresh the SAP HANA dictionary using a command such as:

ALTER ADAPTER "CapabilitiesTestAdapter" refresh at location agent "<agentname>";

If a table or column capability changed, drop and re-create the virtual table:

drop table v_sqltable;


CREATE VIRTUAL TABLE v_sqltable at "captest"."<NULL>"."<NULL>"."SQLTABLE";

Trigger a reconnect to SAP HANA for the session-related caching. To see what is pushed down now, the Explain
Plan feature helps:

SELECT * from v_sqltable WHERE rownumber>=1;

The results indicate the SQL commands that were sent to the remote adapter:

COLUMN SEARCH V_SQLTABLE.ROWNUMBER, V_SQLTABLE.SQLTEXT (LATE MATERIALIZATION)

FILTER V_SQLTABLE.ROWNUMBER > 1

COLUMN SEARCH V_SQLTABLE.ROWNUMBER, V_SQLTABLE.SQLTEXT

REMOTE COLUMN SCAN SELECT "V_SQLTABLE"."ROWNUMBER", "V_SQLTABLE"."SQLTEXT" FROM "SQLTABLE"


"V_SQLTABLE"

Note
The capability settings of this provided sample adapter are kept in static memory, so whenever you restart the
adapter, it clears the user settings and starts with the default settings.

Also keep in mind that the ADAPTERCAPS table returns the capabilities set in the adapter at the moment, not the
SAP HANA data dictionary information and what is actively used by the optimizer.

Data Provisioning Adapter SDK Guide


Introduction PUBLIC 11
The following capabilities cannot be disabled because the UPDATE statement would not work:

● AdapterCapability.CAP_SELECT
● AdapterCapability.CAP_UPDATE
● AdapterCapability.CAP_SIMPLE_EXPR_IN_WHERE

1.4.5 Key Capabilities

An introduction to the key capabilities used in remote source adapters.

Global

The most important adapter capabilities define what kind of SQL commands the adapter supports in general and
other global information.

● CAP_SELECT: The adapter supports SELECT statements


● CAP_INSERT: The adapter supports INSERT statements
● CAP_UPDATE: The adapter supports UPDATE statements
● CAP_DELETE: The adapter supports DELETE statements
● CAP_INSERT_SELECT: The adapter supports a full INSERT...SELECT push-down
● CAP_TRANSACTIONAL_CDC: The adapter supports CREATE REMOTE SUBSCRIPTION using the
transactional method
● CAP_NON_TRANSACTIONAL_CDC: The adapter supports CREATE REMOTE SUBSCRIPTION and every row is
a transaction
● CAP_METADATA_ATTRIBUTE: Set this flag if you want the adapter to store properties along with the virtual
table
● CAP_SUPPORT_RICH_ESS_METADATA: This flag indicates whether additional metadata for semantic search
is available by the adapter
● CAP_SUPPORT_RICH_METADATA_BY_TREE: Indicates whether the adapter returns the list of tables as a flat
list or as a tree
● CAP_TABLE_CAP: Set this flag to consider table-level capabilities
● CAP_COLUMN_CAP: Set this flag to consider column-level capabilities. Note: If CAP_TABLE_CAP is enabled,
then each table must enable CAP_TABLE_COLUMN_CAP as well.

SELECT-related Capabilities

The core SELECT-related capabilities include the following.

● CAP_PROJECT: If this capability is disabled, all columns in the exact order of the virtual table are returned. If
the table is wide and it is typical to select only a subset of the columns, then enable this capability when
returning the rows and the columns need to be returned in the order as requested by the SELECT clause.
● CAP_LIMIT: If a SQL statement like SELECT top 100 * FROM <table> is used frequently, the adapter
should support this capability.

Data Provisioning Adapter SDK Guide


12 PUBLIC Introduction
● CAP_DISTINCT: The adapter supports a SELECT DISTINCT query.
● CAP_SIMPLE_EXPR_IN_PROJ: The adapter supports basic expressions like SELECT revenue*10,..
● CAP_EXPR_IN_PROJ: By default, the adapter allows only selecting columns. Supporting expressions lets you
push a SELECT SUBSTR(name,1,3),… operation to the adapter (assuming the function is supported). Refer
to the CAP_BI_ capabilities.
● CAP_NESTED_FUNC_IN_PROJ: Functions can be nested, for example SELECT upper(SUBSTR(name,
1,3)),… (assuming all functions used are enabled by their matching capabilities).
● CAP_WHERE: When enabled, the SELECT statement can contain WHERE clauses. The types of WHERE
clauses is controlled by other capabilities.
● CAP_SIMPLE_EXPR_IN_WHERE: Allows for queries like WHERE col = 3
● CAP_JOINS: The adapter supports joins. The types of joins is controlled by other capabilities.
● CAP_GROUPBY: The adapter supports aggregation
● CAP_ORDERBY: The adapter supports ORDER BY.

Filters

Filters require the CAP_WHERE capability. Pushing down WHERE clauses is the most important feature of most
adapters. However, not all sources support everything. To provide control, the following capabilities are typically
used.

● CAP_NONEQUAL_COMPARISON: When set, all other comparisons are also enabled (greater than, less than,
greater-equal, not-equal, and so on)
● CAP_EXPR_IN_WHERE: Allows for expressions in WHERE clauses
● CAP_IN: The adapter supports the IN operator
● CAP_LIKE: The adapter supports the LIKE operator; must support the characters % and _
● CAP_BETWEEN: The adapter supports using the BETWEEN clause in SAP HANA. Note: Even if AND and
NON_EQUAL are not enabled, the SQL received by the adapter will resemble col >= 0 AND col <= 100
● CAP_AND: Enables ANDing the same columns together
● CAP_AND_DIFFERENT_COLUMNS: Enables ANDing different columns together
● CAP_OR: Enables ORing the same columns together
● CAP_OR_DIFFERENT_COLUMNS: Enables ORing different columns together

Conversion functions

Conversion functions are important for WHERE clauses when implicit data-type conversions occur. For example,
the command SELECT * FROM table WHERE float_column = 3.0 cannot be pushed down unless
to_double() can be pushed down because the optimizer needs to execute SELECT * FROM table WHERE
float_column = to_double(3.0).

● CAP_BI_CAST
● CAP_BI_TO_*

Data Provisioning Adapter SDK Guide


Introduction PUBLIC 13
Functions

For advanced cases, every SAP HANA function and operation can be pushed down. Depending on where the
function in used (in the projection, in the filter, and so on), you must enable the corresponding capability to allow
for expressions.

Aggregations

Aggregations require CAP_GROUPBY and CAP_EXPR_IN_PROJ. This category consists of the GROUP BY clause,
HAVING clause and the aggregation functions. A statement such as SELECT region, sum(revenue),
count(*) FROM table GROUP BY region requires the capabilities CAP_EXPR_IN_PROJ (otherwise no
functions are allowed), CAP_AGGREGATES for min/max/sum/avg/count/count(distinct column), and
CAP_GROUPBY because of the GROUP BY clause.

● CAP_AGGREGATES
● CAP_COUNT_DIST_MULT_COLS
● CAP_EXPR_IN_GROUPBY
● CAP_HAVING
● CAP_SIMPLE_EXPR_IN_GROUPBY

Joins

Joins require CAP_JOINS. If the global CAP_JOINS is enabled, then inner joins are supported. To support outer
joins, full outer joins, or combinations of inner and outer joins, additional capabilities are available. To support
more than simple a.column = b.column join conditions, expressions can be enabled.

● CAP_JOINS_OUTER
● CAP_JOINS_FULL_OUTER
● CAP_JOINS_MIXED
● CAP_JOINS_OUTER
● CAP_EXPR_IN_FULL_OUTER_JOIN
● CAP_EXPR_IN_INNER_JOIN
● CAP_EXPR_IN_LEFT_OUTER_JOIN

Data Provisioning Adapter SDK Guide


14 PUBLIC Introduction
1.5 Process Overview

This overview describes the process for creating, deploying, and enabling custom adapters for SAP HANA smart
data integration.

Order Task Role Tool Guide

1 Install Data Provisioning Administrator Data Provisioning Agent installer Administration Guide
Agent

2 Install Data Provisioning fea­ Developer SAP HANA studio Adapter SDK Guide
ture

3 Create plug-in project Developer SAP HANA studio Adapter SDK Guide

4 Develop adapter Developer SAP HANA studio Adapter SDK Guide

5 Debug adapter Developer SAP HANA studio Adapter SDK Guide

6 Export adapter as a deploya­ Developer SAP HANA studio Adapter SDK Guide
ble plug-in

7 Deploy and register adapter Administrator Data Provisioning Agent Configuration tool Administration Guide
with SAP HANA

8 Enable adapter with a new re­ Administrator SAP HANA studio Adapter SDK Guide
mote source connection

Data Provisioning Adapter SDK Guide


Introduction PUBLIC 15
2 Install the Data Provisioning Feature

The second step in deploying a custom adapter is to install the Data Provisioning Feature in SAP HANA studio. The
Data Provisioning Feature provides plug-ins that let you run custom adapters in SAP HANA studio.

Prerequisites

Ensure the Data Provisioning Agent has been installed and is available to the computer where you want to install
the Data Provisioning Feature.

Procedure

1. Launch SAP HANA studio.


2. From the Help menu, select Install New Software.
3. Select Add to display the Add Repository dialog box.
4. In the Add Repository dialog box, enter a name such as Data Provisioning Feature.
5. Select Local and browse to the location of the Data Provisioning Agent installation \ui folder, (for example
<DPAgent_root>\ui).
6. Select the ui folder and click OK.
7. Click OK in the Add Repository dialog box.
Data Provisioning Framework displays in the Available Software list.
8. Select the Data Provisioning Framework check box and click Next.
9. Select AdapterFramework and click Next.
10. To continue, select I accept the terms of the license agreement and click Finish.
11. If you receive a security warning regarding unsigned content, click OK to continue the installation.
12. You will be prompted to restart SAP HANA studio. Click Yes when ready.

Next Steps

Next, begin creating a custom adapter in SAP HANA studio by adding a new plug-in project.

Data Provisioning Adapter SDK Guide


16 PUBLIC Install the Data Provisioning Feature
3 Create a Custom Adapter Using a New
Plug-in Project

To begin creating a custom adapter, add a new plug-in project as follows.

Prerequisites

Ensure you have installed the Data Provisioning Feature.

Procedure

1. In SAP HANA studio, open the Plug-in Development perspective ( Window Open Perspective Other
Plug-in Development ).

2. Right-click in the Package Explorer white space and select New Plug-in Project .
3. Enter a project name such as HelloWorldAdapter.
4. For Target Platform, select an OSGi framework: Equinox and click Next.
5. Optionally change the Execution Environment depending on your adapter code.
6. In the Templates dialog, select Data Provisioning Adapter Wizard and click Finish.

Results

The template configures OSGi configuration requirements, registers the adapter with the framework, and so on.
The Package Explorer view displays the new adapter project including three classes:

● Activator.java
● HelloWorldAdapter.java (Expand this class to view the methods to develop your adapter)
● HelloWorldAdapterFactory.java

Data Provisioning Adapter SDK Guide


Create a Custom Adapter Using a New Plug-in Project PUBLIC 17
4 Adapter Classes

When developing an adapter, you can extend one of three classes.

● The default Adapter class


This is a low-level class for adapters that support batch reading only.
● The AdapterCDC class
This class extends the Adapter class and adds real-time operations.
● The BaseAdapterClass
This class extends the AdapterCDC class and provides additional support when building adapters. This class
works with individual tables (no joins are supported). It supports WHERE clauses and projections but without
SAP HANA expressions. For example, a col1*10 or substr(col2,1,1) request would not be pushed to the
adapter but executed later in SAP HANA, and the adapter would return col1, col2 as is.

As a general rule, if the adapter’s source supports SQL-like syntax, the Adapter or AdapterCDC classes are a good
starting point. For adapters that do not require joins, the BaseAdapterClass is a good starting point.

Data Provisioning Adapter SDK Guide


18 PUBLIC Adapter Classes
5 Building an Adapter by Extending the
AdapterCDC

Adapter is an abstract class for which adapter developers provide the implementation. An adapter object enables
SAP HANA to access remote data sources such as Microsoft SQL Server, Twitter, etc. It performs the following
main operations:

● Reports the configuration of the remote source


● Returns the capabilities of the remote source
● Opens and closes the remote source
● Browses the metadata of the remote source
● Returns the table metadata
● Reads the rows of table metadata from the remote source

An extension of the Adapter class, the AdapterCDC class, supports real-time messaging

As an example of how to create a real-time adapter by extending AdapterCDC, we will create a simple
HelloWorld adapter. It provides a single table called HELLO. When the Data Provisioning Agent selects from the
table, the adapter returns 10 rows with each containing a row number and a text. The text consists of the user as
specified in the remote connection and another parameter.

This example adapter also supports real time in the sense that every 5 seconds a new text is sent to SAP HANA.

Related Information

Reporting the Configuration of the Remote Source [page 20]


Reporting the Capabilities of the Remote Source [page 20]
Beginning and Ending Communication with the Remote Source [page 22]
Browsing a Remote Source's Metadata [page 23]
Preparing to Create Virtual Tables in SAP HANA [page 25]
Executing Queries [page 27]
Real-Time Adapter Execution [page 28]
Enterprise Semantic Services API Support [page 37]

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 19
5.1 Reporting the Configuration of the Remote Source

In order to connect to the remote source, the adapter usually needs to get some connection parameter values. But
for SAP HANA to know what kind of parameters the user must enter, the adapter is queried.

The getRemoteSourceDescription method

The Data Provisioning Agent invokes this method when the adapter is created in SAP HANA. It should return a
RemoteSourceDescription object containing one list of normal properties and a list of credential properties,
both stored in SAP HANA in the data dictionary or a secure store.

Example

@Override
public RemoteSourceDescription getRemoteSourceDescription() throws
AdapterException {
RemoteSourceDescription rs = new RemoteSourceDescription();

PropertyGroup connectionInfo = new PropertyGroup("conn","Connection


Info","Connection Info");
connectionInfo.addProperty(new PropertyEntry("name", "Hello whom?"));

CredentialProperties credentialProperties = new CredentialProperties();


CredentialEntry credential = new CredentialEntry(CREDENTIAL, "Credentials");
credential.getUser().setDisplayName("Username");
credential.getPassword().setDisplayName("Password");
credentialProperties.addCredentialEntry(credential);
rs.setCredentialProperties(credentialProperties);
rs.setConnectionProperties(connectionInfo);
return rs;
}

5.2 Reporting the Capabilities of the Remote Source

The Data Provisioning Adapter invokes the following methods to get the list of adapter capabilities, defining what
the adapter can do, and get the source version. These methods will be invoked before or after opening the remote
source.

The getSourceVersion method

The version String in the getCapablities call must first be obtained by the getSourceVersion method call.

Data Provisioning Adapter SDK Guide


20 PUBLIC Building an Adapter by Extending the AdapterCDC
Example

@Override
public String getSourceVersion(RemoteSourceDescription
remoteSourceDescription) throws AdapterException {
return "0.0.0";
}

The getCapabilities and getCDCCapabilities methods

For regular SQL select commands and for real-time pushes, the Data Provisioning Agent needs a list of supported
capabilities for the adapter. This list is returned by the getCapabilities or getCDCCapabilities method.

Example

@Override
public Capabilities<AdapterCapability> getCapabilities(String version) throws
AdapterException {
Capabilities<AdapterCapability> caps = new Capabilities<AdapterCapability>();
caps.setCapability(AdapterCapability.CAP_SELECT);
caps.setCapability(AdapterCapability.CAP_TRANSACTIONAL_CDC);
caps.setCapability(AdapterCapability.CAP_METADATA_ATTRIBUTE);
return caps;
}

@Override
public Capabilities<AdapterCapability> getCDCCapabilities(String version) throws
AdapterException {
Capabilities<AdapterCapability> caps = new Capabilities<AdapterCapability>();
caps.setCapability(AdapterCapability.CAP_SELECT);
caps.setCapability(AdapterCapability.CAP_TRANSACTIONAL_CDC);
caps.setCapability(AdapterCapability.CAP_METADATA_ATTRIBUTE);
return caps;
}

Transactional and non-transactional real-time adapters

There are two types of real-time adapter, one is transactional like Database and the other is non-transactional or
streaming like Twitter, Facebook. For streaming we have a different capability called,
AdapterCapability.CAP_NON_TRANSACTIONAL_CDC

For real-time execution, the adapter must extend the AdapterCDC interface and the getCapabilities method
should return the following at minimum:

Example

@Override
public Capabilities<AdapterCapability> getCapabilities(String version)
throws AdapterException {
Capabilities<AdapterCapability> capability = new
Capabilities<AdapterCapability>();

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 21
List<AdapterCapability> capabilities = new ArrayList<AdapterCapability>();
capabilities.add(AdapterCapability.CAP_TRANSACTIONAL_CDC);
capabilities.add(AdapterCapability.CAP_SELECT);

capability.setCapabilities(capabilities);
return capability;
}

Related Information

Capabilities [page 6]

5.3 Beginning and Ending Communication with the Remote


Source

The open and close methods establish and terminate connections with the remote source.

The open method

This method is invoked when the adapter tests the connection for a remote source, or when it browses a remote
source for metadata or reads data.

The adapter must establish a connection to the remote source using the provided connection information and
ideally keep that connection open until the close method is invoked.

Before an adapter executes any commands other than querying the capabilities or
RemoteSourceDescriptions, the open method is called for a new adapter instance. Here, you should validate
the provided parameters, and throw an AdapterException otherwise.

Example

@Override
public void open(RemoteSourceDescription connectionInfo, boolean isCDC) throws
AdapterException {

@SuppressWarnings("unused")
String password = "";
try {
username = new
String(connectionInfo.getCredentialProperties().getCredentialEntry(CREDENTIAL).get
User().getValue(),
"UTF-8");
password = new
String(connectionInfo.getCredentialProperties().getCredentialEntry(CREDENTIAL).get
Password().getValue(),
"UTF-8");
} catch (UnsupportedEncodingException e1) {

Data Provisioning Adapter SDK Guide


22 PUBLIC Building an Adapter by Extending the AdapterCDC
throw new AdapterException(e1, e1.getMessage());
}

name =
connectionInfo.getConnectionProperties().getPropertyEntry("name").getValue();
}

The close method

When this method is invoked, the adapter must clean up all the memory, shut down any threads, and terminate
the connection to the remote source.

After it has been closed, this adapter instance can no longer handle transactions unless a new open operation is
performed.

Example

@Override
public void close() throws AdapterException {
// There are no resources or connections to be closed
}

5.4 Browsing a Remote Source's Metadata

The setFetchSize, setBrowseNodeId, and browseMetadata methods are used for metadata browsing.

The framework executes these methods to perform browsing of a remote source object. The browseMetadata
method may be called multiple times if there are more than fetchSize nodes.

The setFetchSize method

This method establishes the size of the delivery unit. The Data Provisioning Agent reads the
framework.fetchSize parameter from the dpagentconfig.ini file. When the agent invokes this method of
the adapter, the agent passes that value. This method must assign it to an internal variable so it can be referenced
when the getNext and browseMetadata methods are invoked in order to limit the number of records or
metadata objects being returned by those methods.

Example

@Override
public void setFetchSize(int fetchSize) {
this.fetchsize = fetchSize;
}

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 23
The setBrowseNodeID and browseMetadata methods

When you expand a node inside a remote source, the adapter method setBrowseNodeId invokes to define which
element of the hierarchy was expanded. If the remote source itself was expanded, the passed nodeId string is null;
otherwise, it will be the nodeId string of the expanded node returned by the browseMetadata method.

In the example of a database adapter, the list of all users is at the root level. When one of these users is expanded,
the list of that user’s tables displays. Therefore, whenever the nodeId of the setBrowseNodeId is null, the
browseMetadata method returns a list of users and the nodeId is set to the user name. When the
setBrowseNodeId is not null, the nodeId set is a user and hence returns a list of nodes and their nodeId should
be <user>.<tablename> to uniquely define a table.

If a third level is required, it is recommended to use the following format: <database>.<user>.<table>.


Therefore, browseMetadata would return all databases with nodeId=<databasename> in case the
setBrowseNodeId was null. When the setBrowseNodeId is a database alone, return a list of users for this
database and name each node in the format <database>.<user>. If the node to expand is in the format
<database>.<user>, then return a list of tables with the node name format
<database>.<user>.<tablename> to uniquely identify each table.

Note
Do not rely on the hierarchy in SAP HANA studio to open sequentially. Suppose you expand a hierarchy, then
the session timed out. Upon reconnecting, a new adapter instance starts. The first node to be expanded will be
one of the tree, for example <database1.user1>. So don't keep track of the hierarchy levels expanded in the
browseMetadata method. Also, all nodes should be unique and allow reconstructing the full hierarchy path.

Example

@Override
public void setBrowseNodeId(String nodeId) throws AdapterException {
this.currentbrowsenode = nodeId;
}
@Override
public List<BrowseNode> browseMetadata() throws AdapterException {
if (this.currentbrowsenode == null) {
List<BrowseNode> nodes = new ArrayList<BrowseNode>();
// list should have at max this.fetchsize elements
// okay, in this case it is just one always
BrowseNode node = new BrowseNode(HELLO, HELLO);
node.setImportable(true);
node.setExpandable(false);
nodes.add(node);
return nodes;
} else {
// Well, since there is no hierarchy all non-root nodes return zero
children
return null;
}
}

Data Provisioning Adapter SDK Guide


24 PUBLIC Building an Adapter by Extending the AdapterCDC
5.5 Preparing to Create Virtual Tables in SAP HANA

When SAP HANA creates a new virtual table, the table metadata is required. Your task is therefore to return a
TableMetadata object containing the expected structure in SAP HANA.

The importMetadata() method

Typically this involves specifying a unique table name with which the exact source table can be identified, for
example, nodeId is <database>.<user>.<tablename>. Therefore, TableMetadata should use the same name
as table name so that when querying the virtual table, the adapter does not get just a table name but the full
information required: database, user, and table name in one string.

Furthermore for each source column a SAP HANA datatype has to be chosen and other information being set,
primary key information for example.

Source-specific metadata can be attached to the table and each column by using the corresponding
setAttribute() method. This metadata persists in SAP HANA along with the virtual table and can be queried.

Example

@Override
public Metadata importMetadata(String nodeId) throws AdapterException {
if (nodeId.equals(HELLO)) {
List<Column> schema = new ArrayList<Column>();
Column col1 = new Column("ROWNUMBER", DataType.INTEGER);
schema.add(col1);
Column col2 = new Column("TEXT", DataType.NVARCHAR, 80);
schema.add(col2);
TableMetadata table = new TableMetadata();
table.setName(nodeId);
table.setColumns(schema);
return table;
} else {
throw new AdapterException("No remote table of this name");
}
}

Custom parameters

Some adapters (Twitter, SOAP, ABAP) support the ability to pass custom parameters (user-specified override
parameters) to various input sources and output sources. You can create such adapters by allowing an optional
parameter list to be specified for a virtual table input or output source within the flowgraph XML and for virtual
tables in a Replication Task.

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 25
Table 1: TableOptions types

Type Description

READER_OPTIONS Reader options for input source

LOADER_OPTIONS Loader options for output source

CDC_READER_OPTIONS Reader options for input source that supports change data
capture (CDC)

CDC_LOADER_OPTIONS Loader options for output source that supports change data
capture (CDC)

At design time, during the call to the importMetadata() method, you can set these options as shown in this
example.

TableMetadata metas = new TableMetadata();


//add some columns
//add some reader options also
TableOptions cdcReaderOptions = new TableOptions("Public_Stream");
pEntry = new PropertyEntry("track", "Phrases to track", "A comma-separated list of
phrases e.g. SAP,hello world", false);
cdcReaderOptions.addProperty(pEntry);
pEntry = new PropertyEntry("follow", "User IDs to follow", "A comma-separated list
of user IDs, e.g. 76117579,19923144", false);
cdcReaderOptions.addProperty(pEntry);
//add some reader options to this table--it could be either reader or loader
metas.setTableOptions(OptionsType.CDC_READER_OPTIONS, cdcReaderOptions);

During execution, you can then extract the supplied values from the user interface.

These properties are stored and queried using the following statement: SELECT * FROM
"PUBLIC"."VIRTUAL_TABLE_PROPERTIES"

if (info.getTableOptions().getPropertyGroup("Public_Stream") != null) {
propGroup = info.getTableOptions().getPropertyGroup("Public_Stream");
String track = propGroup.getPropertyEntry("track").getValue();
String follow = propGroup.getPropertyEntry("follow").getValue();
}
Select * from <Virtual_Table_Name> WITH DATA PROVISIONING PARAMETERS ('
<PropertyGroup name="__DP_CDC_READER_OPTIONS__">
<PropertyGroup name="Public_Stream">
<PropertyEntry name="track ">Hello World</PropertyEntry>
<PropertyEntry name="follow ">11223344, 22334455</PropertyEntry>
</PropertyGroup>
</PropertyGroup>');

You can see these values reflected in the following XML sample.

<taskDefinition defaultSchema="SYSTEM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-


instance" xsi:noNamespaceSchemaLocation="taskPlan.xsd" name="" version=""
description="">
<inputSources>
<inputSource type="virtualTable" schema="SYSTEM" name="VT_ORA_QA_EMP"
binding="VT_ORA_QA_EMP">
<parameters>
<parameter name="__DP_CDC_READER_OPTIONS__">
<![CDATA[<PropertyGroup name="Public_Stream">
<PropertyEntry name="track ">Hello World</PropertyEntry>
<PropertyEntry name="follow ">11223344, 22334455</PropertyEntry>
</PropertyGroup>

Data Provisioning Adapter SDK Guide


26 PUBLIC Building an Adapter by Extending the AdapterCDC
]]></parameter>
</parameters>
<mapping source="COL1” target="COL1" isPrimaryKey="true" />
<mapping source="COL2" target="COL2" />
<mapping source="COL3" target="COL3" />
<mapping source="COL4" target="COL4" />
</inputSource>
</inputSources>
.
.
.
.
<operationDefinition name="">
.
.

5.6 Executing Queries

To read data from the source, SAP HANA sends a SQL string to the adapter and then continues fetching sets of
rows until no more are found.

The executeStatement method

The query execution starts with an executeStatement call that passes the SQL constructed by the Data
Federation Layer into the adapter along with generic information about the statement.

The first task is to break down the provided SQL string into its components. For support, the
ExpressionParserUtil.buildQuery method is provided. This call returns a query object that can then be
further analyzed regarding the select part, joins, filters, and other components.

Note
With the capabilities on the adapter, table, and potentially on the column level, the kind of SQL produced by the
Data Federation Layer is controlled. If, for example, the filter capability had been turned off entirely, no SQL
string passed in would ever contain a WHERE clause, thus making the parsing much simpler.

Example

@Override
public void executeStatement(String sql,StatementInfo info) throws
AdapterException {
Query query = null;
List<ExpressionParserMessage> messageList = new
ArrayList<ExpressionParserMessage>();
query = (Query) ExpressionParserUtil.buildQuery(sql, messageList);
if(query == null)
throw new AdapterException("Parsing the sql " + sql + " failed");
ExpressionBase sda_fromclause = query.getFromClause();
if (sda_fromclause instanceof TableReference) {
// Kind of useless, only one single table exists anyhow...
tablename_to_read = ((TableReference) sda_fromclause).getUnquotedName();

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 27
} else {
throw new AdapterException("select does not read a single table???");
}
rowsread = 0;
}

The getNext method

After the executeStatement method completes, the SgetNext method is called in a loop until no additional
rows return.

Example

@Override
public void getNext(AdapterRowSet rows) throws AdapterException {
if (tablename_to_read.equals(HELLO)) {
int batchsize = 0;
while (rowsread<10 && batchsize<this.fetchsize) {
AdapterRow row = rows.newRow();
/*
* Actually we need to check what columns had been selected.
* But since the AdapterCapability.CAP_PROJECT has not been set
* we know for sure the table structure: it is the HELLO table with
the columns
* ROWNUMBER of type Integer
* TEXT of typo nvarchar(80)
*/
row.setColumnValue(0, rowsread);
row.setColumnValue(1, username + " said: Hello " + name);
rowsread++;
batchsize++;
}
} else {
// cannot happen anyhow
}
}

5.7 Real-Time Adapter Execution

Adapters can not only be queried but can push messages to SAP HANA in real time as well. The SAP HANA user
executes the command:

Example

create remote subscription <name> using (select * from <virtual_table> where ...)
target [table | task | procedure] <targetname>;

When this subscription becomes active, the adapter will be informed and must send changes from the source
immediately. These changes are sent as a stream of rows in real time.

Data Provisioning Adapter SDK Guide


28 PUBLIC Building an Adapter by Extending the AdapterCDC
Example

T1; Seq1; insert into table1


T1; Seq2; insert into table1
T2; Seq3; insert into table1
send data
T2; Seq4; insert into table2
T2; Seq5; commit
T1; Seq6; commit
send data

Related Information

Real-Time Recovery Considerations [page 29]


Adding and Removing Real-Time Subscriptions [page 30]
Starting and Stopping Real-Time Subscriptions [page 31]
Real-Time Execution Sequence [page 33]
Real-Time Changed Rows [page 34]
Row Types [page 35]
Real-Time Large-Object Columns [page 36]

5.7.1 Real-Time Recovery Considerations

Several decisions are involved in coding for real-time recovery.

Whenever an adapter restarts and there were active subscriptions, the adapter must identify the records that have
already been processed by SAP HANA for each subscription. This depends on the type of source options that are
available, controlled by the methods requireDurableMessaging and supportsRecovery.

The supportsRecovery method

If this method returns true, the stream of data is received and when a transaction is committed, all the rows with
the commit's transaction ID are written into SAP HANA and committed there. When the adapter restarts, the Data
Provisioning Server calls the start method, and its SubscriptionSpecification.getCommittedID
parameter contains the transaction ID for the data the server would like to get again. It is the adapter's
responsibility now to send the data starting from this past transaction ID again.

When the supportsRecovery method returns false because the source does not allow reading of past data, the
Data Provisioning Server must persist the sent data. The details of this are controlled by the method:
requireDurableMessaging.

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 29
Example

@Override
public boolean supportsRecovery() {
return false;
}

The requireDurableMessaging method

If recovery is not supported, and requireDurableMessaging returns true, all data sent is persisted in the Data
Provisioning Server. For example, if there is a failure during Seq5, the adapter does not need to send Seq1 to Seq4
again. Data that was sent successfully is never lost.

If the method returns false, the Data Provisioning Server can avoid the overhead of storing the intermediate data
in a queue, but the adapter needs to be able to send transactions again. The logic would be that the adapter sends
the data, the Data Provisioning Server waits until the commit is received, and then all changed rows of the
transaction are inserted into SAP HANA. Once the data is committed in SAP HANA, the adapter commitChange
method is invoked and therefore the adapter comprehends that the transaction was safely stored and will not be
requested again. Rows not committed or the commitChange was not received the adapter will need to send again.

Example

@Override
public boolean requireDurableMessaging() {
return true;
}
@Override
public void committedChange(SubscriptionSpecification spec) throws
AdapterException { }

5.7.2 Adding and Removing Real-Time Subscriptions

When a user requests a new real-time subscription, that definition is first added to the adapter by calling the
addSubscription() method. The provided SubscriptionSpecification object contains all information
about the definition, e.g. the select statement of the requested real-time data.

The addSubscription method

At this point the adapter must do everything required to prepare for the real-time capture of the source but should
not activate it yet.

Example

@Override

Data Provisioning Adapter SDK Guide


30 PUBLIC Building an Adapter by Extending the AdapterCDC
public String addSubscription(SubscriptionSpecification spec) throws
AdapterException {
// we do not deal with new subscriptions here but in start
return spec.getSubscription();
}

If the source supports subscribing to changes, like databases or Salesforce, you can use addSubscription to
subscribe to that source and get an ID which you return in this method. If the source does not support subscribing,
this method should just return null.

The removeSubscription method

This is the reverse operation to addSubscription. The subscription was stopped already and should be removed
entirely.

Example

@Override
public void removeSubscription(SubscriptionSpecification spec) throws
AdapterException {
// we do not deal with new subscriptions here but in stop
}

5.7.3 Starting and Stopping Real-Time Subscriptions

When a subscription should start to collect changes in the source and send them to SAP HANA, the start method
is called. When a subscription is reset or removed, the stop method is called.

The start method

The activation of real-time data capture should be done when the start method is called. The adapter gets the
ReceiverConnection object and the SubscriptionSpecification object and thus has everything necessary
to start collecting the changes in the source and send rows via the ReceiverConnection parameter.

Note
The ReceiverConnection is the same for all subscriptions; it is not subscription-specific.

The start method gets the SubscriptionSpecification parameter, which contains all the information about
the requested data. Therefore, the typical procedure is:

1. Remember the ReceiverConnection parameter; it will be needed when sending the data.
2. Parse the received SQL.

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 31
3. Start capturing changes in the source.
4. Start a listener thread receiving the source changes and sending these as changed rows to SAP HANA.

Example

@Override
public void start(ReceiverConnection conn, SubscriptionSpecification spec) throws
AdapterException {
// remember the connection. If it was set already it does not matter, the
connection is the same for all
this.receiverconnection = conn;
// add the new spec to the list of all currently active specs
this.activespecs.put(spec.getSubscription(), spec);
// Parse the spec's SQL Statement so we know what table to read from.
// Granted, there is just one table but its the principles
Query query = null;
List<ExpressionParserMessage> messageList = new
ArrayList<ExpressionParserMessage>();
query = (Query) ExpressionParserUtil.buildQuery(spec.getSQLStatement(),
messageList);
if(query == null)
throw new AdapterException("Parsing the sql " + spec.getSQLStatement() +
" failed");
ExpressionBase fromclause = query.getFromClause();
String tablename = null;
if (fromclause instanceof TableReference) {
// Kind of useless, only one single table exists anyhow...
tablename = unquote(((TableReference) fromclause).getName());
} else {
throw new AdapterException("select does not read a single table???");
}
// We need the table metadata, actually the selected columns from the Query.
// But since the adapter does not support projections, that is the same
anyhow.
TableMetadata m = (TableMetadata) importMetadata(tablename);
this.activespeccolumns.put(spec.getSubscription(), m.getColumns());
// This adapter does something every 5 seconds, we need a thread for that.
// But one thread is enough for all subscriptions.
if (poller == null) {
poller = new Poller();
poller.start();
}
}

The stop method

This is the inverse operation to start, so it should undo everything start did. It tells the source that changes are no
longer requested for this subscription.

@Override
public void stop(SubscriptionSpecification spec) throws AdapterException {
this.activespecs.remove(spec.getSubscription());
this.activespeccolumns.remove(spec.getSubscription());
}

Data Provisioning Adapter SDK Guide


32 PUBLIC Building an Adapter by Extending the AdapterCDC
5.7.4 Real-Time Execution Sequence

The real-time activation commands in SAP HANA occur in four phases: create the remote subscription, alter its
queue, perform the initial load, and mark the end of that load.

There is one peculiar issue in real time: A typical real-time scenario is to copy all existing rows from the source to
the target and then start the real-time capture. But that approach has a timing issue. If the capture starts after the
initial load completes, all the changed rows during the initial load could be lost. If the real-time capture starts
before the initial load, the initial load might find rows already and produce an error with a primary key violation or,
worse, update the recently changed record with the initial values.

To solve that problem and not force customers to switch their source system into a read-only mode while the initial
load runs, the real-time activation commands in SAP HANA run in four phases.

1. Create the remote subscription: This defines the remote subscription, the SQL requested, what the target is,
and so on. All of this is validated, but aside from that, nothing happens. No information is sent to the adapter.
2. Alter the remote subscription queue: This is when the adapter’s addSubscription and start methods are
called. The addSubscription is called only if this is a new subscription, not one that was previously stopped.
Therefore the adapter starts collecting changes and sends them to the Data Provisioning Server. The
subsequent rows are not applied to the target table; they are queued. Then the adapter’s beginMarker
method is called, and it should add a changed row of type BeginMarker to the stream of data.(This separation
is required for transaction log reader adapters only. Regular adapters just send the begin marker as requested
and nothing else.)
3. Execute the initial load: Now there is enough time to execute the initial load by copying the entire data set
from the source to the target, for example by executing an INSERT...SELECT from the virtual table. Because
the changed data is still queued, no data is written into the target table yet.
4. Now that the initial load is finished, add an EndMarker row: The user executes the SQL ALTER REMOTE
SUBSCRIPTION DISTRIBUTE command. With this command, the adapter’s endMarker method is called,
and the method must add an EndMarker row into the stream of data. When the Data Provisioning Server
receives this row, it knows that the initial load has been completed, and it can start distributing all future
changed rows into the SAP HANA target.

Example

@Override
public void beginMarker(String markername, SubscriptionSpecification spec)
throws AdapterException {
if (receiverconnection != null) {
AdapterCDCRowSet rowset =
AdapterAdmin.createBeginMarkerRowSet(markername);
rowset.getCDCRow(0).setSeqID(
new SequenceId(System.currentTimeMillis()));
receiverconnection.sendRowSet(rowset);
} else
throw new AdapterException(
"begin marker requested for a non-active subscription???");
}
@Override
public void endMarker(String markername, SubscriptionSpecification spec)
throws AdapterException {
if (receiverconnection != null) {
AdapterCDCRowSet rowset =
AdapterAdmin.createEndMarkerRowSet(markername);
rowset.getCDCRow(0).setSeqID(
new SequenceId(System.currentTimeMillis()));
receiverconnection.sendRowSet(rowset);

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 33
} else
throw new AdapterException(
"end marker requested for a non-active subscription???");
}

5.7.5 Real-Time Changed Rows

The final step is to implement the code that sends real-time changed rows to SAP HANA.

The adapter is now running and waiting for changed rows in the source. The real-time environment is
transactional: when changed rows are sent to SAP HANA, they will be loaded, but not committed. Therefore, they
do not display unless the adapter sends a commit row command.

Note
There are two kinds of adapters, one that is transactional and another where each row is considered as one
transaction.

In order to send rows, the adapter should use the ReceiverConnection from the start method. By calling its
sendRowSet(AdapterCDCRowSet) method, all rows of that AdapterCDCRowSet are sent to SAP HANA.

Hence, the task of the adapter is to retrieve changed rows, create an AdapterCDCRowSet object, and this object
has to contain all changed rows for all subscriptions plus a commit row.

Dealing with multiple subscriptions can be challenging. If, for example, subscription1 requested col1, col2 where
col1=1, and subscription2 requested col1, col5 without a filter. The adapter needs to capture the changes of col1,
col2, and col5 for all rows in the source. And for each changed row, create one rowset – with the requested
columns – for subscription1 with those rows that met the filter criteria and a second rowset for col1, col5 and all
changed rows – no filter had been requested.

Or, using CDCCapabilities, you can determine what kind of SQL can be pushed down into the adapter and let
SAP HANA do the rest.

Example

class Poller extends Thread {

@Override
public void run() {
int rowcount = 0;
while (isInterrupted() == false) {
try {
TimeUnit.SECONDS.sleep( 5 );
} catch (InterruptedException e) {
interrupt();
}
if (receiverconnection != null && activespecs.size() != 0) {
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.putLong(System.currentTimeMillis());
byte[] transactionid = buffer.array();
SequenceId sequence = new SequenceId(System.currentTimeMillis());
try {
for (SubscriptionSpecification spec : activespecs.values()) {
List<Column> columns =
activespeccolumns.get(spec.getSubscription());

Data Provisioning Adapter SDK Guide


34 PUBLIC Building an Adapter by Extending the AdapterCDC
if (columns != null) {
AdapterCDCRowSet rows = new
AdapterCDCRowSet(spec.getHeader(), columns);
AdapterCDCRow row = rows.newCDCRow(RowType.INSERT);
row.setColumnValue(0, rowcount);
row.setColumnValue(1, username + " said: Hello " +
name);
row.setTransactionId(transactionid);
row.setSeqID(sequence);
receiverconnection.sendRowSet(rows);
rowcount++;
} else {
// cannot happen
}
}
// do not send a commit row if there is no active spec

receiverconnection.sendRowSet(AdapterAdmin.createCommitTransactionRowSet(new

SequenceId(System.currentTimeMillis()),
transactionid));
} catch (AdapterException e) {
// data was not been sent
}
}
}
}
}

5.7.6 Row Types

Defining real-time rows includes specifying the row (operation) type.

For example:

addCDCRow("RSSFEED", adapterrow, RowType.UPSERT);

Row types include:

● INSERT, UPDATE, and UPSERT


● BEFORE_IMAGE and AFTER_IMAGE represent the two parts of an update. The BEFORE_IMAGE row must be
immediately followed by its AFTER_IMAGE row. It is valid to send an AFTER_IMAGE row only, but the virtual
table must have a primary key defined.
● For DELETE rows, all columns must retain the previous values. For example, for the table CUSTOMER with
primary key CUSTOMER_ID, the delete row must include the prior values for LASTNAME, FIRSTNAME, and so
on plus the CUSTOMER_ID value. This is significant for tables that have no primary key.
● EXTERMINATE is similar to DELETE except that only the primary key columns are considered. A primary key
must be defined in the virtual table.
● TRUNCATE means to delete a batch of rows all at once. All columns with values other than null are used in the
WHERE clause of the statement. Note that a TRUNCATE issued on a table with the all columns null is valid,
which would delete all the records from the target table.
● REPLACE rows follow TRUNCATE rows and are inserted, allowing you to replace a set of rows with new ones.

Refer to the Javadoc for a current list of supported row types.

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 35
Example
An adapter queries an RSS news feed every five minutes and returns the last 50 headlines. Because you don't
know which rows were loaded five minutes ago, use RowType.UPSERT to insert new rows and update existing
ones. (RSS does not support deletes.)

Example
You implemented an adapter that reads all the operations on a database table. It includes INSERT,
BEFORE_IMAGE, AFTER_IMAGE, and DELETE rows. The database supports TRUNCATE statements to enable
deleting all the contents of a table. Therefore, a TRUNCATE row with no column values set would be sent to SAP
HANA. Or, the table might be partitioned and the partition with REGION=’US’ should be truncated. Then a
TRUNCATE row with the REGION column containing the value US should be sent to SAP HANA.

Example
The source system indicates that sales order 1234 has changed. You don't know what changed; all you see are
line items 2 and 3 of the sales order. Therefore, in the target you must delete all rows of this sales order and
insert the current source rows again: DELETE WHERE salesorder=1234 and INSERT line items 2 and 3. In
other words, a TRUNCATE row with the SALESORDER column set to 1234 would be followed by all the current
line items using REPLACE.

5.7.7 Real-Time Large-Object Columns


Large-object data types require additional considerations when creating a real-time Data Provisioning Adapter.

When a changed record gets pushed to a Data Provisioning Adapter, the data for all subscribed columns are sent.
A possible exception to this behavior is when one of the columns is a large object (BLOB, CLOB, or NCLOB data
type). These columns sometimes contain too much data to be sent in a single call. If so, the large-object data won't
be sent with the changed record.

When receiving a changed record, the adapter must check the data type of each column to determine if it’s a large
object type and verify whether data was sent for those columns. If there are one or more large object columns
without data, the adapter must call the Row.setColumnLobIdValue(int columnIndex, long lobId) or
Row.setColumnLobIdValue(int columnIndex, long logId, LobCharset charSet) method to notify
the Data Provisioning Server that the column is a LOB and that its data needs to be fetched from the remote
source. The lobId value being passed to the Row.setColumnLobIdValue() method must identify the column and the
row for which data needs to be retrieved from the remote source. The Data Provisioning Server will provide this
value later when invoking the getLob() method of the adapter. If the large object column is NCLOB, you must use
the Row.setColumnLobIdValue(int columnIndex, long logId, LobCharset charSet) method
because it also indicates the encoding applied to the data, which is required for internal processing.

When the changed record is sent to the Data Provisioning Server using the
ReceiverConnection.sendRowSet(RowSet) method, the server determines whether there are any large-
object columns for which data must be retrieved from the remote source. If there are, the adapter getLob(long
lobId, byte[] bytes, int bufferSize) method will be invoked as many times as necessary to get all of
the data. The lobId parameter provided in the getLob() method is the same as what the adapter provided when
calling Row.setColumnLobIdValue(int columnIndex, long lobId) or

Data Provisioning Adapter SDK Guide


36 PUBLIC Building an Adapter by Extending the AdapterCDC
Row.setColumnLobIdValue(int columnIndex, long logId, LobCharset charSet), and it must
contain a value that can be used to identify the row and column for which data must be retrieved from the remote
source. The byte array parameter is empty and must be completed by the adapter, but it can’t contain more bytes
than indicated by the bufferSize parameter.

5.8 Enterprise Semantic Services API Support

Enterprise Semantic Services lets business users identify a data's source by providing an array of information
about the object.

The API includes the following built-in procedures:

Procedure Description

GET_REMOTE_SOURCE_OBJECTS_TREE When a user navigates into the browsing hierarchy of the re­
mote source to select objects/containers to publish, GET_RE­
MOTE_SOURCE_OBJECTS_TREE isthe only way to get access
to the container of an object in the hierarchy.

GET_REMOTE_SOURCE_TABLE_DEFINITIONS Get the common table metadata (the information the adapter
already has in its TableMetadata object). Provides a light­
weight procedure to get legacy metadata.

GET_REMOTE_SOURCE_OBJECTS_LIST Get a flat list of all remote objects with their unique names.

GET_REMOTE_SOURCE_OBJECT_DESCRIPTIONS Get the multi-language descriptions of the objects.

GET_REMOTE_SOURCE_COLUMN_DESCRIPTIONS Get the multi-language descriptions of the columns.

Adapters can support Enterprise Semantic Services by calling these procedures. For example, by calling
GET_REMOTE_SOURCE_TABLE_DEFINITIONS with a defined remote source name and remote table name(s), you
can get detailed information about tables, columns, descriptions, primary keys, foreign keys, and so on.

These procedures can also provide information not available in regular tables like table properties, column
properties, and Enterprise Semantic Services definitions. Therefore, as an adapter developer, it is useful to provide
as much information as possible about the tables, for example using importMetadata(). These procedures let
you query a remote source to retrieve all table metadata subset elements at once rather than importing each one
as a virtual object.

Related Information

GET_REMOTE_SOURCE_TABLE_DEFINITIONS Procedure [page 38]


GET_REMOTE_SOURCE_OBJECTS_LIST Procedure [page 39]
GET_REMOTE_SOURCE_OBJECT_DESCRIPTIONS Procedure [page 41]
GET_REMOTE_SOURCE_COLUMN_DESCRIPTIONS Procedure [page 41]
IS_TREE Property [page 42]

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 37
5.8.1 GET_REMOTE_SOURCE_TABLE_DEFINITIONS Procedure

Get the common table metadata (the information the adapter already has in its TableMetadata object).

Input Parameters

Parameter Data Type Description

REMOTE_SOURCE_NAME NVARCHAR(256) Name of the remote source to query

UNIQUE_NAME NVARCHAR(5000) A tabletype (GET_REMOTE_SOURCE_OB­


JECTS_UNIQUE_NAMES_TYPE) with the list of unique
names (column UNIQUE_NAME) for which the detailed
data is requested.

Output Parameters

Column Data Type Description

UNIQUE_NAME NVARCHAR(5000) Unique name with which to import the table

DATABASE_NAME NVARCHAR(256) Database

OWNER_NAME NVARCHAR(256) Owner

PHYSICAL_NAME NVARCHAR(256) Physical name of the object

REMOTE_OBJECT_TYPE VARCHAR(256) To differentiate between tables, functions, views.

Allowed values are:

● TABLE
● VIEW
● PROCEDURE
● OTHER: To capture objects that are not mappable/
importable in SAP HANA. For example, SYNONYM
in SAP HANA is considered importable, but when
creating a virtual table on it, it fails.

See SAP HANA SYS.OBJECTS.OBJECT_TYPE for refer­


ence.

DESCRIPTION NVARCHAR(5000) The description in the language of the default locale

DEFAULT_LANGUAGE VARCHAR(2) The default locale for the description; can be NULL.

IS_INSERTABLE VARCHAR(5)

IS_UPDATABLE VARCHAR(5)

IS_DELETEABLE VARCHAR(5)

Data Provisioning Adapter SDK Guide


38 PUBLIC Building an Adapter by Extending the AdapterCDC
Column Data Type Description

IS_UPSERTABLE VARCHAR(5)

IS_SELECTABLE VARCHAR(5)

IS_REMOTE_SUBSCRIP­ VARCHAR(5)
TION_SUPPORTED

IS_REMOTE_SUBSCRIP­ VARCHAR(5)
TION_TRANSACTIONAL

5.8.2 GET_REMOTE_SOURCE_OBJECTS_LIST Procedure

Get a flat list of all remote objects with their unique names. The remote object unique name can be used to get
more details using subsequent calls to other procedures.

Input Parameters

Parameter Data Type Description

REMOTE_SOURCE_NAME NVARCHAR(256) Name of the remote source to query

FILTER_DATABASE NVARCHAR(256) Optional filter on the database. Does not support pat­
tern, only equals. Empty string ('') used when no data­
base, NULL value used when FILTER_DATABASE should
not be used to narrow the search.

FILTER_OWNER NVARCHAR(256) Optional filter on the owner, supporting pattern in the


form of owner%. Empty string ('') used when no owner,
NULL value used when FILTER_OWNER should not be
used to narrow the search.

FILTER_PHYSICAL_NAME NVARCHAR(256) Optional filter on the name of the object, supporting pat­
tern in the form of owner%. NULL (or empty '') value
used when FILTER_PHYSICAL_NAME should not be
used to narrow the search.

FILTER_UNIQUE_NAME NVARCHAR(5000) Optional filter on the unique name of the object, sup­
porting pattern in the form of owner%. NULL (or empty
'') value used when FILTER_UNIQUE_NAME should not
be used to narrow the search.

Output Parameters

OBJECTS: A TableType (GET_REMOTE_SOURCE_OBJECTS_LIST_TYPE) with the list of objects

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 39
Column Data Type Description

UNIQUE_NAME NVARCHAR(5000) Unique name with which to import the table

DATABASE_NAME NVARCHAR(256) Database

OWNER_NAME NVARCHAR(256) Owner

PHYSICAL_NAME NVARCHAR(256) Physical name of the object

DISPLAY_NAME NVARCHAR(5000) Display name of the importable object

OBJECT_TYPE VARCHAR(32) To differentiate between tables, functions, views.

Allowed values are:

● TABLE
● VIEW
● PROCEDURE
● OTHER--To capture objects that are not mappable/
importable in SAP HANA. For example, SYNONYM
in SAP HANA is considered importable, but when
creating a virtual table on it, it fails.

See SAP HANA SYS.OBJECTS.OBJECT_TYPE for refer­


ence.

DESCRIPTION NVARCHAR(5000) The description in the language of the default locale

DEFAULT_LANGUAGE VARCHAR(2) The default locale for the description; can be NULL.

IS_USER_DEPENDENT VARCHAR(5) An object might return the same data regardless of what
user it is calling, or it depends on the user. Example: A
table with row-level security.

LAST_MODIFICATION_TIME­ TIMESTAMP Optional, indicates the last time the object was modi­
STAMP fied.

Depending on the source, the information about the last


metadata modification might not be stored as a time­
stamp. Therefore, the adapter has to convert this infor­
mation into a TIMESTAMP.

● For sources managing incremental version numbers


(as in SAP HANA), the conversion to a timestamp
can be ADD_SECONDS (TO_TIMESTAMP
('1970-01-01 00:00:00'),
<VERSION_NUMBER>)
● For sources that directly manage timestamp, no
conversion is required.

Data Provisioning Adapter SDK Guide


40 PUBLIC Building an Adapter by Extending the AdapterCDC
5.8.3 GET_REMOTE_SOURCE_OBJECT_DESCRIPTIONS
Procedure

Get the multi-language descriptions of the objects.

Input Parameters

Parameter Data Type Description

REMOTE_SOURCE_NAME NVARCHAR(256) Name of the remote source to query

UNIQUE_NAME NVARCHAR(5000) A tabletype (GET_REMOTE_SOURCE_OB­


JECTS_UNIQUE_NAMES_TYPE) with the list of unique
names (column UNIQUE_NAME) for which the detailed
data is requested.

LANGUAGE VARCHAR(2) An optional tabletype (GET_REMOTE_SOURCE_LAN­


GUAGES_TYPE) with all languages requested; for exam­
ple, you might only be interested in EN, CN. If empty, all
languages are requested.

Output Parameters

Column Data Type Description

UNIQUE_NAME NVARCHAR(5000) Unique name with which to import the object

LANGUAGE VARCHAR(2) The language of the description

TEXT NCLOB The description of the object in the given language

5.8.4 GET_REMOTE_SOURCE_COLUMN_DESCRIPTIONS
Procedure

Get the multi-language descriptions of the columns.

Input Parameters

Parameter Data Type Description

REMOTE_SOURCE_NAME NVARCHAR(256) Name of the remote source to query

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the AdapterCDC PUBLIC 41
Parameter Data Type Description

UNIQUE_NAME NVARCHAR(5000) A tabletype (GET_REMOTE_SOURCE_OB­


JECTS_UNIQUE_NAMES_TYPE) with the list of unique
names (column UNIQUE_NAME) for which the detailed
data is requested.

LANGUAGE VARCHAR(2) An optional tabletype (GET_REMOTE_SOURCE_LAN­


GUAGES_TYPE) with all languages requested; for exam­
ple, you might only be interested in EN, CN. If empty, all
languages are requested.

Output Parameters

Column Data Type Description

UNIQUE_NAME NVARCHAR(5000) Unique name with which to import the object

COLUMN_NAME NVARCHAR(256)

LANGUAGE VARCHAR(2) The language of the description

TEXT NCLOB The description of the object in the given language

5.8.5 IS_TREE Property

The IS_TREE column in the REMOTE_SOURCES system view indicates whether Enterprise Semantic Services
must use the browsing hierarchy API in the prepare publication phase or the ObjectList procedure.

View Column SQL Data Type Dimension Not Null Comment

IS_TREE VARCHAR 5 X Specifies if browsing hi­


erarchy is deeply
nested: TRUE/FALSE

By default, all remote sources previously created have this property set to false (which means the browsing
hierarchy is not flat). If you want your adapter to browse tree-like hierarchies, add the
CAP_SUPPORT_RICH_METADATA_BY_TREE capability to your adapter.

Sample Code

public Capabilities<AdapterCapability> getCapabilities(String version)


throws AdapterException {
Capabilities<AdapterCapability> capbility = new
Capabilities<AdapterCapability>();
//Add other capabilities here
……
……
capbility.setCapability(AdapterCapability.CAP_SUPPORT_RICH_METADATA_BY_TREE);
return capbility;
}

Data Provisioning Adapter SDK Guide


42 PUBLIC Building an Adapter by Extending the AdapterCDC
Data Provisioning Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC PUBLIC 43
6 Building an Adapter by Extending the
BaseAdapterClass

In order to simplify working with the adapter SDK for common adapters, the BaseAdapterClass and its
referenced TableLoader class can be extended.

The most important constraint for adapters of this kind is they do not support push-down of joins and expressions.
For example, SELECT SUBSTRING(col1, 1, 10) FROM <virtualtable> could not be handled by such an
adapter. Instead, SAP HANA would select the col1 from the adapter and perform the substring operation inside
the database.

While the BaseAdapterClass deals with all global operations, for each table (or group of tables), a TableLoader
class (or its simplified version, the TableLoaderSimpleFilter class) is to be used. This is useful in particular for
tables where the table structure is constant. For example a Tweet always has the same columns, hence you could
build a TableLoader for Tweets..

The TableLoaderSimpleFilter class is based on the TableLoader class but analyzes the filter conditions and
arranges them into lists of filters per column. Therefore, the pushed-down filter requires AND conditions between
different columns (no ORs). For each single column, multiple filters are allowed with either ANDs or ORs but not
both. In other words, the logical bracketing is:

((col1 … OR col1 … OR col1) AND (col2… OR col2… OR col2…) AND (col3…..

Related Information

Reporting the Configuration of the Remote Source Description for BaseAdapterClass [page 44]
Reporting the Capabilities of the Remote Source for BaseAdapterClass [page 45]
Beginning and Ending Communication with the Remote Source for BaseAdapterClass [page 46]
Browsing a Remote Source's Metadata for BaseAdapterClass [page 47]
Preparing to Create Virtual Tables in SAP HANA for BaseAdapterClass [page 47]
Executing Queries for BaseAdapterClass [page 48]
Real-Time Execution for BaseAdapterClass [page 50]
Real-Time Changed Rows for BaseAdapterClass [page 50]

6.1 Reporting the Configuration of the Remote Source


Description for BaseAdapterClass
The methods to add the remote source description for BaseAdapterClass are similar to those for AdapterCDC,
except that there are two methods now. Each method gets the root object to add the custom properties to. Also,
helper methods are provided to simplify adding elements like addUserCredential.

Data Provisioning Adapter SDK Guide


44 PUBLIC Building an Adapter by Extending the BaseAdapterClass
The addRemoteSourceDescriptors method

By adding other PropertyGroup or PropertyElement objects to the provided PropertyGroup, a hierarchy of


elements for which the user must provide is created.

Example

@Override
public void addRemoteSourceDescriptors(PropertyGroup root) throws
AdapterException {
root.addProperty(new PropertyEntry("name", "Hello whom?"));
}

The addRemoteSourceCredentialDescriptors method

For all elements added to the provided CredentialProperties object, the user provided values will be stored in
a SAP HANA secured area. The helper methods addUserCredential and addPasswordOnlyCredential are
available to simplify that task.

Example

@Override
public void addRemoteSourceCredentialDescriptors(CredentialProperties credential)
throws AdapterException {
addUserCredential(credential, "Credentials", "Credentials", "Username",
"Password");
}

6.2 Reporting the Capabilities of the Remote Source for


BaseAdapterClass

The Data Provisioning Adapter invokes these methods to get the list of adapter capabilities, defining what the
adapter can do, and get the source version. These methods will be invoked before or after opening the remote
source.

As the BaseAdapterClass itself supports projections, real-time transactions and more, it returns a first set of
capabilities already. Typical pushdown scenarios are turned on by overriding the corresponding pushdown*
method and letting it return true. This sets the corresponding capabilities. The default implementation is that each
capability is turned off.

If more flexible control over the capabilities is needed, the get*Capabilities method can return an array of
capabilities to be set for the adapter.

The same approach is taken for table capabilities and column capabilities. The default implementation sets them
to something useful, but can be overridden.

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the BaseAdapterClass PUBLIC 45
Example

protected boolean pushdownSDAsupportsFilters()


protected boolean pushdownSDAsupportsPerColumnSettings()
protected boolean pushdownSDAsupportsAnd()
protected boolean pushdownSDAsupportsOr()
protected boolean pushdownSDAsupportsIn()
protected boolean pushdownSDAsupportsLike()
protected boolean pushdownSDAsupportsMoreThanEqual()
protected AdapterCapability[] getSDACapabilites()
protected TableCapability[] getTableCapabilities(String nodeId)

6.3 Beginning and Ending Communication with the Remote


Source for BaseAdapterClass

The open and close methods establish and terminate connections with the remote source.

The open method

These are identical to those in the AdapterCDC version. Note the use of the getPropertyValueByPath method,
however, which allows access to individual values, easily and securely.

Example

@Override
public void open(RemoteSourceDescription descriptor, boolean cdc) throws
AdapterException {
username = getUsername(descriptor, CREDENTIAL);
@SuppressWarnings("unused")
String password = getPassword(descriptor, CREDENTIAL);

name = getPropertyValueByPath(descriptor, "name");


}

The close method

Example

@Override
public void close() throws AdapterException {
// There are no resources or connections to be closed
}

Data Provisioning Adapter SDK Guide


46 PUBLIC Building an Adapter by Extending the BaseAdapterClass
6.4 Browsing a Remote Source's Metadata for
BaseAdapterClass

When SAP HANA requests the list of remote tables, the addNodes method is called, and the adapter has to add
either table nodes or group nodes to it.

Unlike when using the AdapterCDC class directly, the BaseAdapterClass constructs the hierarchical node IDs
internally and provides helper functions to create table and group nodes via createNewTableBrowseNode and
createNewGroupNode.

Example

@Override
public void addNodes(List<BrowseNode> nodes) throws AdapterException {
nodes.add(createNewTableBrowseNode(HELLO, HELLO, "Hello World Table"));
}

6.5 Preparing to Create Virtual Tables in SAP HANA for


BaseAdapterClass

Whenever SAP HANA creates a new virtual table, the table metadata is required. Therefore, your task is to return a
TableMetadata object containing the expected structure in SAP HANA.

Unlike with the AdapterCDC, this version of the importMetadata method turned the dot separated full path like
<database>.<user>.<table> into an ArrayList with the individual levels. And an empty shell of the
TableMetadata is provided as well. In addition, the TableLoader has static methods to add columns, primary
keys for various datatypes in a convenient way.

Example

public void importMetadata(ArrayList<String> fullIDStringToLevels, TableMetadata


table) throws AdapterException

Example

@Override
public void importMetadata(ArrayList<String> fullIDStringToLevels, TableMetadata
table) throws AdapterException {
TableLoaderHelloWorldAdapter2.importMetadata(table);
}

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the BaseAdapterClass PUBLIC 47
6.6 Executing Queries for BaseAdapterClass

To read data from the source, SAP HANA sends a SQL string to the adapter and then continues fetching a set of
rows until no more are found.

While in the AdapterCDC case, the executeStatement method provided the raw SQL only. The version here
parses the SQL and determines which TableLoader class to use.

The executeStatement is present in case some generic operation is required but usually can be left empty.

The executeStatement method

The executeStatement is invoked with a TableLoader object representing the source table to read from.
Additionally, the code used to read data from the source is triggered here as well.

Example

@Override
protected void executeStatement(TableLoader tableloader) throws AdapterException {
// parsing happens in the BasedAdapterClass already
}

The getTableLoader method

This method must return the proper TableLoader for each table name. Depending on the use case, each table
might have a TableLoader object on its own or a single TableLoader object that deals with all source tables. For
a database, the second implementation makes sense because the table structure is derived from the source. For a
table with a constant structure, for example the HELLO table in the HelloWorld2Adapter, one TableLoader
per table makes sense.

Example

@Override
protected TableLoader getTableLoader(String tableName)
throws AdapterException { if (tableName != null && tableName.equals(HELLO)) {
return new TableLoaderHelloWorldAdapter2(this);
} else {
throw new AdapterException("Unknow table"); } }

The TableLoader class

The TableLoader class performs two tasks:

Data Provisioning Adapter SDK Guide


48 PUBLIC Building an Adapter by Extending the BaseAdapterClass
1. Returns any object containing the source values for one row
2. Sets the values of the SAP HANA row

The getNextRowData has to return an object that contains all the data itself or that can be used to construct all
data for the current row. If the current row is the last row, then the hasNoMoreRows method is to be set.
Alternatively, when the function returns a null object, it is the indicator that no more data can be found.

Example

@Override
protected Object getNextRowData() throws AdapterException {
if (getRowNumberToRead() < 10) {
// return an object that can be used to parse the return values of the
row.
// anything as long as it is not null
return getRowNumberToRead();
} else {
hasNoMoreRows();
return null;
}
}

The object the getNextRowData returned is sent into the setColumnValue method. Based on the position of the
column in the table metadata, the row's column value has to be set.

Example

@Override
protected void setColumnValue(int tablecolumnindex,
int returncolumnindex,
AdapterRow row,
Object o) throws AdapterException {
switch (tablecolumnindex) {
case 0:
row.setColumnValue(returncolumnindex, (Integer) o);
break;
case 1:
row.setColumnValue(returncolumnindex,
((HelloWorldAdapter2) getAdapter()).username +
" said: Hello " +
((HelloWorldAdapter2) getAdapter()).name);
break;
}
}
@Override
public void executeStatementEnded() throws AdapterException {
// no resources to close
}

As an example, imagine you executed SELECT TEXT, TEXT, ROWNUMBER from HELLO. You requested the
second column of the table twice and the first column of the table as third. Because the index starts with 0, the
setColumnValue will be called with:

tablecolumnindex=1 (TEXT) and returncolumnindex=0 (first column of the select)


tablecolumnindex=1 (TEXT) and returncolumnindex=1 (second column of the select)
tablecolumnindex=0 (ROWNUMBER) and returncolumnindex=2 (third column of the select)

Therefore, only the columns actually selected from will be processed, avoiding unnecessary computations on
columns not to be read.

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the BaseAdapterClass PUBLIC 49
6.7 Real-Time Execution for BaseAdapterClass

In real-time execution, adding subscriptions, distributing the rows to the correct subscribers, and dealing with the
begin/end marker has been already implemented. All that remains is to implement the code to start and stop a
subscription.

In case the source does not support sending messages, a frequent polling can be done as well. For that, the
method getPollingInterval should return the polling sleep time in seconds. Then the poll method is called
at that frequency.

Example

@Override
protected void startSubscription(SubscriptionRuntimeInformation s) throws
AdapterException {
}
@Override
protected void stopSubscrition(SubscriptionSpecification subscription) {
}
@Override
public int getPollingInterval() {
return 5;
}

6.8 Real-Time Changed Rows for BaseAdapterClass

To send changed rows, the BaseAdapterClass provides the helper methods addCDCRow, sendRows and
commit.

If getPollingInterval returns a value other than zero, then the pollStart method is called when starting the
first subscription of the adapter and the pollEnd method when the last subscription has been removed. While at
least one subscription is active, the poll method is called every <polling-interval> seconds. The pollStart
method allows adding the logic needed before the polling even starts. Then the poll method is called frequently. If
the last subscription is removed, the pollEnd method is called.

Example

@Override
public void pollStart() throws AdapterException {
counter = 0;
}

@Override
public void poll() {
try {
addCDCRow(HELLO, counter++, RowType.INSERT);
sendRows();
commit();
} catch (AdapterException e) {
e.printStackTrace();
}

Data Provisioning Adapter SDK Guide


50 PUBLIC Building an Adapter by Extending the BaseAdapterClass
}
@Override
public void pollEnd() throws AdapterException {
}

Data Provisioning Adapter SDK Guide


Building an Adapter by Extending the BaseAdapterClass PUBLIC 51
7 Building an Adapter Using Camel Adapter

Camel Adapter is a framework that allows you to create custom adapters using a few configuration files and Spring
DSL (Domain Specific Language) with little or even no coding efforts. It is based on Apache Camel and Spring
Framework. To use Camel Adapter, you must know Apache Camel and Spring Framework.

Their websites and the versions that the Camel Adapter uses are listed below.

● Apache Camel (Version 2.15.3) http://camel.apache.org/


● Apache Camel Components (Version 2.15.3) http://camel.apache.org/components.html
● Spring Framework (Version 4.2.0) http://spring.io/

Apache Camel has many built-in components that can be used to access a variety of outside data sources. You can
use them and define a custom adapter to access these supported data sources.

Note
For now, Camel Adapter only supports non-database components, meaning that it only supports Camel
components that access non-database data sources.

Related Information

Define Custom Adapter in adapters.xml [page 52]


Use Camel Route to Implement Adapter Functionalities [page 56]
SDA Camel Route Defined within <camelContext> Tag [page 57]
Process Metadata Browse and Import [page 57]
Process SQL SELECT Statement [page 58]
Process SQL INSERT/UPDATE/DELETE Statement [page 59]
Dependent JAR Files [page 60]

7.1 Define Custom Adapter in adapters.xml

Defining a new custom adapter starts with adding an Adapter element in <DPAgent_root>/camel/
adapters.xml file. Here is an example.

Example

<Adapters>
...
<Adapter

Data Provisioning Adapter SDK Guide


52 PUBLIC Building an Adapter Using Camel Adapter
type="CamelFacebookAdapter"
displayName="Camel Facebook Adapter">
<RemoteSourceDescription>
<PropertyGroup name="configuration" displayName="Configuration">
<PropertyEntry
name="httpProxyHost"
displayName="HTTP Proxy Host"
description="HTTP Proxy Host"
isRequired="false"/>
<PropertyEntry
name="httpProxyPort"
displayName="HTTP Proxy Port"
description="HTTP Proxy Port"
isRequired="false"/>
</PropertyGroup>
<CredentialEntry
name="app_credential"
displayName="App Credential"
userDisplayName="App ID"
passwordDisplayName="App Secret"/>
<CredentialEntry
name="user_credential"
displayName="User Credential"
userDisplayName="User ID"
passwordDisplayName="User Access Token"/>
</RemoteSourceDescription>
<Capabilities>CAP_SELECT,CAP_BIGINT_BIND</Capabilities>
<RouteTemplate>facebook.xml</RouteTemplate>
</Adapter>
</Adapters>

The following must be defined in the custom adapter configuration.

● Adapter type, display name, and description


● Remote source description
● Adapter capabilities
● Camel Route template

Related Information

Adapter Type, Display Name, and Description [page 53]


Remote Source Description [page 54]
Adapter Capabilities [page 55]
Camel Route Template [page 56]

7.1.1 Adapter Type, Display Name, and Description

Adapter type, display name, and description are defined as XML attributes of the Adapter element. They are used
to construct an AdapterFactory for the custom adapter.

Data Provisioning Adapter SDK Guide


Building an Adapter Using Camel Adapter PUBLIC 53
Example

<Adapter type="CamelFacebookAdapter"
displayName="Camel Facebook Adapter"
description="Camel Facebook Adapter">

It is equivalent to the following SDK AdapterFactory definition: CamelFacebookAdapterFactory

public class CamelFacebookAdapterFactory implements AdapterFactory {


...
@Override
public String getAdapterType() {
return "CamelFacebookAdapter";
}
@Override public String getAdapterDisplayName() {
return "Camel Facebook Adapter";
}
@Override public String getAdapterDescription() {
return "Camel Facebook Adapter";
}
}

7.1.2 Remote Source Description

Remote source description defines which parameters the custom adapter has. It is defined by
RemoteSourceDescription element. What you define is what Adapter.getRemoteSourceDescription() returns.

<RemoteSourceDescription>
<PropertyGroup
name="configuration"
displayName="Configuration">
<PropertyEntry
name="httpProxyHost"
displayName="HTTP Proxy Host"
description="HTTP Proxy Host" isRequired="false"/>
<PropertyEntry
name="httpProxyPort"
displayName="HTTP Proxy Port"
description="HTTP Proxy Port" isRequired="false"/>
</PropertyGroup>
<CredentialEntry
name="app_credential"
displayName="App Credential"
userDisplayName="App ID"
passwordDisplayName="App Secret"/>
<CredentialEntry
name="user_credential"
displayName="User Credential"
userDisplayName="User ID"
passwordDisplayName="User Access Token"/>
</RemoteSourceDescription>

PropertyGroup element is corresponding to the return of RemoteSourceDescription.getConnectionProperties().


There must be a root PropertyGroup element under RemoteSourceDescription element. Under the root
PropertyGroup element, nested PropertyGroup elements and PropertyEntry elements could be defined. The
attributes of PropertyGroup and PropertyEntry element are corresponding to properties of SDK PropertyGroup

Data Provisioning Adapter SDK Guide


54 PUBLIC Building an Adapter Using Camel Adapter
and PropertyEntry. Supported PropertyGroup XML attributes and their counterparts in SDK PropertyGroup class
are below.

PropertyGroup XML Attributes SDK PropertyGroup Properties

name name

displayName displayName

description description

Supported PropertyEntry XML attributes and their counterparts in SDK PropertyEntry class are below.

PropertyEntry XML Attributes SDK PropertyEntry Properties

name name

displayName displayName

description description

defaultValue defaultValue

isRequired required

The return of RemoteSourceDescription.getCredentialProperties() is defined via CredentialEntry elements.


CredentialEntry element is corresponding to SDK CredentialEntry class. CredentialEntry element has four
attributes.

CredentialEntry XML Attributes SDK CredentialEntry Properties

name Name of the credential entry

displayName Display name of the credential entry

userDisplayName Display name of the user attribute of the credential entry.

passwordDisplayName Display name of the password attribute of the credential entry.

7.1.3 Adapter Capabilities

Adapter capabilities must be defined as a comma-separated list of Capabilities element.

Adapter Capabilities

<Capabilities>
CAP_SELECT,
CAP_BIGINT_BIND
</Capabilities>

This definition is equivalent to the following implementation of Adapter.getCapabilities(String version) method.

Data Provisioning Adapter SDK Guide


Building an Adapter Using Camel Adapter PUBLIC 55
Adapter.getCapabilities

@Override
public Capabilities<AdapterCapability> getCapabilities(String version) throws
AdapterException {
List<AdapterCapability> list = new ArrayList<AdapterCapability>();
list.add(AdapterCapability.CAP_SELECT);
list.add(AdapterCapability.CAP_BIGINT_BIND);
Capabilities<AdapterCapability> capabilities = new
Capabilities<AdapterCapability>();
capabilities.setCapabilities(list);
return capabilities;
}

7.1.4 Camel Route Template

Camel route template defines the processing logic for methods of Adapter using Spring DSL. It must be tied to
adapter definition by the RouteTemplate XML element. For example:

<RouteTemplate>facebook.xml</RouteTemplate>

Here, facebook.xml is the route template file that is put in <DPAgent_root>/cameldirectory.

Related Information

Use Camel Route to Implement Adapter Functionalities [page 56]

7.2 Use Camel Route to Implement Adapter Functionalities

The Camel route template is a Spring configuration file. Camel Adapter provides a template file (found in
<DPAgent_root>/camel/route-template.xml) with which you can begin to create the route template. Copy
this file and rename it to a new route template file name for your adapter (for example, facebook.xml).
Comments inside this file will help guide you through steps of adding content.

Use placeholders to represent remote source parameter values

Remote source parameters are defined within RemoteSourceDescription element of Adapter configuration in
adapters.xml. In the route template XML file, you can use the Spring style placeholder $
{<remote_source_parameter_name>} in Spring bean configuration, or use the Camel style placeholder
{{<remote_source_parameter_name>}} within the <camelContext> tag to represent the remote source
parameter values. Note that you cannot use the Spring style placeholder $

Data Provisioning Adapter SDK Guide


56 PUBLIC Building an Adapter Using Camel Adapter
{<remote_source_parameter_name>} within the <camelContext> tag. For example, you can use $
{<httpProxyHost>} to represent the value of the following remote source parameter.

<PropertyEntry name="httpProxyHost"
displayName="HTTP Proxy Host"
description="HTTP Proxy Host"
isRequired="false"/>

For credential entry, you must use the placeholder ${<credential_name>_user} and $
{<credential_name>_password} to represent the values of the user and password properties of the credential
entry. (<credential_name> represents the name attribute of the credential entry). For example, for the following
credential entry, you can use ${app_credential_user} and ${app_credential_password} to represent the values of
App ID and App Secret.

<CredentialEntry name="app_credential"
displayName="Credential"
userDisplayName="App ID"
passwordDisplayName="App Secret"/>

For the above examples, if the remote source parameters are used within <camelContext> tag, they must be
represented using the Camel style placeholders {{<httpProxyHost>}}, {{app_credential_user}}, and
{{app_credential_password}}.

7.3 SDA Camel Route Defined within <camelContext> Tag

The core of the route template is a Camel route that is defined to process a variaty of Adapter SDA functionalities.
At runtime, the custom adapter instance transforms the calls on Adapter SDA methods to Camel messages, and
then passes them to the Camel route to process.

<!-- Configure Camel route using Spring DSL. -->


<camelContext xmlns="http://camel.apache.org/schema/spring">
<template id="producerTemplate"/>
<route>
<from uri="direct:sda"/>
...
</route>
</camelContext>

A Camel message header DpCommand represents which operation needs to be processed for the message. Its
value can be one of the four values: browse, import, query, update.

7.4 Process Metadata Browse and Import

The messages with DpCommand header “browse” and “import” correspond to the calls on
Adapter.browseMetadata and Adapter.importMetadata, respectively. Camel Adapter provides a component
MetadataComponent to allow you to specify a metadata XML file in which all tables's definitions are configured.

Data Provisioning Adapter SDK Guide


Building an Adapter Using Camel Adapter PUBLIC 57
You must set the metadata file name as DpMetadataConfigFile header. And the metadata file must be put in the
<DPAgent_root>/camel directory.

<when>
<simple>${header.DpCommand} == 'browse' or ${header.DpCommand} == 'import'</
simple>
<setHeader headerName="DpMetadataBrowser">
<simple>config</simple>
</setHeader>
<setHeader headerName="DpMetadataConfigFile">
<simple>my-metadata.xml</simple>
</setHeader>
<to uri="dpmetadata:config"/>
</when>

7.5 Process SQL SELECT Statement

The message with DpCommand header “query” requests a SQL query. The message body is a SQL SELECT
statement.

<when>
<!-- A query command, that is, the in-message body is a SQL SELECT statement. --
>
<simple>${header.DpCommand} == 'query'</simple>
<!-- Convert the SELECT statement to a structural SQL bean. -->
<bean ref="sqlBean" method="toSQLBean"/>
<!--
Process the query command. The result must be a collection of JavaBeans or Maps
in the in-message body. The collection must be "iterable" - that is, it must
implement java.util.Iterator or java.util.Iterable.
If the result is a collection of JavaBeans, each JavaBean represents a
table record. The names of JavaBean properties must exactly match the column
names.
And subsequently 'DpResultType' header must be set to 'bean'.
If the result is a collection of Maps, each Map object represents a table
record.
The keys of Map must represent the column names, and the values are column
values.
And subsequently 'DpResultType' header must be set to 'map'.
-->
<!--
Set 'DpResultType' header to 'bean' if the result is a collection of JavaBeans.
Set 'DpResultType' header to 'map' if the result is a collection of Maps. -->
<setHeader headerName="DpResultType"><simple>bean</simple></setHeader>
<to uri="dpresult:dpresult"/>
</when>

You can convert the SELECT statement to a structural SQL bean using a Camel bean that Camel Adapter provides.
From the SQL bean, you can get all parts of the SQL statement.

SQL Bean Properties Java Types Description

columns array of java.lang.String Column names in the INSERT, UPDATE,


DELETE or SELECT statement.

Data Provisioning Adapter SDK Guide


58 PUBLIC Building an Adapter Using Camel Adapter
SQL Bean Properties Java Types Description

dmlType java.lang.String DML type - INSERT, UPDATE, DELETE or


SELECT

owner java.lang.String Schema of the table.

tableName java.lang.String Table name

values array of java.lang.String Column values.

whereClause array of com.sap.hana.dp.camel.

WhereColumn

The SELECT statement must be processed by configuring Camel processors and/or endpoints. The result must be
a collection of JavaBeans or Maps, and is set as the message body before it flows into the 'dpresult:dpresult'
endpoint. The following must be noted.

● If the result is a collection of JavaBeans, each JavaBean is corresponding a data row in the target table. Each
JavaBean property must have same name as the column name in the target table.
● If the result is a collection of Maps, each Map object is corresponding a data row in the target table. In the map,
keys must be the column names of the target table.
● The result must be a collection of JavaBeans or Maps. Here, the collection object could be an array, a Java
collection class that implements java.util.Iterator, a Java class that implements java.util.Iterable, or even a
single JavaBean or Map that represents only one row result.

7.6 Process SQL INSERT/UPDATE/DELETE Statement

The message with DpCommand header 'update' requests a SQL INSERT/UPDATE/DELETE operation. The
message body is the SQL statement. The concrete SQL statement type could be consulted via the SQL bean's
dmlType property. You can convert the SQL statement using SQL Bean. Note that the SQL statement could be a
prepared statement. If the SQL statement is a prepared statement, there will be a DpIsPreparedStatement header
with 'true' value. And the values of the prepared statement will be represent as a map and set to the
DpPreparedStatementValues header. The SQL statement must be processed by configuring Camel processors
and/or endpoints. Before it flows into the 'dpresult:dpresult' endpoint, you must specify how many rows are
inserted/updated/deleted via the DpUpdateCount header.

<when>
<!-- An update command, that is, the in-message body is a SQL INSERT/UPDATE/
DELETE statement. -->
<simple>${header.DpCommand} == 'update'</simple>
<!-- Convert the SELECT statement to a structural SQL bean. -->
<bean ref="sqlBean" method="toSQLBean"/>
<!-- Process the update command. The update command is an INSERT/UPDATE/DELETE
statement.
You can use SQLBean's dmlType property to determine the concrete SQL statement
type. E.g.
<choice>
<when>
<simple>${body.dmlType} == 'INSERT'</simple>
... </when>

Data Provisioning Adapter SDK Guide


Building an Adapter Using Camel Adapter PUBLIC 59
<when>
<simple>${body.dmlType} == 'UPDATE'</simple>
... </when>
<when>
<simple>${body.dmlType} == 'DELETE'</simple>
... </when>
<otherwise>
...
</otherwise>
</choice>
The result must be an integer that indicates how many rows are updated.
It is set as the header 'DpUpdateCount'. E.g.
<setHeader headerName="DpUpdateCount">1</setHeader>
-->
<to uri="dpresult:dpresult"/>
</when>

7.7 Dependent JAR Files

Once you complete the route template configuration, you need to determine which dependent jar files are needed
at runtime. These jars are mainly Camel component jars and their dependencies. They must be put into
<DPAgent_root>/camel/lib directory to be accessible to Camel Adapter. Before putting your dependent jars
into the lib directory, make sure that they are not provided by Data Provisioning Agent in <DPAgent_root>/
plugins directory. If any jars already exist in <DPAgent_root>/plugins directory, you don't need to put them
into <DPAgent_root>/camel/lib directory.

Data Provisioning Adapter SDK Guide


60 PUBLIC Building an Adapter Using Camel Adapter
8 Debugging

To set up debugging for a custom adapter, first import the launch configuration, then set up the debug
configuration with the appropriate bundles.

Then you can test your adapter by adding break points and running the adapter.

Related Information

Import the Launch Configuration [page 61]


Set up the Debug Configuration [page 62]
Register the Debugging Agent and Adapters in SAP HANA [page 62]

8.1 Import the Launch Configuration

To enable debugging for a custom adapter, first import the Data Provisioning Agent launch configuration.

Procedure

1. Import the agent launch configuration by selecting File Import Run/Debug Launch Configurations .
2. Click Next and browse to the Data Provisioning Agent installation and open the ui folder (for example
<DPAgent_root>\ui).
3. Select the ui folder and click OK.
4. In the Import Launch Configurations window, select the check box for the doc folder, and in the right pane
select the check box for AgentConfig.launch.
5. Click Finish.

Results

The configuration is available under the OSGi Framework node (in SAP HANA studio, from the Debug Agent Config
menu, select Debug Configurations).

Data Provisioning Adapter SDK Guide


Debugging PUBLIC 61
8.2 Set up the Debug Configuration

To debug a custom adapter, after importing the launch configuration, set up the debug configuration.

Procedure

1. Right-click the project and select Debug As Debug Configurations .


2. Click Add Required Bundles.
3. Click Validate Bundles.
4. In the Bundles list, verify the following bundles are selected:

○ com.sap.hana.dp.agent
○ com.sap.hana.dp.adapterframework
○ com.sap.hana.dp.log4jfragment
5. On the Arguments tab, verify the following arguments are present:

○ Program arguments:
-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -
consoleLog –console

○ VM arguments:
Declipse.ignoreApp=true -Dosgi.noShutdown=true

Next Steps

Once the adapter is running inside the OSGi console, next register the debug agent and adapters in SAP HANA.

8.3 Register the Debugging Agent and Adapters in SAP


HANA

Data Provisioning Agents and adapters must be registered before they can be used.

Context

In a production environment, administrators use the Data Provisioning Agent Configuration tool. However, in a
development environment, you typically don't have access to this tool. Therefore, you can register the agent and
its adapters in one of two ways:

Data Provisioning Adapter SDK Guide


62 PUBLIC Debugging
● In the OSGI console, enter the following command.

dplogin <hana_host_name> <hana_port> <user_name> <password>

If not already registered, this command registers the agent (invokes the CREATE AGENT SQL statement on
SAP HANA) and any installed adapters (invokes the CREATE ADAPTER SQL statement on SAP HANA).
● Individually invoke the following SQL statements:

CREATE ADAPTER <adapter_name> AT LOCATION AGENT <agent_name>

CREATE AGENT <agent_name> PROTOCOL [‘TCP’ | ‘HTTP’] HOST <host_name> PORT


<port_number>

Note
Host and port are only applicable if the protocol is TCP.

Data Provisioning Adapter SDK Guide


Debugging PUBLIC 63
9 Deploying a Custom Adapter

After developing and debugging a custom adapter, you then deploy it to the Data Provisioning Agent and register it
with SAP HANA.

The process for deploying a custom adapter for SAP HANA is as follows:

1. Export the plug-in project as .jar.


2. Deploy the adapter (.jar) to the Data Provisioning Agent using the Data Provisioning Agent Configuration
tool.
3. Register the adapter with SAP HANA using the Data Provisioning Agent Configuration tool.
4. Enable the adapter in SAP HANA studio (create a new remote source and import tables).

Related Information

Export the Adapter as a Deployable Plug-in [page 64]


Deploy and Register the Custom Adapter [page 65]
Use the Custom Adapter in SAP HANA Studio [page 66]

9.1 Export the Adapter as a Deployable Plug-in

To make a custom adapter available to SAP HANA, first export it as a deployable plug-in.

Procedure

1. In SAP HANA studio, ensure the Eclipse Plug-in Development Environment is installed. If not, install it as
follows:

a. Select Help Install New Software .


b. Select a site and expand General Purpose Tools.
c. Select Eclipse Plug-in Development Environment and click Next twice.
d. Review and accept the license agreements and click Finish to install.
2. In the Project Explorer view, right-click the project and select Export.
3. Expand Plug-in Development, select Deployable plug-ins and fragments, and click Next.
4. Select the plug-in and browse to the directory where the plug-in .jar file will be generated.
5. Click Finish.

Data Provisioning Adapter SDK Guide


64 PUBLIC Deploying a Custom Adapter
Next Steps

Using the Data Provisioning Agent Configuration tool, deploy the custom adapter (.jar) to the Data Provisioning
Agent, and register the adapter with the SAP HANA server.

9.2 Deploy and Register the Custom Adapter

After developing a custom adapter, deploy it to the Data Provisioning Agent and register it with the SAP HANA
server.

Context

Refer to the Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality for
details on the following steps.

Procedure

1. Deploy the adapter (.jar) to the Data Provisioning Agent.


2. Register the adapter with the SAP HANA server.

Next Steps

Enable the adapter in SAP HANA studio (create a new remote source and import tables).

Data Provisioning Adapter SDK Guide


Deploying a Custom Adapter PUBLIC 65
9.3 Use the Custom Adapter in SAP HANA Studio

To use a custom adapter in SAP HANA studio, you create a new remote source, select the adapter and agent, and
configure the connection.

Context

To add a remote data source:

Procedure

1. In the Systems view, open Provisioning Remote Sources .


2. Right-click Remote Sources and select New Remote Source.
3. Enter a Source Name.
4. Select the Adapter Name from the drop-down list.
5. Select the appropriate agent.
6. Enter the values for specified properties.
7. Click the Save this editor icon in the upper right-hand corner of the window.
8. Click the Test connection icon in the upper right-hand corner to verify the entered data is correct.

Next Steps

Import tables per the SAP HANA Administration Guide: 6.1.1.2 Creating Virtual Tables from Remote Objects.

Data Provisioning Adapter SDK Guide


66 PUBLIC Deploying a Custom Adapter
10 Additional Information

This section includes supplementary information regarding creating custom adapters for SAP HANA.

Related Information

Data Types [page 67]


Trace Logging [page 68]
Exception Handling [page 68]
AdapterFactory Class [page 68]

10.1 Data Types

Remote source data types map to the following custom adapter data types.

Also refer to the Data Types section of the SAP HANA SQL and System Views Reference for more information on
how these data types map to your sources.

Remote source data type Adapter SDK data types

Numeric types TINYINT, SMALLINT, INTEGER, BIGINT, DECIMAL, REAL, DOUBLE

Character string types VARCHAR, NVARCHAR, ALPHANUM

Datetime types DATE, TIME, TIMESTAMP, SECONDDATE

Binary types VARBINARY

Large object types CLOB, NCLOB, BLOB

Boolean type BOOLEAN

Related Information

SAP HANA SQL and System Views Reference

Data Provisioning Adapter SDK Guide


Additional Information PUBLIC 67
10.2 Trace Logging

To use the logger in an adapter, use the log4j log manager.

Java adapters use log4j. The logs are in the OSGI console and framework.log file. The log file is located in the log
folder relative to the Data Provisioning Agent executable.

public static Logger logger = LogManager.getLogger("TestAdapter");

To log use:

logger.info(“Started”);

You can configure the log level using the framework.log.level property in dpagentconfig.ini. Note this will
require a restart of the Data Provisioning Agent.

framework.log.level=ALL

10.3 Exception Handling

Java provides the AdapterException class that lets you throw an exception in case of failure. The
AdapterException is a checked exception, which allows you to throw the exception with just text or with text and
ID. When the ID is provided in the exception, it will be sent as a string to the server.

The AdapterException also provides a needReconnect option that is useful when you lose the connection to
the source system and you want the server to call open again. In that scenario, you can throw an
AdapterException with this flag set to true, then the server will clean up the old session and call open again.

10.4 AdapterFactory Class

If the adapter requires parameters during deployment, the methods getAdapterConfig and potentially the
validateAdapterConfig methods of the AdapterFactory class can be used to define and later validate the
list of parameters to be entered.

Example

@Override
public RemoteSourceDescription getAdapterConfig() throws AdapterException {
PropertyGroup ui = new PropertyGroup("UI");
PropertyGroup connectionProperties = new
PropertyGroup("config","FileAdapter Configuration");
PropertyEntry root = new PropertyEntry(FileAdapter.ROOTDIR, "Root
Directory", "The absolute root directory, no file can be read from outside");

Data Provisioning Adapter SDK Guide


68 PUBLIC Additional Information
PropertyEntry fileformat = new PropertyEntry(FileAdapter.FILEFORMATDIR,
"File Format Root Directory", "All file formats are loceated here or in a sub
directory");
connectionProperties.addProperty(root);
connectionProperties.addProperty(fileformat);
ui.addProperty(connectionProperties);

CredentialProperties credentialProperties = new CredentialProperties();


CredentialEntry passwordOnly = new CredentialEntry(HASHED_PASSWORD,
"Access Token Credential", true);
passwordOnly.getPassword().setDisplayName("AccessToken");
credentialProperties.addCredentialEntry(passwordOnly);

RemoteSourceDescription rsd = new RemoteSourceDescription();


rsd.setConnectionProperties(ui);
rsd.setCredentialProperties(credentialProperties);
return rsd;
}
@Override
public boolean validateAdapterConfig(RemoteSourceDescription rsd)
throws AdapterException {
CredentialProperties credentialProperties = rsd.getCredentialProperties();
if(credentialProperties.hasCredentialEntry(HASHED_PASSWORD)){
CredentialEntry credential =
credentialProperties.getCredentialEntry(HASHED_PASSWORD);
try {
String token = new
String(credential.getPassword().getValue(),"UTF-8");
if(token.isEmpty() || token.isEmpty())
throw new AdapterException("AccessToken can not be
blank");
} catch (UnsupportedEncodingException e) {
throw new AdapterException(e.getMessage());
}
}
return true;
}

Data Provisioning Adapter SDK Guide


Additional Information PUBLIC 69
Important Disclaimers and Legal Information

Hyperlinks
Some links are classified by an icon and/or a mouseover text. These links provide additional information.
About the icons:

● Links with the icon : You are entering a Web site that is not hosted by SAP. By using such links, you agree (unless expressly stated otherwise in your agreements
with SAP) to this:

● The content of the linked-to site is not SAP documentation. You may not infer any product claims against SAP based on this information.
● SAP does not agree or disagree with the content on the linked-to site, nor does SAP warrant the availability and correctness. SAP shall not be liable for any
damages caused by the use of such content unless damages have been caused by SAP's gross negligence or willful misconduct.

● Links with the icon : You are leaving the documentation for that particular SAP product or service and are entering a SAP-hosted Web site. By using such links,
you agree that (unless expressly stated otherwise in your agreements with SAP) you may not infer any product claims against SAP based on this information.

Beta and Other Experimental Features


Experimental features are not part of the officially delivered scope that SAP guarantees for future releases. This means that experimental features may be changed by SAP
at any time for any reason without notice. Experimental features are not for productive use. You may not demonstrate, test, examine, evaluate or otherwise use the
experimental features in a live operating environment or with data that has not been sufficiently backed up.
The purpose of experimental features is to get feedback early on, allowing customers and partners to influence the future product accordingly. By providing your feedback
(e.g. in the SAP Community), you accept that intellectual property rights of the contributions or derivative works shall remain the exclusive property of SAP.

Example Code
Any software coding and/or code snippets are examples. They are not for productive use. The example code is only intended to better explain and visualize the syntax and
phrasing rules. SAP does not warrant the correctness and completeness of the example code. SAP shall not be liable for errors or damages caused by the use of example
code unless damages have been caused by SAP's gross negligence or willful misconduct.

Gender-Related Language
We try not to use gender-specific word forms and formulations. As appropriate for context and readability, SAP may use masculine word forms to refer to all genders.

Data Provisioning Adapter SDK Guide


70 PUBLIC Important Disclaimers and Legal Information
Data Provisioning Adapter SDK Guide
Important Disclaimers and Legal Information PUBLIC 71
go.sap.com/registration/
contact.html

© 2018 SAP SE or an SAP affiliate company. All rights reserved.


No part of this publication may be reproduced or transmitted in any
form or for any purpose without the express permission of SAP SE
or an SAP affiliate company. The information contained herein may
be changed without prior notice.
Some software products marketed by SAP SE and its distributors
contain proprietary software components of other software vendors.
National product specifications may vary.
These materials are provided by SAP SE or an SAP affiliate company
for informational purposes only, without representation or warranty
of any kind, and SAP or its affiliated companies shall not be liable for
errors or omissions with respect to the materials. The only
warranties for SAP or SAP affiliate company products and services
are those that are set forth in the express warranty statements
accompanying such products and services, if any. Nothing herein
should be construed as constituting an additional warranty.
SAP and other SAP products and services mentioned herein as well
as their respective logos are trademarks or registered trademarks of
SAP SE (or an SAP affiliate company) in Germany and other
countries. All other product and service names mentioned are the
trademarks of their respective companies.
Please see https://www.sap.com/about/legal/trademark.html for
additional trademark information and notices.

You might also like