PUBLIC
SAP HANA Smart Data Integration and SAP HANA Smart Data Quality 2.0 SP00
Document Version: 1.0 – 2016-12-07
Adapter SDK Guide
Content
1 Introduction..................................................................4
1.1 Prerequisites.................................................................. 4
1.2 Adapter Functionality............................................................ 5
1.3 Adapter Instances...............................................................6
1.4 Capabilities....................................................................6
Description.................................................................6
Three Levels of Capabilities......................................................8
Design Considerations.........................................................9
Capabilities Test Adapter.......................................................10
Key Capabilities..............................................................11
1.5 Process Overview.............................................................. 14
2 Install the Data Provisioning Feature...............................................15
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.......................................19
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 ........................................24
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..................................................32
Real-Time Changed Rows......................................................33
Row Types.................................................................35
Real-Time Large-Object Columns.................................................36
5.8 Enterprise Semantic Services API Support............................................ 36
IS_TREE...................................................................37
6 Building an Adapter by Extending the BaseAdapterClass...............................38
6.1 Reporting the Configuration of the Remote Source Description for BaseAdapterClass..............38
2
P U B L I C
Adapter SDK Guide
Content
6.2 Reporting the Capabilities of the Remote Source for BaseAdapterClass........................39
6.3 Beginning and Ending Communication with the Remote Source for BaseAdapterClass.............40
6.4 Browsing a Remote Source's Metadata for BaseAdapterClass...............................40
6.5 Preparing to Create Virtual Tables in SAP HANA for BaseAdapterClass.........................41
6.6 Executing Queries for BaseAdapterClass..............................................41
6.7 Real-Time Execution for BaseAdapterClass............................................43
6.8 Real-Time Changed Rows for BaseAdapterClass........................................44
7 Building an Adapter Using Camel Adapter...........................................45
7.1 Define Custom Adapter in adapters.xml...............................................45
Adapter type, display name, and description.........................................46
Remote Source Description.....................................................47
Adapter Capabilities..........................................................48
Camel Route Template........................................................49
7.2 Use Camel Route to Implement Adapter Functionalities...................................49
7.3 SDA Camel Route defined within <camelContext> tag.................................... 50
7.4 Process metadata browse and import................................................50
7.5 Process SQL SELECT statement....................................................51
7.6 Process SQL INSERT/UPDATE/DELETE statement......................................52
7.7 Dependent JAR files.............................................................53
8 Debugging...................................................................54
8.1 Import the Launch Configuration................................................... 54
8.2 Set up the Debug Configuration.................................................... 55
8.3 Register the Debugging Agent and Adapters in SAP HANA................................. 55
9 Deploying a Custom Adapter .................................................... 57
9.1 Export the Adapter as a Deployable Plug-in............................................57
9.2 Deploy and Register the Custom Adapter............................................. 58
9.3 Use the Custom Adapter in SAP HANA Studio..........................................59
10 Additional Information.........................................................60
10.1 Data Types...................................................................60
10.2 Trace Logging.................................................................60
10.3 Exception Handling............................................................. 61
10.4 AdapterFactory Class............................................................61
Adapter SDK Guide
Content
P U B L I C 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.
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
4
P U B L I C
Adapter SDK Guide
Introduction
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
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
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.
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
levels.
Realtime processing In addition to querying tables, adapters can provide the data in real time using a remote
subscription.
Adapter SDK Guide
Introduction
P U B L I C 5
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.
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.
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.
6
P U B L I C
Adapter SDK Guide
Introduction
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.
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
v_sqltable;
CAP_SELECT
CAP_EXPR_IN_PROJ
Adapter SDK Guide
Introduction
P U B L I C 7
SQL supported by the adapter Minimum required Capabilities
SELECT SUBSTR(sqltext,1,3) FROM
v_sqltable;
CAP_SELECT
CAP_EXPR_IN_PROJ
CAP_NESTED_FUNC_IN_PROJ
SELECT * FROM v_sqltable WHERE
rownumber = 3;
CAP_SELECT
CAP_SIMPLE_EXPR_IN_WHERE
SELECT * FROM v_sqltable WHERE
rownumber <> 3;
CAP_SELECT
CAP_SIMPLE_EXPR_IN_WHERE
CAP_NONEQUAL_COMPARISON
SELECT * FROM v_sqltable WHERE
rownumber > 3;
CAP_SELECT
CAP_SIMPLE_EXPR_IN_WHERE
CAP_NONEQUAL_COMPARISON
SELECT * FROM v_sqltable WHERE
rownumber > 3 and rownumber < 6;
CAP_SELECT
CAP_SIMPLE_EXPR_IN_WHERE
CAP_NONEQUAL_COMPARISON
CAP_AND
SELECT * FROM v_sqltable WHERE
rownumber > 3 and sqltext > 'Z';
CAP_SELECT
CAP_SIMPLE_EXPR_IN_WHERE
CAP_NONEQUAL_COMPARISON
CAP_AND_DIFFERENT_COLUMNS
SELECT rownumber, COUNT(*),
MIN(sqltext) FROM v_sqltable GROUP BY
rownumber;
CAP_GROUPBY
CAP_AGGREGATES, CAP_EXPR_IN_PROJ
SELECT rownumber, COUNT(DISTINCT
sqltext) FROM v_sqltable GROUP BY
rownumber;
CAP_GROUPBY
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.
8
P U B L I C
Adapter SDK Guide
Introduction
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.
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’)
Adapter SDK Guide
Introduction
P U B L I C 9
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.
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";
10
P U B L I C
Adapter SDK Guide
Introduction
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.
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
Adapter SDK Guide
Introduction
P U B L I C 11
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.
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 _
12
P U B L I C
Adapter SDK Guide
Introduction
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_*
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
Adapter SDK Guide
Introduction
P U B L I C 13
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
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
Agent
Administrator Data Provisioning Agent installer Administration Guide
2 Install Data Provisioning fea
ture
Developer SAP HANA studio Adapter SDK Guide
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
ble plug-in
Developer SAP HANA studio Adapter SDK Guide
7 Deploy and register adapter
with SAP HANA
Administrator Data Provisioning Agent Configuration tool Administration Guide
8 Enable adapter with a new re
mote source connection
Administrator SAP HANA studio Adapter SDK Guide
Related Information
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
14
P U B L I C
Adapter SDK Guide
Introduction
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.
Adapter SDK Guide
Install the Data Provisioning Feature
P U B L I C 15
Related Information
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
16 P U B L I C
Adapter SDK Guide
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
Adapter SDK Guide
Create a Custom Adapter Using a New Plug-in Project
P U B L I C 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.
18
P U B L I C
Adapter SDK Guide
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.
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");
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 19
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.
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;
}
20
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
@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>();
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]
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 21
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) {
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.
22
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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;
}
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
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 23
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;
}
}
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.
24
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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.
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);
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 25
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>
]]></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="">
.
.
26
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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();
} 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.
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 27
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.
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
28
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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.
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
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 29
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
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
}
30
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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.
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);
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 31
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());
}
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,
32
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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);
} 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
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 33
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());
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
}
}
}
}
}
34
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
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.
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.
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 35
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
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.
SAP HANA includes the following built-in procedures:
GET_REMOTE_SOURCE_OBJECTS_LIST
GET_REMOTE_SOURCE_TABLE_DEFINITIONS
GET_REMOTE_SOURCE_TABLE_ESS_DEFINITIONS
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), a
user can easily 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
36
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
procedures enable querying a remote source to retrieve all table metadata subset elements at once rather than
having to import each one as a virtual object.
5.8.1 IS_TREE
The IS_TREE column in the REMOTE_SOURCES public 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;
}
Adapter SDK Guide
Building an Adapter by Extending the AdapterCDC
P U B L I C 37
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…..
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.
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?"));
38
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
}
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.
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)
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
P U B L I C 39
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
}
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.
40
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
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);
}
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.
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
P U B L I C 41
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:
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.
42
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
// 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.
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.
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
P U B L I C 43
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();
}
}
@Override
public void pollEnd() throws AdapterException {
}
44
P U B L I C
Adapter SDK Guide
Building an Adapter by Extending the BaseAdapterClass
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.
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
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"
Adapter SDK Guide
Building an Adapter Using Camel Adapter
P U B L I C 45
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
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.
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";
}
}
46
P U B L I C
Adapter SDK Guide
Building an Adapter Using Camel 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
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
Adapter SDK Guide
Building an Adapter Using Camel Adapter
P U B L I C 47
PropertyEntry XML Attributes SDK PropertyEntry Properties
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 en
try.
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.
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;
}
48
P U B L I C
Adapter SDK Guide
Building an Adapter Using Camel Adapter
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 49]
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 $
{<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"
Adapter SDK Guide
Building an Adapter Using Camel Adapter
P U B L I C 49
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.
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>
50
P U B L I C
Adapter SDK Guide
Building an Adapter Using Camel Adapter
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.
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.
Adapter SDK Guide
Building an Adapter Using Camel Adapter
P U B L I C 51
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>
<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>
52
P U B L I C
Adapter SDK Guide
Building an Adapter Using Camel Adapter
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.
Adapter SDK Guide
Building an Adapter Using Camel Adapter
P U B L I C 53
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.
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).
54
P U B L I C
Adapter SDK Guide
Debugging
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:
Adapter SDK Guide
Debugging
P U B L I C 55
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.
56
P U B L I C
Adapter SDK Guide
Debugging
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
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
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.
Adapter SDK Guide
Deploying a Custom Adapter
P U B L I C 57
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.
Related Information
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
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).
Related Information
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
58
P U B L I C
Adapter SDK Guide
Deploying a Custom Adapter
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.
Related Information
Administration Guide for SAP HANA Smart Data Integration and SAP HANA Smart Data Quality (PDF)
SAP HANA Administration Guide (HTML)
SAP HANA Administration Guide (PDF)
Adapter SDK Guide
Deploying a Custom Adapter
P U B L I C 59
10 Additional Information
This section includes supplementary information regarding creating custom adapters for SAP HANA.
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
Related Information
SAP HANA SQL and System Views Reference (HTML)
SAP HANA SQL and System Views Reference (PDF)
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”);
60
P U B L I C
Adapter SDK Guide
Additional Information
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");
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;
}
Adapter SDK Guide
Additional Information
P U B L I C 61
@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;
}
62 P U B L I C
Adapter SDK Guide
Additional Information
Important Disclaimers and Legal Information
Coding Samples
Any software coding and/or code lines / strings ("Code") included in this documentation are only examples and are not intended to be used in a productive system
environment. The Code is only intended to better explain and visualize the syntax and phrasing rules of certain coding. SAP does not warrant the correctness and
completeness of the Code given herein, and SAP shall not be liable for errors or damages caused by the usage of the Code, unless damages were caused by SAP
intentionally or by SAP's gross negligence.
Accessibility
The information contained in the SAP documentation represents SAP's current view of accessibility criteria as of the date of publication; it is in no way intended to be a
binding guideline on how to ensure accessibility of software products. SAP in particular disclaims any liability in relation to this document. This disclaimer, however, does
not apply in cases of willful misconduct or gross negligence of SAP. Furthermore, this document does not result in any direct or indirect contractual obligations of SAP.
Gender-Neutral Language
As far as possible, SAP documentation is gender neutral. Depending on the context, the reader is addressed directly with "you", or a gender-neutral noun (such as "sales
person" or "working days") is used. If when referring to members of both sexes, however, the third-person singular cannot be avoided or a gender-neutral noun does not
exist, SAP reserves the right to use the masculine form of the noun and pronoun. This is to ensure that the documentation remains comprehensible.
Internet Hyperlinks
The SAP documentation may contain hyperlinks to the Internet. These hyperlinks are intended to serve as a hint about where to find related information. SAP does not
warrant the availability and correctness of this related information or the ability of this information to serve a particular purpose. SAP shall not be liable for any damages
caused by the use of related information unless damages have been caused by SAP's gross negligence or willful misconduct. All links are categorized for transparency
(see: http://help.sap.com/disclaimer).
Adapter SDK Guide
Important Disclaimers and Legal Information
P U B L I C 63
go.sap.com/registration/
contact.html
© 2016 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 http://www.sap.com/corporate-en/legal/copyright/
index.epx for additional trademark information and notices.