Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

DMS documents via HTTP service

Hi everyone,

A few weeks ago, I was looking for ways to get documents (pictures, PDF files) from SAP DMS to my iPhone. This might be possible via a direct connection between the content server and the iPhone. However, I didn't like exposing the content server in this way and was hoping to use some kind of Internet service in the R/3 system.

It turned out that this was quite easy to do with a custom REST service. It came with a nice bonus; I can also use the service in my browser and it will start the appropiate program (Acrobat Reader, Powerpoint, ...) as well. The last part depends on the correct configuration of DMS customizing, I will show you at the end of the blog how to do that.


The requirement

The requirement was quite simple. I want an URL, similar to the RFC via SOAP URLs, to which I can pass an HTTP request with the key of the DMS document. I want response to contain the (binary) content of the document. Documents in DMS can be identified uniquely using the four key fields:

  1. Type (DRAW-DOKAR)
  2. Number (DRAW-DOKNR)
  3. Version (DRAW-DOKVR)
  4. Part (DRAW-DOKTL)

The simplest way of passing the document key is via an HTTP GET call, where the parameters are appended to the URL. So if the service URL is http://<sapserver>:<port>/sap/bc/zdoc, the GET call is http://<sapserver>:<port>/sap/bc/zdoc?type=<type>&number=<number>&version=<version>&part=<part>


Creating the service

You can create custom HTTP services in transaction SICF. In the selection screen, choose Execute (F8) and the following screen will appear:

Within the tree, you will find some frequently used services. For example, BSP pages can be found under path default_host/sap/bc/bsp/sap/, while WebDynpros reside under default_host/sap/bc/webdynpro/sap/. The path in the tree corresponds to the resulting URL of the service.

To create a new service, right-click on its parent and choose New Subelement from the context menu.

Dismiss the popup with a warning that SAP upgrades might overwrite the new service. A new popup appears where you must provide the name of the service:

After you choose Input (Enter), a screen appears where you can enter more information. At this moment, nothing is needed but you can enter a simple description. Choose Store (Ctrl + S) afterwards; because a service is a repository object you must either declare it as local or put it in a transport request.

When you go back, you can see the service in the tree. It is still gray because it isn't active yet.


Creating a handler

The next step is to create ABAP code which handles a call to the zdoc service. For this, we need a class which implements the IF_HTTP_EXTENSION interface. Start transaction SE24, enter a class name and choose Create (F5).

In the next popup, enter a description and choose Save (Enter). Like with the service, a popup will appear for choosing a transport request.

On the tabpage Interfaces, type IF_HTTP_EXTENSION on the first line and press Enter.

If you go to tabpage Methods, you will see the interface method HANDLE_REQUEST. Double-click it, and confirm the popup that you want to save the class.

Replace the method with the following code:

METHOD if_http_extension~handle_request.

  DATA:
    lr_request      TYPE REF TO if_http_request,
    lr_response     TYPE REF TO if_http_response,
    lv_value        TYPE string,
    lv_data         TYPE xstring,
    ls_draw         TYPE draw,
    ls_checkout_def TYPE dms_checkout_def,
    ls_doc_file     TYPE dms_doc_file,
    ls_phio         TYPE dms_phio,
    ls_file         TYPE cvapi_doc_file,
    lt_files        TYPE TABLE OF cvapi_doc_file,
    lt_content      TYPE TABLE OF drao.
  FIELD-SYMBOLS <fs_content> TYPE drao.

* 1.
  lr_request  = server->request.
  lr_response = server->response.

  IF lr_request->get_method( ) EQ 'GET'.
*   2.
*   Retrieve document key
    lv_value = lr_request->get_form_field( 'type' ).
    ls_draw-dokar = lv_value.
    lv_value = lr_request->get_form_field( 'number' ).
    ls_draw-doknr = lv_value.
    lv_value = lr_request->get_form_field( 'version' ).
    ls_draw-dokvr = lv_value.
    lv_value = lr_request->get_form_field( 'part' ).
    ls_draw-doktl = lv_value.

*   3.
*   Check document existence and read details
    CALL FUNCTION 'CVAPI_DOC_GETDETAIL'
      EXPORTING
        pf_dokar  = ls_draw-dokar
        pf_doknr  = ls_draw-doknr
        pf_dokvr  = ls_draw-dokvr
        pf_doktl  = ls_draw-doktl
      IMPORTING
        psx_draw  = ls_draw
      TABLES
        pt_files  = lt_files
      EXCEPTIONS
        not_found = 1
        OTHERS    = 2.
    IF sy-subrc NE 0.
*     Put the error in the response
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_value.
      lr_response->set_cdata( lv_value ).
      lr_response->set_status( code = 400 reason = 'Document not found!' ).
      EXIT.
    ENDIF.

*   4.
*   Convert file information
    READ TABLE lt_files INTO ls_file INDEX 1.
    MOVE-CORRESPONDING ls_file TO: ls_doc_file, ls_phio.

*   Checkout document
    SELECT SINGLE kpro_use FROM tdwa INTO ls_checkout_def-kpro_use
      WHERE dokar EQ ls_draw-dokar.
    ls_checkout_def-comp_get = 'X'.
    ls_checkout_def-content_provide = 'TBL'.
    CALL FUNCTION 'CV120_DOC_CHECKOUT_VIEW'
      EXPORTING
        ps_cout_def = ls_checkout_def
        ps_doc_file = ls_doc_file
        ps_draw     = ls_draw
        ps_phio     = ls_phio
      TABLES
        ptx_content = lt_content
      EXCEPTIONS
        error       = 1
        OTHERS      = 2.
    IF sy-subrc NE 0.
*     Put the error in the response
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_value.
      lr_response->set_cdata( lv_value ).
      lr_response->set_status( code = 400 reason = 'Failed to read document!' ).
      EXIT.
    ENDIF.

*   5.
*   Put document content in response
    LOOP AT lt_content ASSIGNING <fs_content>.
      CONCATENATE lv_data <fs_content>-orblk INTO lv_data IN BYTE MODE.
    ENDLOOP.
    lv_data = lv_data(<fs_content>-orln).
    lr_response->set_data( lv_data ).
    lr_response->set_status( code = 200 reason = '' ).
*   6.
*   Set MIME type
    SELECT SINGLE mimetype FROM tdwp INTO lv_value
      WHERE dappl EQ ls_file-dappl.
    IF sy-subrc EQ 0.
      lr_response->set_header_field( name = 'Content-Type' value = lv_value ).
    ENDIF.
  ENDIF.

ENDMETHOD.

Some explanation of the code:

  1. The method handle_request has one parameter called server. For us, only the HTTP request and the HTTP response are important. First of all, the HTTP method of the request is checked.
  2. The document key is extracted from the URL. Note that the URL construct is regarded as a kind of form.
  3. The call of function module CVAPI_DOC_GETDETAIL serves two purposes: first, the existence of the document is checked; second, some of the details needed to view the document are retrieved. If there is an error, the error message is put into the body of the response (by method set_cdata) so the user will see it in the browser. The HTTP status is a kind of error/success code.
  4. The document must be 'checked out' to an internal table, which is done by calling function module CV120_DOC_CHECKOUT_VIEW with the right parameters. Again, if there is an error, it is returned in the response.
  5. The (binary) data in the internal table is put into a (binary) string of the correct length and put into the response. Note that we use the method set_data for binary data, instead of set_cdata which is suitable for character data only.
  6. Your operating system can tell if a file is a picture or a PDF file by looking at its extension. Within HTTP, we don't have extensions, so we have to use another way to indicate the type of content. This is the MIME type. The MIME type should be maintained within DMS customizing, on something that is called a Workstation Application. Start transaction SPRO, and start customizing activity Cross-Application Components -> Document Management -> General Data -> Define Workstation Application. If you double click on a row, you can enter a mime type:

    This is an important step, because the browser will not be able to select the right program or plugin to open the file. Otherwise, you might see just some unintelligible text.


Assign the handler to the service

After activating the handler class, you must indicate that requests to the ZDOC service are handled by this class. Return to transaction SICF and open the service (hint: you can enter the service name on the selection screen). Select tab Handler List, enter the class name and choose Store (Ctrl + S).

Return to the previous screen and choose Activate Service from the context menu. You'll have to confirm this in the popup that appears.

After activation, you can test the service by choosing Test Service from the same context menu. A browser window will open, asking you for your SAP credentials.

Notice that the SAP client is also a parameter in the URL. If you omit it, the request will be processed by the default client. The service will return some error message because you didn't specify a document yet.

To perform a real test, lookup the key of some DMS document in transaction CV03N.

If you extend the URL in the address bar with
&type=<type>&number=<number>&version=<version>&part=<part>
so that it becomes something like
http://10.150.1.232:8000/sap/bc/zdoc?sap-client=800&type=Z01&number=0000000000000010000000239&versio...
the document will appear on your screen. Please pay attention to the leading zeroes!

15 Comments