Experiences with SAP Gateway

"My foots always in my mouth i just can't stomach defeet" – Hilltop Hoods

Posts Tagged ‘SAP

SAP Netweaver Gateway Java Library

leave a comment »

Just finished building my first Android app with the SAP Netweaver Gateway Java Library.  I really like this library it compliments Android nicely.

The generated proxies have a lot of potential, they are easy to use and very flexible. With future releases I hope we get to see a lot more examples, especially around the power of SAP annotations and semantics.

For those interested the app was built with the help of Android In Action http://www.manning.com/ableson3/, by far the best Android book I have read, nicely paced, it pulls no punches and assumes you know what your doing and that you are ready to learn more.

Advertisements

Written by rsol1

February 10, 2012 at 1:41 pm

Posted in Uncategorized

Tagged with , , , ,

Filter String How To

leave a comment »

Today I was asked during a demo to share how I use IV_FILTER_STRING and get round the Filter String Gotcha.

To start with the following code is not optimized and should be used for reference purposes only, for example it uses string values, not types and the select should use an index key select first before getting results.

I use a Paging Query Provider (see image below), it has a method SET_FILTER_STR which converts the provided IV_FILTER_STRING into a table of Filter String Select Options, these values are then passed to a method SET_WHERE_FILTER which is used to assign filter select options to one of 10 available range tables.

The range tables are then used to create a WHERE clause for a generic SQL SELECT statement which looks similar to below.

SELECT ( m_fields )
UP TO ( m_up_to )
INTO  TABLE et_results
FROM ( m_table )
WHERE ( m_where_str )
ORDER BY ( m_order_str )

The Code for the filter string conversion looks something like –

* <SIGNATURE>---------------------------------------------------------------------------------------+
 * | Instance Public Method ZCL_GWPAGINGQUERYPROVIDER->ZIF_GWPAGINGQUERY~SET_FILTER_STR
 * +-------------------------------------------------------------------------------------------------+
 * | [--->] IV_FILTER_STRING               TYPE        STRING
 * +--------------------------------------------------------------------------------------</SIGNATURE>
 METHOD zif_gwpagingquery~set_filter_str.

   DATA:
     lt_filter_select_options TYPE /iwbep/t_mgw_select_option,
     lt_filter_string TYPE TABLE OF string,
     lt_key_value TYPE /iwbep/t_mgw_name_value_pair,
     ls_filter_string TYPE string,
     lv_input TYPE string,
     lv_name TYPE string,
     lv_value TYPE string.

   CONSTANTS:
     co_substringof TYPE string VALUE 'substringof(',
     co_startswith TYPE string VALUE 'startswith(',
     co_endswith TYPE string VALUE 'endswith('.

   FIELD-SYMBOLS:
     <fs_range_tab> LIKE LINE OF lt_filter_select_options ,
     <fs_select_option> TYPE /iwbep/s_cod_select_option,
     <fs_key_value> LIKE LINE OF lt_key_value.

   lv_input = iv_filter_string.

 *--- get rid of ) & ' and make AND's uppercase
   REPLACE ALL OCCURRENCES OF ')' IN lv_input WITH ''.
   REPLACE ALL OCCURRENCES OF `'` IN lv_input WITH ''.
   REPLACE ALL OCCURRENCES OF 'and' IN lv_input WITH 'AND'.
   REPLACE ALL OCCURRENCES OF 'eq' IN lv_input WITH 'EQ'.
   SPLIT lv_input AT 'AND' INTO TABLE lt_filter_string.

 *--- build a table of key value pairs based on filter string
   LOOP AT lt_filter_string INTO ls_filter_string.
     APPEND INITIAL LINE TO lt_key_value ASSIGNING <fs_key_value>.

     IF ls_filter_string CS co_substringof.
       ls_filter_string = substring_after( val = ls_filter_string sub = co_substringof ).
       CONDENSE ls_filter_string.
       SPLIT ls_filter_string AT ',' INTO lv_value lv_name.
       <fs_key_value>-value = |*{ lv_value }*|.
     ELSEIF ls_filter_string CS co_startswith.
       ls_filter_string = substring_after( val = ls_filter_string sub = co_startswith ).
       CONDENSE ls_filter_string.
       SPLIT ls_filter_string AT ',' INTO lv_name lv_value.
       <fs_key_value>-value = |{ lv_value }*|.
     ELSEIF ls_filter_string CS co_endswith.
       ls_filter_string = substring_after( val = ls_filter_string sub = co_endswith ).
       CONDENSE ls_filter_string.
       SPLIT ls_filter_string AT ',' INTO lv_name lv_value.
       <fs_key_value>-value = |*{ lv_value }|.
     ELSE.
       CONDENSE ls_filter_string.
       SPLIT ls_filter_string AT ' EQ ' INTO lv_name lv_value.
       <fs_key_value>-value = |{ lv_value }|.
     ENDIF.
     <fs_key_value>-name = to_upper( lv_name ).
    ENDLOOP.

 *--- add key value pairs to filter select options
   LOOP AT lt_key_value ASSIGNING <fs_key_value>.
     APPEND INITIAL LINE TO lt_filter_select_options ASSIGNING <fs_range_tab>.
     <fs_range_tab>-property = <fs_key_value>-name.
     APPEND INITIAL LINE TO <fs_range_tab>-select_options ASSIGNING <fs_select_option>.
     <fs_select_option>-sign = 'I'.
     IF <fs_key_value>-value CS '*'.
       <fs_select_option>-option = 'CP'.
     ELSE.
       <fs_select_option>-option = 'EQ'.
     ENDIF.
     <fs_select_option>-low = <fs_key_value>-value.
   ENDLOOP.
 *--- call method that create where string for filter select options
   me->set_where_filter( lt_filter_select_options ).
 ENDMETHOD.
.
.
 * <SIGNATURE>---------------------------------------------------------------------------------------+
 * | Instance Public Method ZCL_GWPAGINGQUERYPROVIDER->ZIF_GWPAGINGQUERY~SET_WHERE_FILTER
 * +-------------------------------------------------------------------------------------------------+
 * | [--->] IT_FILTER_SELECT_OPTIONS       TYPE        /IWBEP/T_MGW_SELECT_OPTION
 * +--------------------------------------------------------------------------------------</SIGNATURE>
 METHOD zif_gwpagingquery~set_where_filter.

   DATA:
     lv_property TYPE string,
     lv_range_name TYPE string.

   FIELD-SYMBOLS:
     <fs_filter_select_option> LIKE LINE OF it_filter_select_options,
     <fs_range> TYPE /iwbep/t_cod_select_options.

   LOOP AT it_filter_select_options ASSIGNING <fs_filter_select_option>.
     ADD 1 TO m_range_tabix. "Assumption max 10 filters
     lv_range_name = |MRA_RANGE{ m_range_tabix }|.

     ASSIGN (lv_range_name) TO <fs_range>.
     <fs_range> = <fs_filter_select_option>-select_options.

     lv_property = to_upper(<fs_filter_select_option>-property).
     "build where string
     IF m_where_str IS NOT INITIAL.
       m_where_str = |{ m_where_str } AND |.
     ENDIF.

     m_where_str = |{ m_where_str }{ lv_property } IN { lv_range_name }|.

   ENDLOOP.
 ENDMETHOD.

At runtime.

The result.

Written by rsol1

January 25, 2012 at 2:35 pm

Posted in Uncategorized

Tagged with , , , , ,

Back it up!

leave a comment »

Spent the last 3 days rebuilding my SAP NetWeaver Gateway Pre-Packaged Trial Version for Linux, could have easily been avoided.

Thought i would share a couple of things I had to do post installation.
Update SAP Profile Parameters
– set FQDN
– increased ABAP/BUFFERSIZE
– increased abap/timeout
– increased rdisp/max_wprun_time
– SSL parameters
TODO set SICF time outs optimized for debugging

Found the following blogs useful for the SSL
Setup HTTPS for the SAP NetWeaver Testdrive SR1 on Linux
he Full Monty – Part 5 – HTTPS & Sapcrypto
note: had to enable STRUSTSSO2 before activating SSL service in SMICM

I used WinSCP to edit and FTP files, find this quicker

Ran SGEN (Generate All Objects)
– 65000 objects takes approximately 6 hours

Setup a static ip address – found the following blog very useful
HowTo Configure a network card in Suse/openSUSE 11.x for LAN and Internet Access

Recreated the Gateway services
– backed up everything to SAPLink nuggets, including all the mimes 🙂

Started to use external storage to back up the VM

Written by rsol1

January 24, 2012 at 1:05 pm

Posted in Uncategorized

Tagged with , , ,

Addressing with key

with one comment

This one is a pretty big gotcha and it is hard to explain so bear with me.

KeyPredicate: A predicate that identifies the value(s) of the key Properties of an Entry. If the Entry has a single key Property the predicate may include only the value of the key Property. If the key is made up of two or more Properties, then its value must be stated using name/value pairs. More precisely, the syntax for a KeyPredicate is shown by the following figure.


[Source: OData: URI Conventions: 3.1. Addressing Entries]

Examples of using key and value pairs
http://services.odata.org/Northwind/Northwind.svc/Suppliers(1)
http://services.odata.org/Northwind/Northwind.svc/Suppliers(SupplierID=1)

How the call is structured is very much in the hands of the client API and in my experience they tend to use the KEY when its available.

When you address entries using the KEY in the SAP Netweaver Gateway 2.0 Trial Version it is important to note that the table IT_KEY_TAB leaves out the NAME.

This means we have to do something like the following to make IT_KEY_TAB usable in a SQL statement

lt_key_tab = it_key_tab.
lv_name = 'SUPPLIERID'.
LOOP AT lt_key_tab ASSIGNING <fs_key_tab>.
  CHECK <fs_key_tab>-name EQ space.
  <fs_key_tab>-name = lv_name.
ENDLOOP.

Which allows us to make the following call

SELECT * FROM SUPPLIER WHERE SUPPLIERID EQ 1

The trouble is the NAME is not populated when calling from an association either

eg.
http://services.odata.org/Northwind/Northwind.svc/Suppliers(1)/Products

In this case the SUPPLIER entity DAO is not called, the PRODUCT Entity GET_ENTITYSET method is and the IT_KEY_TAB has a missing NAME value

We have to look at the IV_SOURCE_NAME to derive the source entity key for our SQL call

CASE iv_source_name.
 WHEN 'Category'.
   lv_name = 'CATEGORYID'.
 WHEN 'Product'.
  lv_name = 'PRODUCTID'.
 WHEN 'Supplier'.
  lv_name = 'SUPPLIERID'.
ENDCASE.
LOOP AT lt_key_tab ASSIGNING <fs_key_tab>.
 CHECK <fs_key_tab>-name EQ space.
 <fs_key_tab>-name = lv_name.
ENDLOOP.

Which will hopefully end with

SELECT * FROM PRODUCT WHERE SUPPLIERID EQ 1

The following happens when you call associated KEYS

eg.

http://services.odata.org/Northwind/Northwind.svc/Suppliers(1)/Products(2)

In the GET_ENTITY method of the PRODUCT data provider, the IT_KEY_TAB has a single entry again with a missing NAME which relates to the the SOURCE entity, which in the scenario above is SUPPLIER.

In this case we can get the PRODUCT KEY from IT_NAVIGATION_PATH-KEY

or IT_NAVIGATION_PATH-KEY_TAB (or by calling the source entity)

 
IF iv_source_name EQ 'Product'.
    READ TABLE it_key_tab INTO ls_key_tab INDEX 1.
 ELSE.
    READ TABLE it_navigation_path ASSIGNING <fs_navigation_path> INDEX 1.
    IF <fs_navigation_path>-key IS NOT INITIAL. "1:1
       ls_key_tab-value =  <fs_navigation_path>-key.
     ELSE.
       IF iv_source_name = '<SourceEntity>'. "N:1
          DATA: lo_SourceEntity TYPE REF TO zcl_SourceEntity,
          ls_SourceEntity TYPE zcl_service_model=>ts_SourceEntity.
          " go to the source and get the key
          CREATE OBJECT lo_SourceEntity.
          lo_SourceEntity->get_entity( EXPORTING iv_entity_set_name = iv_entity_set_name
                                                 iv_source_name = iv_source_name
                                                 it_key_tab = it_key_tab
                                                 it_navigation_path = it_navigation_path
                                       IMPORTING es_SourceEntity = ls_SourceEntity ).
          ls_key_tab-value = ls_SourceEntity-productid.
        ENDIF.
     ENDIF.
ENDIF
* UPDATED code for N:1 where i call the source entity to get key

I have 3 completely different working solutions for this catering for different scenarios. <Still work needed to re-factor to one generic>
SELECT SINGLE * FROM PRODUCT WHERE PRODUCTID EQ 2

And you end up with

Written by rsol1

January 17, 2012 at 2:08 pm

Posted in Uncategorized

Tagged with , , ,

Filter String Gotcha

with one comment

.. the Logical Operators ‘eq’,’ne’,’le’,’lt’,’ge’,’gt’ and the String functions ‘substringof’, ‘startswith’, ‘endswith’ are supported currently.
[SOURCE : SAP Note 1574568 – SAP NetWeaver Gateway 2.0 – Known Constraints ]

On their own both the Logical Operators and String Functions work fine.

In Method GET_ENTITYSET the Logical Operator get appended to the IT_FILTER_SELECT_OPTIONS and the String function are put into IV_FILTER_STRING.

When you combine both Logical Operators and String functions

eg. /Suppliers?$filter=City eq ‘London’ and startswith(SupplierName,’A’)

The logical operators are not parsed into IT_FILTER_SELECT_OPTIONS  instead they are included in IV_FILTER_STRING

Written by rsol1

January 17, 2012 at 11:56 am

Posted in Uncategorized

Tagged with , , ,

Entityset Name issue in OData Browsers

leave a comment »

I have been testing the “OData Channel Generation Tool”

The OData Channel Generation Tool enables you to import an existing service description file and, from this, automatically generate implementations for data and metadata provisioning on the backend for exposure via SAP NetWeaver Gateway. [Source: SAP Netweaver Gateway: OData Channel Generation Tool |

I noticed for some of the services generated the collections didn’t work as expected in various OData Browsers.

For example in the OData Browser for some a runtime exception was thrown and in the Sesame Data Browser various collections were disabled.

 

Through trial and error I found for the EntitySet’s where a name was nominated different from the Entity, that name was not reflected in the Atom:Title which still had the generated <Entity>Collection convention.

eg.

Creating an EntitySet ‘Customers’ for the Entity Customer

lo_entity_type = model->get_entity_type( 'Customer' ).
lo_entity_set = lo_entity_type->create_entity_set( 'Customers' ).

<EntitySet Name="Customers" EntityType="ZNWGWSAMP.Customer" sap:content-version="1"/>
.
<app:collection href="CustomerCollection" sap:content-version="1">
 <atom:title>CustomerCollection</atom:title>
</app:collection>

EntitySet Name ≠atom:title turned out to be the cause of the OData Browser issues.

To manually set the EntitySet Name in the Model you set the member title (not the SAP member title)
* text-004 = |Customers|.
lo_entity_set->set_member_title( iv_text_key = 'zcl_nwind_model-004'
iv_text_obj_type  = /iwbep/cl_mgw_abs_model=>gcs_sap_text_object_types-text_symbol_class ).

Now the title and name are the same
<EntitySet Name="Customers" EntityType="ZNWGWSAMP.Customer" sap:content-version="1"/>
.
<app:collection href="Customers" sap:content-version="1">
 <atom:title>Customers</atom:title>
</app:collection>

And the OData Browsers are working as expected

  

The <atom:title> element, as specified in [RFC4287] section 4.2.14, MAY contain the name of the EntitySet…
[Source [MS-ODATA]: Open Data Protocol (OData) Specification – 2.2.6.2.1 Entity Set (as an Atom Feed Element)  ]

I think some of the LINQ and Silverlight libraries are enforcing this.

Written by rsol1

January 17, 2012 at 3:25 am

Posted in Uncategorized

Tagged with , , ,

Metadata cache cleanup

leave a comment »

The Gateway service metadata is cached, if you change your model you will need to do the following

1. Inside of your model change the GET_LAST_MODIFIED method,  that way when the metadata is loaded the application knows to cache the latest version.

* CONSTANTS: lv_timestamp TYPE timestamp VALUE 20120117015224 .
* rv_last_modified = lv_timestamp
GET TIME STAMP FIELD rv_last_modified.

2. Then run transaction /IWFND/CACHE_CLEANUP
choose “Cleanup Cache for all Models” and run

Written by rsol1

January 17, 2012 at 3:01 am

Posted in Uncategorized

Tagged with , , ,