Oracle XDB Restlet Adapter - Testing

  • Testing using a browser

Test your REST examples with your favorite browser using these URLs (they where configured as XMLDB Servlets by src/com.noelios.restlet.ext.xdb_11.1/resources/sql/postInstall.sql script):

http://localhost:8080/searchapp/search?kwd=marcelo+ochoa
http://localhost:8080/userapp/users/scott/orders/300

Note: XMLDB is an HTTP 1.0 complaint connector, usually modern browsers try to connect using HTTP 1.1 so you can experiment that the browser leave the connection open because is trying to use Keep-alive feature.

  • Tests using telnet

Some test can be done by using telnet application to see which headers are sent and got as response from REST WS. For example:

[mochoa@mochoa resources]$ telnet localhost 8080
Trying 127.0.0.1...
Connected to live.dbprism.mochoa.dyndns.org (127.0.0.1).
Escape character is '^]'.
GET /userapp/users/scott/orders/300 HTTP/1.0


HTTP/1.1 200 OK
MS-Author-Via: DAV
DAV: 1,2,<http://www.oracle.com/xdb/webdav/props>
Date: Tue, 03 Jun 2008 19:18:11 GMT
Server: Noelios-Restlet-Engine/1.1.snapshot
Content-Type: text/plain; charset=ISO-8859-1
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Content-Length: 28



__Order "300" for user "scott"__
  • Minimalistic test comparing REST versus native SOAP

** Benchmarking REST application

As you can see in a previous section there is simple User application which returns orders for scott user. Using ApacheBench you can test the application executing:

ab -n {total_request} -c {concurrent_request} http://localhost:8080/userapp/users/scott/orders/300

Where total_request is number of request sent by Apache benchmark paralleling it in concurrent_request request. A result of this execution on my notebook can be compared in this Google sheet.

** Testing a similar WS implemented using SOAP

A similar functionality can be implemented using XMLDB Native SOAP WS. For doing that create this Java source at Scott’s schema:

create or replace and compile java source named “my.OrderCalculator” as

package my;


import java.util.logging.Level;
import java.util.logging.Logger;


public class OrderCalculator {
    /**
     * Java Util Logging variables and default values
     */
    private static Logger logger = null;


    /**
     * Constant used to get Logger name
     */
    static final String CLASS_NAME = OrderCalculator.class.getName();


    static {
            logger = Logger.getLogger(CLASS_NAME);
            logger.setLevel(Level.ALL);
    }


   public static String getOrder(String userName, int orderId) {
        logger.entering(CLASS_NAME,"getOrder",new Object [] {userName,new Integer(orderId)});
        logger.exiting(CLASS_NAME,"getOrder","Order '"+orderId+"' for user '"+userName+"'");
        return "Order '"+orderId+"' for user '"+userName+"'";
   }
}

Note that I have added JDK Logging functionality to compare a closer example to Restlet functionality, obviously routing and many other default Restlet functionalities are not compared with this example. And his PLSQL Call Spec:

CREATE OR REPLACE PACKAGE orders_calculator AUTHID CURRENT_USER AS
  FUNCTION getOrder(user_name IN VARCHAR2, order_id IN NUMBER) RETURN VARCHAR2
as LANGUAGE JAVA NAME
     'my.OrderCalculator.getOrder(java.lang.String, int) return
java.lang.String';
END orders_calculator;
/

As you can see OrderCalculator class is using JDK logging package, to get JDK logging working this grant is required:

SQL> exec dbms_java.grant_permission( 'SCOTT',
'SYS:java.util.logging.LoggingPermission', 'control', '' );
SQL> commit;

Finally to send a POST message using Apache benchmark its necessary to edit a POST XML message like:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://xmlns.oracle.com/orawsv/SCOTT/ORDERS_CALCULATOR/GETORDER">
   <env:Header/>
   <env:Body>
      <ns1:SVARCHAR2-GETORDERInput>
<ns1:USER_NAME-VARCHAR2-IN>scott</ns1:USER_NAME-VARCHAR2-IN>
         <ns1:ORDER_ID-NUMBER-IN>300</ns1:ORDER_ID-NUMBER-IN>
      </ns1:SVARCHAR2-GETORDERInput>
   </env:Body>
</env:Envelope>

The ApacheBench command line will look like:

ab -A scott:tiger -H 'SOAPAction: "GETORDER"' -p /tmp/soap-post-func.txt -n {total_request} -c {concurrent_request} http://localhost:8080/orawsv/SCOTT/ORDERS_CALCULATOR/GETORDER

Where:

  • -A scott:tiger is the HTTP authorization information (XMLDB Native WS do not accept anonymous login)
  • -H ‘SOAPAction: “GETORDER” is a required HTTP header for SOAP
  • -p /tmp/soap-post-func.txt is file which have the POST XML message showed above
  • and the URL is a default URL used to execute Native SOAP WS in XMLDB

Do not forget grant XDB_WEBSERVICES and XDB_WEBSERVICES_OVER_HTTP roles to SCOTT, also register the orawsv Servlet.

** Testing with Apache mod_mem_cache.

One of the most important consequence of REST architecture is that is on top on HTTP protocol, so why not include for example reverse proxy in front of Oracle XMLDB Restlet adapter. On Linux you can try this configuration: Add this to /etc/httpd/modules.d/57_mod_mem_cache.conf file:

.....
<IfModule mod_cache.c>

    # CacheEnable - A cache type and partial URL prefix below which caching is enabled
    #CacheEnable mem /manual
    #CacheEnable fd /images
    CacheEnable mem /userapp
    CacheEnable mem /orawsv

</IfModule>
....

This will enable mod_mem_cache to any URL starting with /userapp/ directory, this directory will be retrieved using Apache mod_proxy. Edit /etc/httpd/modules.d/30_mod_proxy.conf adding these lines:

<IfModule mod_proxy.c>
....
    SetEnv proxy-nokeepalive 1
....
    ProxyPass /userapp/ http://localhost:8080/userapp/
    ProxyPassReverse /userapp/ http://localhost:8080/userapp/
    ProxyPass /orawsv/ http://localhost:8080/orawsv/
    ProxyPassReverse /orawsv/ http://localhost:8080/orawsv/
....
</IfModule>

This will redirect automatically any URL http://localhost:80/userapp/ to XMLDB http://localhost:8080/userapp/. Finally start Apache Web Server and test the URL http://localhost/userapp/users/scott/orders/300 note now we are using port 80, not port 8080. To boost your REST WS performance you can change, for example, the expiration header of the response, many WS can use this trick a typically example is a weather service which is updated every 30 minutes. In our User application this change can be injected at org.restlet.example.tutorial.OrderResource class method represent, for example:

    @Override
    public Representation represent(Variant variant) throws ResourceException {

        Representation result = null;


        if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
            result = new StringRepresentation("Order \"" + this.orderId  + "\" for user \"" + this.userName + "\"");
       }


        Date expirationDate = new Date(System.currentTimeMillis()+10000);

        result.setExpirationDate(expirationDate);
        return result;
    }

This small change represent for 100 request in User application a difference between: Requests per second: __49.65__ [#/sec] (mean) to Requests per second: __1407.90__ [#/sec] (mean) and 1 request to XMLDB instead of 100 request, reducing a lot a server workload. There is no change with our SOAP WS because POST messages are not cached by mod_mem_cache reverse proxy.

*** Testing performance with Apache JMeter

XMLDB Restlet Adapter can be tested with Apache JMeter, here some captures with the above REST and SOAP WS. Test plan used with Users Restlet example:

Thread Group -> Thread Properties
Number of Threads (users): 10

Ramp Up Period (in seconds): 0

Loop Count: 200

HTTP Request -> Web Server
Server Name or IP: localhost
HTTP Request -> HTTP Request
Protocol: http
Method: GET
Path: /userapp/users/scott/orders/300

Gaussian Random Timer -> Thread Delay Properties
Deviation (in milliseconds): 100.0
Constant Delay Offset (in milliseconds): 300

Test plan used with Users SOAP example:

Number of Threads (users): 10
Ramp Up Period (in seconds): 0
Loop Count: 200

SOAP/XML-RPC Request -> Web Server
Server Name or IP: localhost
HTTP Request -> HTTP Request
URL: http://localhost:8080/orawsv/SCOTT/ORDERS_CALCULATOR/GETORDER

Send SOAPAction: GETORDER
User KeepAlive: true
SOAP/XML-RPC Data Filename: /tmp/soap-post-func.txt


HTTP Header Manager -> Headers Stored in the Header Manager
Authorization: Basic c2NvdHQ6dGlnZXI=    (Base 64 encoding of scott/tiger)
Gaussian Random Timer -> Thread Delay Properties
Deviation (in milliseconds): 100.0
Constant Delay Offset (in milliseconds): 300
/tmp/soap-post-func.txt file content:

<?xml version = '1.0'?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://xmlns.oracle.com/orawsv/SCOTT/ORDERS_CALCULATOR/GETORDER">

   <env:Header/>
   <env:Body>
      <ns1:SVARCHAR2-GETORDERInput>

<ns1:USER_NAME-VARCHAR2-IN>scott</ns1:USER_NAME-VARCHAR2-IN>
         <ns1:ORDER_ID-NUMBER-IN>300</ns1:ORDER_ID-NUMBER-IN>
      </ns1:SVARCHAR2-GETORDERInput>
   </env:Body>
</env:Envelope>