Skip to Content

In this blog I will give a simple example on how to use a captcha in ABAP Webdynpro.

Architecture

We generate a simple string in ABAP and send it to a servlet on a J2EE server. This servlet generates an image with SimpleCaptcha. http://simplecaptcha.sourceforge.net/ . The generated images is received on the ABAP stack and copied to a cached response so the captchastring is not in the url of the image.

The source of the java application can be downloaded here. (tested on SAP NW 7.31 SP5)

Captcha Servlet

This is a simple servlet application that generates a captcha image from a given string.

1.       Download the simplecaptcha library

2.       Create an external library DC and name it captcha/lib

3.       Add the simplecaptcha jar-file to the libraries folder

4.       Add the jar-files to the public part of the library

5.       Create a new web module development component and name it captcha

6.       Add a DC dependency between the web module and the library

7.       Add a new servlet and give it a name and package

8.       Copy the simplecaptcha.jar to the WEB-INF/lib directory

9.       Enter the following code:

package com.flexso.captcha;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.flexso.captcha.noise.CustomNoiseProducer;
import nl.captcha.Captcha;
import nl.captcha.servlet.CaptchaServletUtil;
import java.awt.Color;
import java.awt.Font;
public class CaptchaImage extends HttpServlet {
          private static final long serialVersionUID = 1L;
            private static int _width = 180;
            private static int _height = 60;
            private static final List<Color> COLORS = new ArrayList<Color>(2);
            private static final List<Font> FONTS = new ArrayList<Font>(3);
            static {
                        COLORS.add(Color.BLACK);
                        COLORS.add(Color.BLUE);
                        FONTS.add(new Font("Geneva", 2, 48));
                        FONTS.add(new Font("Courier", 1, 48));
                        FONTS.add(new Font("Arial", 1, 48));
                      }
    public CaptchaImage() {
        super();
    }
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                    response.setContentType("image/png");
                    response.setCharacterEncoding("UTF-8");
                    String word = request.getParameter("captcha");
                    WordGenerator wordRenderer = new WordGenerator(word);
                    Captcha captcha = new Captcha.Builder(_width, _height).addText(wordRenderer)
                .gimp()
                .addNoise())
                .build();
              CaptchaServletUtil.writeImage(response, captcha.getImage());
          }
}

10.   Add a class WordGenerator to be able to decide which word will be used to generate the captcha image.

package com.flexso.captcha;
import nl.captcha.text.producer.TextProducer;
public class WordGenerator implements TextProducer
{
  private String text = "";
  public WordGenerator(String text){
            this.text = text;
  }
  public String getText(){
    return this.text;
  }
}

11.   Adapt the web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>LocalDevelopment~captcha~flexso.com</display-name>
  <servlet>
    <description></description>
    <display-name>CaptchaImage</display-name>
    <servlet-name>CaptchaImage</servlet-name>
    <servlet-class>com.flexso.captcha.CaptchaImage</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CaptchaImage</servlet-name>
    <url-pattern>/captcha.png</url-pattern>
  </servlet-mapping>
</web-app>

12.   Create an enterprise application DC with the name captcha/ear

13.   Add the web module to the captcha/ear project

14.   Adapt the application.xml

<?xml version="1.0" encoding="ASCII"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" version="5">
  <display-name>LocalDevelopment~captcha~ear~flexso.com</display-name>
  <module>
    <web>
      <web-uri>flexso.com~captcha.war</web-uri>
      <context-root>captcha</context-root>
    </web>
  </module>
</application>

15.   Deploy the ear file.

16.   Test the captcha http://<portalserver>:<port>/captcha/captcha.png?captcha=test

/wp-content/uploads/2012/12/captcha_164885.png

ABAP Webdynpro application

In the ABAP WD application we build a random string to send to the captcha application.

We request the captcha image  on the ABAP server and provide it in a cached response to our webdynpro application to hide the captcha string in the url.

1.       Create a new web dynpro component

2.       In the component controller create a context node CAPTCHA with 3 strings: SOURCE, ANSWER, IMAGE.

/wp-content/uploads/2012/12/context_164886.png

3.       Create method generate_captcha in the component controller. In this method we create a random string, with this string we build the url to the J2EE server to generate the captcha image. At the end of the method we copy the image from the java server to a cached response on the ABAP stack and then we fill the context. This is the code:

method GENERATE_CAPTCHA .
  CONSTANTS: CO_ALFABET type CHAR30 VALUE 'abcdefghijklmnopqrstuvwxyz'.
  DATA: lv_captcha type CHAR08,
        lv_i   type i,
        lv_char type CHAR01,
        lv_seed type i,
        lo_ran type ref to cl_abap_random_int,
        lv_url type string,
        lv_length type i value 8.
*   generate a random string with length lv_length
  lv_seed = sy-timlo.
  lv_i = STRLEN( co_alfabet ) - 1.
  lo_ran = cl_abap_random_int=>create( min = 0 max = lv_i seed = lv_seed ).
  DO lv_length TIMES.
    lv_i = lo_ran->get_next( ).
    lv_char = CO_ALFABET+lv_i(1).
    lv_i = lo_ran->get_next( ).
    if lv_i MOD 2 = 0 .
      TRANSLATE lv_char TO UPPER CASE.
    endif.
    CONCATENATE lv_captcha lv_char into lv_captcha.
  ENDDO.
*   build url to generate captcha image
  CONCATENATE '/captcha/captcha.png?captcha=' lv_captcha into lv_url.
  lv_url = wd_this->GET_CACHED_URL( lv_url ).
  " Set data in context
  DATA lo_nd_captcha TYPE REF TO if_wd_context_node.
  DATA lo_el_captcha TYPE REF TO if_wd_context_element.
  DATA ls_captcha TYPE wd_this->Element_captcha.
  lo_nd_captcha = wd_context->get_child_node( name = wd_this->wdctx_captcha ).
  lo_el_captcha = lo_nd_captcha->get_element( ).
  lo_el_captcha->set_attribute( name =  `SOURCE` value = lv_captcha ).
  lo_el_captcha->set_attribute( name =  `IMAGE` value = lv_url ).
endmethod.

4.       To copy the image from the JAVA response to the ABAP response I created a separate method GET_CACHED_URL. Importing parameter IV_URL (STRING) and returning parameter OV_URL(STRING)

method GET_CACHED_URL .
  DATA : lo_http_client TYPE REF TO if_http_client,
         lo_cached_response TYPE REF TO IF_HTTP_RESPONSE,
         lv_image TYPE xstring,
         lv_guid TYPE GUID_32.
  " read the image on the java server
  CALL METHOD cl_http_client=>create_by_url
    EXPORTING
      url    = 'http://<javaserver>:<port>'
    IMPORTING
      client = lo_http_client
    EXCEPTIONS
      OTHERS = 1.
  CALL METHOD lo_http_client->request->set_header_field
    EXPORTING
      name  = '~request_uri'
      value = iv_url.
  CALL METHOD lo_http_client->send
    EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3.
  CALL METHOD lo_http_client->receive
    EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3.
  lv_image = lo_http_client->response->get_data( ).
  " send the image to the cached response on the ABAP server
  CREATE OBJECT lo_cached_response TYPE CL_HTTP_RESPONSE EXPORTING ADD_C_MSG = 1.
  " SET IMAGE TO MIME
  lo_cached_response->SET_DATA( lv_image ).
  lo_cached_response->SET_HEADER_FIELD( NAME = IF_HTTP_HEADER_FIELDS=>CONTENT_TYPE VALUE = 'image/png' ).
  lo_cached_response->SET_STATUS( CODE = 200 REASON = 'OK' ).
  lo_cached_response->SERVER_CACHE_EXPIRE_REL( EXPIRES_REL = 100 ).
  " generate an url
  CALL FUNCTION 'GUID_CREATE'
    IMPORTING
      EV_GUID_32 = lv_guid.
  CL_WD_UTILITIES=>CONSTRUCT_WD_URL( EXPORTING
  APPLICATION_NAME = 'Z_CAPTCHA' "WEBDYNPRO APPLICATION NAME
  IMPORTING OUT_LOCAL_URL = OV_URL ).
  CONCATENATE OV_URL '/' lv_guid sy-uzeit '.png' INTO OV_URL.
  CL_HTTP_SERVER=>SERVER_CACHE_UPLOAD( URL = OV_URL RESPONSE = lo_CACHED_RESPONSE ).
endmethod.

5.       Open the view of the webdynpro

6.       In the context tab, bind the CAPTCHA node from the componentcontroller.

/wp-content/uploads/2012/12/context2_164887.png

7.       In the layout tab add following elements to the view:

     a.       An image and bind the source property to attribute IMAGE af the CAPTCHA node.

     b.      An input field and bind the property value to attribute ANSWER of the CAPTCHA node.

     c.       A button ‘check captcha’ and assign an action CHECK

     d.      A button refresh and assign an action REFRESH

8.       Implement the 2 actionhandlers

method ONACTIONCHECK.
    DATA lo_nd_captcha TYPE REF TO if_wd_context_node.
    DATA lo_el_captcha TYPE REF TO if_wd_context_element.
    DATA ls_captcha TYPE wd_this->Element_captcha.
*     get message manager
    data lo_api_controller     type ref to if_wd_controller.
    data lo_message_manager    type ref to if_wd_message_manager.
    data: lv_Str type string,
          lv_text type string,
          lv_type type I.
    " read context
    lo_nd_captcha = wd_context->get_child_node( name = wd_this->wdctx_captcha ).
    lo_el_captcha = lo_nd_captcha->get_element( ).
    lo_el_captcha->get_static_attributes( IMPORTING static_attributes = ls_captcha ).
    " get message manager
    lo_api_controller ?= wd_This->Wd_Get_Api( ).
    CALL METHOD lo_api_controller->GET_MESSAGE_MANAGER
      RECEIVING
        MESSAGE_MANAGER = lo_message_manager
        .
    lv_type = IF_WD_MESSAGE_MANAGER=>CO_TYPE_ERROR.
    lv_text = 'ERROR!'.
    TRANSLATE LS_CAPTCHA-SOURCE TO UPPER CASE.
    TRANSLATE LS_CAPTCHA-ANSWER TO UPPER CASE.
"   compare captcha answer with the source
    if LS_CAPTCHA-SOURCE = LS_CAPTCHA-ANSWER.
      lv_type = IF_WD_MESSAGE_MANAGER=>CO_TYPE_STANDARD.
      lv_text = 'OK!'.
    endif.
*     report message
    CALL METHOD lo_message_manager->REPORT_MESSAGE
      EXPORTING
        MESSAGE_TEXT              = lv_text
        MESSAGE_TYPE              = lv_type
      RECEIVING
        MESSAGE_ID                = lv_str
        .
endmethod.
method ONACTIONREFRESH .
  WD_COMP_CONTROLLER->GENERATE_CAPTCHA( ).
endmethod.

Create a web dynpro application object and test the application!

Result:

/wp-content/uploads/2012/12/result_164888.png

To report this post you need to login first.

15 Comments

You must be Logged on to comment or reply to a post.

  1. Richard Zenarosa

    Hi Jaochim,

    Have you had any issues with the new reCAPTCHA images? The worst case we’ve had is someone trying 30 times before he/she got it right.

    Regards,

    Richard

    (0) 
  2. samir sute

    Dear Joachim Van Praet,

    Very nice blog it helped me lot. one 1 issue is coming in my case. I have implemented everything as you suggested. when i run the Eclipse project that time captcha is gettingg render but same captcha is not getting render when I run the WD application.

    Can you please suggest what possibly the issue. It would be great help.

    Thanks,

    Samir

    (0) 
    1. Joachim Van Praet Post author

      Hi Samir,

      Can you check the connection between your 2 systems?

      The one where you generate the captcha and the abap system.

      Try to debug and check your return code in the method GET_CACHED_URL

      1. CALL METHOD lo_http_client->send 
      2.     EXCEPTIONS 
      3.       http_communication_failure = 1 
      4.       http_invalid_state         = 2 
      5.       http_processing_failed     = 3. 
      6.   CALL METHOD lo_http_client->receive 
      7.     EXCEPTIONS 
      8.       http_communication_failure = 1 
      9.       http_invalid_state         = 2 
      10.       http_processing_failed     = 3.


      Is your image received in this method? Is there content in lv_image after lv_image = lo_http_client->response->get_data( ) ?


      Did you change the application name in this piece of code?

      1. CL_WD_UTILITIES=>CONSTRUCT_WD_URL( EXPORTING 
      2.   APPLICATION_NAME = ‘Z_CAPTCHA’ “WEBDYNPRO APPLICATION NAME 
      3.   IMPORTING OUT_LOCAL_URL = OV_URL ).

      kr,

      Joachim

      (0) 
      1. samir sute

        Dear Joachim,

        Thanks a lot for your reply.

        I have debugged and found there is  http_communication_failure = 1 in receive method.

        1.   CALL METHOD lo_http_client->receive
        2.     EXCEPTIONS
        3.       http_communication_failure = 1
        4.       http_invalid_state         = 2
        5.       http_processing_failed     = 3.

        I am using Local Eclipse Apache server for captcha image generation. I have mentioned java server as below for connection.

        ” read the image on the java server

           CALL METHOD cl_http_client=>create_by_url

             EXPORTING

               url    = https://localhost:8443‘   “‘http://<javaserver>:<port>’

             IMPORTING

               client = lo_http_client

             EXCEPTIONS

               OTHERS = 1.

        Should i use IP address of my local system instead of https://localhost:8443..?

        Thank you again Joachim.

        Best regards,

        Samir

        (0) 
      2. samir sute

        Dear Joachin,

        I have used IP address also but still http_communication_failure = 1 in receive method.

        1.   CALL METHOD lo_http_client->receive
        2.     EXCEPTIONS
        3.       http_communication_failure = 1
        4.       http_invalid_state         = 2
        5.       http_processing_failed     = 3.

        Can you please suggest what could be the issue.

        (Note for your reference : I thought it is problem of local Apache http, so I configured Tomcat to support SSL or https. so now communication is between sap https and local

        tomcat https.)

        Thanks and Regards,

        Samir

        (0) 
        1. Joachim Van Praet Post author

          Samir,

          It will not work when you host your captcha servlet on your client PC. You have to use a java server in your network that can be reached by the SAP server. Contact your system administrator, he can help.

          kr,

          Joachim

          (0) 
          1. samir sute

            Dear Joachin,

            The issue has been resolved.

            Below r the mistakes.

            1) I was translating final url into upper case

            2) I was passing url to method cl_http_client=>create_by_url

                      a) “http://localhost:8080″.

                      b) “https://localhost:8080″

                      c) “https://<IP>:8080

            Below corrections I did to resolve the issue,

            1) I removed statement translation .

            2) Pass url to method cl_http_client=>create_by_url as below

                

               lo_component   = wd_this->wd_get_api( ).

               lo_application = lo_component->get_application( ).

               ip = lo_application->get_remote_address( ).

               CONCATENATE ‘HTTP://’ ip ‘:8080’ INTO url.

               ” read the image on the java server

               CALL METHOD cl_http_client=>create_by_url

                 EXPORTING

                   url           = url “‘http://localhost:8080‘   “‘http://localhost:8080‘ “‘http://<javaserver>:<port>’

                 IMPORTING

                   client        = lo_http_client

                 EXCEPTIONS

                   OTHERS        = 1.

            Result: Now I can able to receive and display the image from my Local Eclipse Apache on ABAP WD application.

            Thanks a lot Joachin, I can able to do this because of ur blog only.

            Thanks and Regards,

            Samir

            (0) 

Leave a Reply