Additional Blogs by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member181879
Active Contributor
0 Kudos

This is a collection of questions that we see often with short answers to these questions. Where possible, direct links will be used to documentation or to (newly written) Weblogs.


How to Get a Carriage Return into Text (for <htmlb:textEdit>)?


Sometimes you must generate a text string in ABAP that contains carriage return and linefeed sequences. This is interesting in cases where (JavaScript) source code is dynamically built together, or where a specific layout is required. Typical variation of this question: “Required to generate an output like”:




<!code>  Problem:
<!code>  Cause:
<!code>  Remedy:



This can be easily achieved by using the constant CL_ABAP_CHAR_UTILITIES=>CR_LF. Example coding would be:




<!code>  DATA: text TYPE STRING.
<!code>  CONCATENATE 'Problem:' 'Cause:' 'Remedy:'
<!code>    INTO text
<!code>    SEPARATED BY CL_ABAP_CHAR_UTILITIES=>CR_LF.


Or:




<!code>  DATA: text TYPE STRING.
<!code>  CONCATENATE 'Problem:' CL_ABAP_CHAR_UTILITIES=>CR_LF
<!code>              'Cause:'   CL_ABAP_CHAR_UTILITIES=>CR_LF
<!code>              'Remedy:'
<!code>    INTO text.



Keep in mind that for Web AS 620 the “CLASS DEFINITION LOAD” sequence is still required. In Web AS 640 this is magically done.


How to Convert an XSTRING into STRING?


The simple assignment "string = xstring" does not work. However, ABAP contains special classes to handle such conversion. See: CL_ABAP_CONV_IN_CE and CL_ABAP_CONV_OUT_CE




<!code>  DATA: Xcontent  TYPE XSTRING,
<!code>        content   TYPE STRING,
<!code>        conv      TYPE REF TO CL_ABAP_CONV_IN_CE,
<!code>        len       TYPE I.
<!code>   
<!code>  conv = CL_ABAP_CONV_IN_CE=>CREATE( input = Xcontent ).
<!code>  conv->READ( importing data = content len = len ).
</pre>


The BSP Application only Works in Debugger


Typical question: "We have developed a BSP application and we're trying to call some function. It doesn't work. However, if we execute the BSP application in debug mode, everything works fine. Why?"



When you log onto a WebAS system the Dynpro engine is started that contains all the logic to handle interaction with the SAPGUI. For example, things such as rendering controls and doing file upload/download are all available.



When you run an HTTP request into Web AS, a very special "dark" (or background) Dynpro is started. A minimal Dynpro is required to get ABAP up and running. But now there is no SAPGUI available, so no SAPGUI-based service will work. Also little things such as popup windows do not work.



So why does the code work in the debugger? Because, once the debugger is triggered,it realizes that a normal Dynpro is required for the debugger and not the "dark" one. So the full-blown Dynpro engine is started, making all SAPGUI services available.



You can consider finding the code that causes the error and bracket it with IF-statements in this function, so that no SAPGUI interaction is required in the context of a HTTP call.


How to Determine if BSP (HTTP request) Is Active?


Typical question: "For our application, the type of environment being used must be checked so as to remove some code when not running in SAPGUI."



The function group SFES provides at least two interesting functions: GUI_IS_AVAILABLE and GUI_IS_ITS. But to test for BSP, or more exactly for HTTP request processing, the function ICF_IS_PLUGIN_SESSION should be used. The interesting exported parameters are plugin_session (Y/N), and plugin_protocol (1=HTTP, 2=HTTPS).


Why Do My Cookies Not Work?


Ever so often (twice this week!) the typical question rolls through my mailbox: “I get and set cookies like a maniac, but they do not work”.



The answer is subtle, very subtle. You must distinguish between the HTTP request (that is sent from the browser to the server), and the HTTP response (that is returned as a reply from the server to the browser). If we want to send a cookie to the browser, we must set the cookie in the response! The request is discarded after it has been processed. If we want to read a cookie from the browser, then look in the request.



It's always:




<!coderesponse->set_cookie( name = ‘what_brian_likes’ value = ‘chocolate chip’ ).


AND:




<!coderequest->get_cookie( EXPORTING name = ‘what_brian_likes’ IMPORTING value = value ).



For the record (for those interested), the set_cookie call places a “Set-Cookie” header field into the HTTP response. The get_cookie reads from the “Cookie” header field in the HTTP request. Having this additional information available makes it easier to understand HTTP traces. See also: Persistent Client State: HTTP Cookies or the new official internet standard RFC 2965 .



Why is it possible to do both get and set calls for cookies on both the HTTP request and response? This is because the Web AS can also function as an HTTP client (effectively as a browser), in which case the Web AS is sending out HTTP requests. Then it should be possible to set a cookie in the request.


Browser Problems Related to GZIP Content-Encoding


In the recent past we have had a number of diverse problems that have all been correlated to GZIP compression problems in the browser. Typical problems:



    • CSS files are not used and display is formatted with any style sheets.

    • JavaScript files are corrupt, causing undetermined JavaScript errors, and showing just garbage in any debugger.

    • ZIP files are corrupt and can not be unpacked

    • PDF documents are not displayed, only blank screen is displayed, or an error message is given that the PDF file is damaged.

    • EXE and JAR files are not executed after download.



It is very important to understand that this is an intermittent problem, sometimes related to a timing problem in the browser. Which often means that nine out of ten times things work perfectly, and only once fails. And then in other cases we see the failure all the time on some systems. Specifically for the Internet Explorer, this very much depends on the combination of patches installed.



We have seen this problem now in different versions and/or patches of Internet Explorer, Netscape and Mozilla browsers. As such, it has become difficult to find workarounds, as the exact combination where the problem occurs, is not always clear.



For descriptions of the problems relative to IE browsers, see following links:



http://support.microsoft.com/default.aspx?scid=kb;en-us;837251&Product=ie600 http://support.microsoft.com/default.aspx?kbid=331596 http://support.microsoft.com/default.aspx?scid=kb;en-us;823386&Product=ie http://support.microsoft.com/default.aspx?scid=kb;en-us;822002&Product=ie http://support.microsoft.com/default.aspx?scid=kb;en-us;823099&Product=ie



The simplest way to test for this situation is to disable HTTP/1.1. GZIP encoding is only supported with HTTP/1.1, and not with HTTP/1.0. In the case of HTTP/1.1, the browser usually sends an additional header "Accept-Encoding" to indicate that it is willing to accept GZIP encoded data. The server, on seeing this header, and if the application also requests compression, will optionally encode the data with GZIP and set the “Content-Encoding” header to gzip. The server is not required to use GZIP encoding.



Therefore, whenever problems are observed, our recommendation is to reconfigure the browser to use only HTTP/1.0, thereby also disabling GZIP compression. For Internet Explorer, this can be done in the browser menu Tools, Options, and then Advanced settings. Remove both checkboxes associated with HTTP/1.1.



Development Aspect:  The ICM layer does not automatically compress outgoing traffic. This has to be explicitly requested by applications. So one approach to disable GZIP compression is on the server to explicitly not request that GZIP be enabled for outgoing traffic. For BSP applications, there are two places where this can be configured.



The first place is within the ICF layer (see transaction SICF). For each node in the ICF tree, it is possible to request that compression be activated for all requests that match this ICF node. This is done on the “service data” tab for the specific note. Keep in mind that these settings can be inherited from parent nodes.



Furthermore, for each BSP page/controller, a compression option is supported, that will also enable GZIP compression if supported by the browser. With this, it is possible to more fine granular enable or disable compression only for specific pages.



In general we do NOT wish to recommend that GZIP be disabled, as we expect with time that newer browser patches will make the problems completely disappear. In terms of development, it is better to set these flags as are required, and have then globally enable/disable GZIP compression for the complete application server, until all browsers have been patched.



The Global Switch: The workaround with changing the browser settings is not very practical for large installations. Also changing the application settings is possible, but result in a modification. Also, in the long run, GZIP compression is actually helpful (probably more for WAN type connections).



For this reason, a new profile parameter (ict/disable_compression = true) is now supported to enable/disable GZIP for all traffic on the application server. This will be available in all new kernels after April 2004 (see OSS note 715598). The "emergency switch" should be considered all cases where problems as described above are observed.


BSP Page Events


There are a number of interesting events for each BSP, and one or two mysterious ones which seems never to be used. Here is a quick overview of the page events.



onCreate: Is triggered directly after the page is created. It is effectively a constructor for the page. If the BSP application is stateful, pages are created only once and cached. Then onCreate will be called only once. Here you will do things such as read your data from the database into a page attributes. Typically all attributes will also be set with default values. Later during autofill, those attributes for which exist new values, will be overwritten.



onRequest: Not used anymore. It is there because of things that never was allowed to leave the lab. No questions on this. This event is always called directly before onInputProcessing, and as such is equivalent to it.



onInputProcessing: Is only called for event handling. At a very technical level: it is only called if the formfield onInputProcessing is in the incoming request. Otherwise, no event, and no call.



onInitialization: This event is called for every request/response cycle. If there was an event,it has been processed. Now we are preparing the data (page attributes) for the layout handling.



onLayout: This is the actual call to the code we generate for the layout. Renders out that nice HTML page.



onManipulation: See onRequest. This event is also tied to things that never came. It can be ignored.



onDestroy: Page destructor.


HTMLB: How to Cancel a Form Submit


Specify an onClientClick event. This string is pure JavaScript code, that must contain all the necessary code to determine whether the submit should be cancelled or not. With this JavaScript code, an additional object called htmlbevent is placed inside a JavaScript function. On this htmlbevent object there are two interesting attributes cancelSubmit and returnValue. The cancelSubmit influences whether the form is submitted or not. The other is the return value to the real onclick event in DOM. This is usually false to cancel further event processing.



Here an example:




<!code>  <%@page language="abap"%>
<!code>  <%@extension name="htmlb" prefix="htmlb"%>
<!code>  <htmlb:content>
<!code>   <htmlb:page>
<!code>    <htmlb:form>
<!code>   
<!code>     <htmlb:button
<!code>       text          = "Submit Form"
<!code>       onClick       = "myClickHandler"
<!code>       onClientClick = "htmlbevent.cancelSubmit = !window.confirm('Submit?');"
<!code>     />
<!code>   
<!code>    </htmlb:form>
<!code>   </htmlb:page>
<!code>  </htmlb:content>
<!code>   


The JavaScript function window.confirm() will display a popup with the specificied text, and offer two buttons OK and Cancel. Depending on the selected button, the cancelSubmit flag is set.


HTMLB: How to Hook into Every Form Submit


The HTMLB library is calls a pre and post JavaScript methods just before the actual form is submitted. These functions can be "intercepted" and enhanced with additional logic.




<!code>  <%@page language="abap"%>
<!code>  <%@extension name="htmlb" prefix="htmlb"%>
<!code>  <htmlb:content>
<!code>   <htmlb:page>
<!code>   
<!code>      var origPre  = htmlbSubmitPre;
<!code>      var origPost = htmlbSubmitPost;
<!code>    </script>
<!code>   
<!code>      function htmlbSubmitPre()
<!code>      {
<!code>        window.status =  'Posting...';
<!code>        origPre();
<!code>      }
<!code>      function htmlbSubmitPost()
<!code>      {
<!code>        window.status += 'Waiting Answer';
<!code>        origPost();
<!code>      }
<!code>    </script>
<!code>    <htmlb:form>
<!code>     <htmlb:button
<!code>       text          = "Submit Form"
<!code>       onClick       = "myClickHandler"  />
<!code>    </htmlb:form>
<!code>   </htmlb:page>
<!code>  </htmlb:content>
<!code>   


The important part of this example code is that there must be two script blocks. In the first block references to the original functions are hooked. In the second block the functions are overwritten, and supplied with their additional functionality. As a last step, the original functions are still called.


BSP Navigation: A Simple Example of Next/Previous Buttons


Sometimes it is required to have a next/previous button on each page, to help the user through a few simple steps. This is typical for guided processed, such as “employee self services”, where there are number of steps that must be completed, and on each step one can go on to the next step, or return to the previous step. Here is a small example, that use the new design in design2003 buttons, and with a careful setting of the onClick string, can use a very simple route in the onInputProcessing route to handle the stepping. (Consider also to use a PHTMLB roadMap in these types of applications to show the actual step been processed.)




<!codeonInputProcessing for all three pages
<!code>   
<!code>  DATA: event TYPE REF TO if_htmlb_data.
<!code>  event = cl_htmlb_manager=>get_event_ex( request ).
<!code>  IF event IS NOT INITIAL AND event->event_name = 'button'.
<!code>    navigation->goto_page( event->event_server_name ).
<!code>  ENDIF.
<!code>   
<!codepage1.htm
<!code>   
<!code>  <%@page language="abap" otrTrim="true"%>
<!code>  <%@extension name="htmlb" prefix="htmlb"%>
<!code>  <htmlb:content design="design2003">
<!code>    <htmlb:page>
<!code>      <htmlb:form>
<!code>        <htmlb:button       text          = "Page 2"
<!code>                            design        = "NEXT"
<!code>                            onClick       = "page2.htm" />
<!code>      </htmlb:form>
<!code>    </htmlb:page>
<!code>  </htmlb:content>
<!code>   
<!codepage2.htm
<!code>   
<!code>  <%@page language="abap" otrTrim="true"%>
<!code>  <%@extension name="htmlb" prefix="htmlb"%>
<!code>  <htmlb:content design="design2003">
<!code>    <htmlb:page>
<!code>      <htmlb:form>
<!code>        <htmlb:button       text          = "Page 1"
<!code>                            design        = "PREVIOUS"
<!code>                            onClick       = "page1.htm" />
<!code>        <htmlb:button       text          = "Page 3"
<!code>                            design        = "NEXT"
<!code>                            onClick       = "page3.htm" />
<!code>      </htmlb:form>
<!code>    </htmlb:page>
<!code>  </htmlb:content>
<!code>   
<!codepage3.htm
<!code>   
<!code>  <%@page language="abap" otrTrim="true"%>
<!code>  <%@extension name="htmlb" prefix="htmlb"%>
<!code>  <htmlb:content design="design2003">
<!code>    <htmlb:page>
<!code>      <htmlb:form>
<!code>        <htmlb:button       text          = "Page 2"
<!code>                            design        = "PREVIOUS"
<!code>                            onClick       = "page2.htm" />
<!code>      </htmlb:form>
<!code>    </htmlb:page>
<!code>  </htmlb:content>
<!code>   


In this example, the next and previous buttons have their onClick handler immediately wired with the name of the new target page. This way, the event handler needs little no more information to complete the navigation.


OTR Text and Trailing Spaces


<p><u>Question:</u> OTR texts in my page come with long tail of spaces on the end. In normal HTML spaces are trimmed but when I use OTR texts for labels, buttons or tooltips, they are looking ugly. How can I get rid of these spaces?</p>

<p>We have added a new attribute for the page directive - otrTrim. This attribute has boolean type (true or false) and is false by default (for the backward compatibility). If it is set to true all OTR texts on the page are condensed (all spaces are removed from beginning and end of the string). Use as: “<%@ page language="abap" <b>otrTrim = "true"</b> %>”</p>


Printing BSP Pages that Contain HTMLB Elements


Why Can Model Binding Parameters Not Be Set Dynamically?


<!code>  <htmlb:inputField id="id1” type="string" value="//model/client.clientno" />
<!code>   


However, I would like to set the actual model binding string dynamically for many complex reasons. My code is now (with v = “//model/client.clientno”):




<!code>  <htmlb:inputField id="id1” type="string" value="<%=v%>" />
<!code>   


However, now in the browser the string "//model/client.clientno" appears, and not the value from the model.



Answer: The problem is actually complex. When the compiler sees a tag, it generates code to execute it. The BSP compiler sees:




<!code>  <tag:element a1= "v1"/>
<!code>   


It generates code:




<!code>  DATA: t1 TYPE REF TO CLTAG_ELEMENT.
<!code>  CREATE OBJECT _t1.
<!code>  _t1->a1 = 'v1'.
<!code>  ...code to execute tag...
<!code>   


The problem is now what happens if the attribute is of type integer, or of type ref to data, and we have model binding. Then it is not possible to place the binding string inside the attribute (not matching types). For this reason, once model binding is set for a specific attribute, there are two generated attributes on the element. The one is of the configured type, the other (prefixed with _) is of type string for the binding path. Only one is set by the BSP compiler.



The BSP compiler sees:




<!code>  <tag:element a1 = "//model/m1"/>
<!code>   


It generates code:




<!code>  DATA: t1 TYPE REF TO CLTAG_ELEMENT.
<!code>  CREATE OBJECT _t1.
<!codet1->[b][/b]a1 = '//model/m1'.
<!code>  ...code to execute tag...
<!code>   


Already at compile time, the compiler must know whether it must set a1 or _a1. Model binding is pure compile time construct, and does not work at runtime. It is not possible to set this parameter dynamically.



The only possible way to achieve this, if you really insist, is to dynamically process the tag directly on the page. Then you must set the attributes yourself correctly. For background reading information, I would recommend this new weblog: BSP Programming: Writing Composite Elements. The expected code will be about:




<!code>  <%
<!code>      DATA: myTag TYPE REF TO CL_TAG_ELEMENT.
<!code>      myTag = CL_TAG_ELEMENT=>FACTORY( a1 = vhvalue ).
<!code>      WHILE mpage_context->element_process( myTag )
<!code>                    = if_bsp_element=>co_element_continue.
<!code>      ENDWHILE.
<!code>  %>
</pre>


How Do I Download/Publish a .XLS File?


Question: It is required to display data from an internal table into an Excel sheet through BSP.



Answer: We have this complete answer from Suresh Babu. Effectively the data from the itab is concatenated into a string, each field separated by TAB, and each line terminated with a CR/LF.




<!code>  DATA: flights TYPE FLIGHTTAB,
<!code>        flight  LIKE LINE OF flights,
<!code>        output  TYPE string.
<!code>   
<!code>  CONSTANTS: crlf TYPE string VALUE CL_ABAP_CHAR_UTILITIES=>CR_LF,
<!code>             tab  TYPE string VALUE CL_ABAP_CHAR_UTILITIES=>HORIZONTAL_TAB.
<!code>            
<!code>  SELECT * FROM sflight INTO TABLE flights UP TO 20 ROWS.
<!code>   
<!code>  LOOP AT flights INTO flight.
<!code>    DATA: seatsmax TYPE string.  seatsmax = flight-seatsmax. CONDENSE seatsmax.
<!code>    DATA: seatsocc TYPE string.  seatsocc = flight-seatsocc. CONDENSE seatsocc.
<!code>    CONCATENATE output 
<!code>                flight-carrid     tab
<!code>                flight-connid     tab
<!code>                flight-fldate     tab
<!code>                flight-planetype  tab
<!code>                seatsmax          tab
<!code>                seatsocc          tab
<!code>                crlf
<!code>           INTO output.
<!code>  ENDLOOP.
<!code>   
<!code>  * some Browsers have caching problems when loading Excel format
<!code>  response->delete_header_field( name = if_http_header_fields=>cache_control ).
<!code>  response->delete_header_field( name = if_http_header_fields=>expires ).
<!code>  response->delete_header_field( name = if_http_header_fields=>pragma ).
<!code>   
<!code>  * start Excel viewer either in the Browser or as a separate window
<!code>  response->set_header_field( name  = if_http_header_fields=>content_type
<!code>                              value = 'application/msexcel' ).
<!code>  response->set_header_field( name  = if_http_header_fields=>content_disposition
<!code>                              value = 'attachment; filename=webforms.xls' ).
<!code>   
<!code>  * finally display Excel format in Browser
<!code>  response->set_cdata( data = output ).
<!code>   
<!code>  * do not process Layout, response has been rendered
<!code>  navigation->response_complete( ).
<!code>   
<!code>   
</pre>


Closing a stateful BSP Application Inside the SAPGUI HtmlViewer


Question: A stateful BSP application is run inside the SAPGUI using the HtmlViewer control.  To close also the BSP session, the system/session_management example is used. However, the sessions are never closed.



Answer:

 For a very good discussion of this topic, see the excellent weblog Updated!: Calling a BSP application from a SAPGui Transaction from Thomas Jung. Here below is a small summary of the problem, with a minor modification to make it even easier.



In principle, the session management approach uses a popup browser window to quickly close the session. This popup window shares cookies with the original window, so that a request to close the session in the popup window will still send the session cookies from the original window. This request lands in the same session and can close the session quickly.



However, once the BSP application runs inside the SAPGUI within the HtmlViewer control, this fails. The reason for this is the way that the HtmlViewer control is implemented. The popup window is a complete new instance of the browser. The new popup window does not share cookies with the original window, and as such no session id (stored in a cookie) is available in the popup window. It is then not possible to access/close the original session.



Thomas’s suggestion contains two very important ideas. For one, the close sequence for the BSP application is not done via a popup window, but directly inside the original browser session. Secondly, the protection hook to do the final close sequence is not inside the browser (using the typical frameset approach), but actually stored inside the ABAP code that contains the HtmlViewer control.



The simple approach is to just find the place where the HtmlViewer control is destructed and add a few lines to also close the BSP session.




<!code
<!code>  html->free( ).       FREE html.
<!code>  container->free( ).  FREE container.
<!code>   


For a more simplified approach, we do not create an extra dummy page inside the application. Instead we use two interesting features from ICM and BSP together. The ICM supports a sap-sessioncmd=CANCEL mode. With this, the session is killed by ICM, and then the incoming URL is still processed inside a new session. We just require an URL that looks exactly like the application URL in the part up to the name of the application, so that the session id cookie will be send with it.



After ICM has cancelled the session, the URL will still be processed. So we require an URL that will be processed by the BSP runtime without any problems (and without opening a new session!). For this, we just use a special 1x1 URL. The BSP runtime has a performance improvement that will always reply with a 1x1.gif for an incoming URL of the format “.../1x1”. However, this 1x1 image is cached, and the trick will only work once. So we just add one timestamp to make the URL unique for the browser, and have it trigger the loading of the 1x1 image.



The complete code change is:




<!codeDATA: url(255) TYPE c.
<!codehtml->get_current_url( IMPORTING url = url ).
<!codecl_gui_cfw=>flush( ).
<!codeCONCATENATE url '/' sy-uzeit '/1x1?sap-sessioncmd=cancel' INTO url.
<!codehtml->show_url( url = url ).
<!codecl_gui_cfw=>flush( ).
<!code>  html->free( ).       FREE html.
<!code>  container->free( ).  FREE container.
<!code>   


By adding the session close sequence in the dynpro coding, the BSP session is terminated when the dynpro is left.


Printing  a BSP Page


Question: In my BSP, there is a button used for printing the page. When user clicks on that button, that page should get printed on printer. How can this be achieved?



Answer:

 A very elegant solution was presented by Raja in Printing from BSP Application. Just add the following code to your BSP page:




<!code>  <htmlb:button   id            = "btnPrint"
<!code>                  text          = "Print"
<!code>                  onClientClick = "javascript:window.print()"
<!code>  />
<!code>   


Craig and Maximilian adds the last part of the puzzle: note there are some problems with printing some of the BSP Elements - colours, graphics and things sometimes don't look exactly right.  In Internet Explorer, go to menu Extras -> Internet Options -> Advanced. There look for Printing, and the Checkbox "Print background colours and images" - check this setting when/before printing your pages, to make sure you will get the right output.


Calling BAPIs from BSP


Question:

Using Call transaction in a BSP scenario encountered a problem with using a BAPI in a BSP application. Within this BAPI a CALL TRANSACTION was made. During this call, any message types E, A and X caused HTTP error 500 with no dump info and a termination of the session. For some background information, see also: BSP In-Depth: MESSAGE Statement Handling.



Answer: When a BSP application is active, it is run in a context of a "dark" Dynpro, which cannot handle all the features of a normal Dynpro in a session of type Plug-In HTTP. If the BAPI/RFC is called with DESTINATION NONE, a new RFC type session is opened. With such a type session the message type E, A and X result in 'normal' behaviour where the BAPI returns message type E. This doesn’t work with Function modules that are not RFC enabled. However, usually it is easy to create a RFC enabled wrapper around it.


9 Comments