Tuesday, April 29, 2014

Expiration times of L1 & L2 caches with Hazelcast for WSO2 products

Recently I had a doubt about how L1 & L2 caches work with WSO2 products. Then I went through the blog on WSO2 Multi-tenant Cache: JSR-107 (JCache) implementation based on Hazelcast by Azeez and got an idea about the caches and how they work.

Then I had a doubt about the expiration times of these two caches, whether they have separate cache expiration times or whether they share the same expiration time value. After having a discussion about this, found out that both caches are using the same expiration time value.

In Cabon 4.2.0 based products, which is using Hazelcast, the cache expiration time cannot be configured and right now, it is hard coded. The default timeout value used with our products is 15 minutes.

Friday, April 25, 2014

Retrieving availble HTTP methods from a back-end server through WSO2 ESB APIs

Assume you have a set of back-end servers that supports different combinations of HTTP methods and you have a need to retrieve which server supports what methods. This can be achieved using WSO2 ESB with the help of an API. Lets see how this can be done using a simple example.

Pre-requisites
  • Download the latest version of WSO2 ESB distribution (WSO2 ESB) to a preferred location on you file system.
  • Download an instance of WSO2 Application Server (WSO2 AS) distribution to host your web application. I will be using version 5.0.1 in this example.

Step 1 - Deploying the web application in WSO2 AS.

Extract the WSO2 AS distribution to a preferred location and start the server. Follow the instructions in the guide Deploying JAX-WS and JAX-RS Applications and deploy the sample web application.

Step 2 - Creating the API in WSO2 ESB

Now lets create an API pointing to the above web application to retrieve it's HTTP methods.

Follow the instructions given under Adding APIs in the Management Console and add an API with the below configuration.


<api xmlns="http://ws.apache.org/ns/synapse" name="CustomerAPI" context="/customer">
   <resource methods="POST GET DELETE PUT" url-mapping="/*">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:8280/proxyapi"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
   </resource>
</api>


Step 3 - Invoking the API to retrieve the available methods of the back-end service.

$ curl -v -X OPTIONS http://localhost:8280/customer/customerservice


When you invoke the above curl command, it would return you all the available methods as below.


* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
> OPTIONS /customer/customerservice HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8280
> Accept: */*
>
< HTTP/1.1 200 OK
< Max-Forwards: 1
< Host: 127.0.0.1:8280
< Allow: POST,GET,DELETE,PUT,OPTIONS,HEAD
< Accept: */*
< Date: Fri, 25 Apr 2014 13:09:10 GMT
< Server: WSO2-PassThrough-HTTP
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact


This is a simple scenario where there is only one layer between your back-end server and your client (which is the API). But assume that the message flows through another API/proxy service before reaching the back-end and still you need to get the methods allowed by the back-end server. To enable this, you can use the parameter Max-Forwards.

Assume a configuration as below.


<api xmlns="http://ws.apache.org/ns/synapse" name="CustomerAPI" context="/customer">
   <resource methods="DELETE POST PUT OPTIONS" url-mapping="/*">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:8280/proxyapi"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
   </resource>
</api>

<api xmlns="http://ws.apache.org/ns/synapse" name="ProxyAPI" context="/proxyapi">
   <resource methods="PUT OPTIONS" url-mapping="/*">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:10763/jaxrs_basic_44/services/customers"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
   </resource>
</api>



To retrieve the methods of the first API, you can specify the MAX-Forwards parameter as below.

$ curl -v -X OPTIONS http://localhost:8280/customer/customerservice -H "Max-Forwards: 0"

It would return the methods available of the first API which is CustomerAPI

* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
> OPTIONS /customer/customerservice HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8280
> Accept: */*
> Max-Forwards: 0
>
< HTTP/1.1 200 OK
< Max-Forwards: 0
< Host: localhost:8280
< Allow: POST, DELETE, PUT
< Content-Type: application/x-www-form-urlencoded; charset=UTF-8
< Accept: */*
< Date: Fri, 25 Apr 2014 12:17:02 GMT
< Server: WSO2-PassThrough-HTTP
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact


If you need to retrieve the methods of the second API, which is ProxyAPI, you need to give the command with Max-Forwards parameter set to 1. Then, it would return the methods allowed by that API only.

$ curl -v -X OPTIONS http://localhost:8280/customer/customerservice -H "Max-Forwards: 1"

The result would be as follows. It returns the available methods of the second API.

* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
< OPTIONS /customer/customerservice HTTP/1.1
< User-Agent: curl/7.29.0
< Host: localhost:8280
< Accept: */*
< Max-Forwards: 1
<
> HTTP/1.1 200 OK
> Max-Forwards: 0
> Host: localhost:8280
> Allow: PUT
> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
> Accept: */*
> Date: Fri, 25 Apr 2014 18:20:16 GMT
> Server: WSO2-PassThrough-HTTP
> Transfer-Encoding: chunked
>
* Connection #0 to host localhost left intact



If you need the HTTP methods of the actual back-end server, you need to call the first API using a command as shown below

curl -v -X OPTIONS http://localhost:8280/customer/customerservice -H "Max-Forwards: 2"

It would give the result as below with all the HTTP methods allowed by the back-end.

* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
< OPTIONS /customer/customerservice HTTP/1.1
< User-Agent: curl/7.29.0
< Host: localhost:8281
< Accept: */*
< Max-Forwards: 2
<
> HTTP/1.1 200 OK
> Allow: POST,GET,DELETE,PUT,OPTIONS,HEAD
> Content-Type: application/octet-stream
> Date: Fri, 25 Apr 2014 18:20:13 GMT
> Server: WSO2-PassThrough-HTTP
> Transfer-Encoding: chunked
>
* Connection #0 to host localhost left intact


Monday, April 21, 2014

Setting up a simple WSO2 AS cluster when custom keystores are used

When we setup cluster deployments, people are reluctant to use the default keystore that is shipped with WSO2 products due to security reasons. Therefore, we can use a different keystore within our products other than what is shipped by default (wso2carbon.jks).

Lets see how this is possible and what changes we need to do in addition to the normal cluster configuration we do.

NOTE: I'm demonstrating on how to setup a simple cluster with one WSO2 Application Server Manager node and one WSO2 ELB node.

1. Download a fresh WSO2 Application Server pack from the product page. 


2. Remove the default wso2carbon.jks that is shipped with the product. This file resides in $PRODUCT_HOME/repository/resources/security folder and drop the custom keystore that you created.

keytool -genkey -alias appserverks -keyalg RSA -keystore appserver.jks -storepass appserver


3. Change the following configuration files of the App server pack so that it refers the custom keystore file.
- $PRODUCT_HOME/repository/conf/carbon.xml
- $PRODUCT_HOME/repository/conf/identity.xml


4. Follow the instructions listed under the section Configuring the Manager Node and setup the Application server manager node.


5. Follow the instructions given in WSO2 Clustering & Deployment Guide and configure a WSO2 ELB as explained in Configuring the Load Balancer section.


6. Start the WSO2 ELB node


7. Start the WSO2 Application Server node & access the Management Console and it would throw an exception as below.



[2014-04-21 23:34:40,530] ERROR - TargetHandler I/O error: General SSLEngine problem javax.net.ssl.SSLHandshakeException: General SSLEngine problem at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1362) at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:513) at sun.security.ssl.SSLEngineImpl.writeAppRecord(SSLEngineImpl.java:1177) at sun.security.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:1149) at javax.net.ssl.SSLEngine.wrap(SSLEngine.java:469) at org.apache.http.nio.reactor.ssl.SSLIOSession.doWrap(SSLIOSession.java:220) at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:254) at org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:380) at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:118) at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:160) at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:342) at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:320) at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280) at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106) at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:604) at java.lang.Thread.run(Thread.java:724)
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1683) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:278) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868) at sun.security.ssl.Handshaker$1.run(Handshaker.java:808) at sun.security.ssl.Handshaker$1.run(Handshaker.java:806) at java.security.AccessController.doPrivileged(Native Method) at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1299) at org.apache.http.nio.reactor.ssl.SSLIOSession.doRunTask(SSLIOSession.java:238) at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:270) ... 9 more Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:138) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1328) ... 17 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ... 23 more

To overcome this issue, you need to import the public certificate of the custom keystore that you used at the App server node, to the $ELB_HOME/repository/resources/security/client-truststore.jks of the WSO2 ELB instance.

Extracting the public certificate
 
keytool -export -alias appserverks -keystore appserver.jks -file appserverks.cert


Importing the public certificate to the client-truststore.jks of the WSO2 ELB instance


keytool -import -file appserverks.cert -keystore client-truststore.jks -storepass wso2carbon -alias appserverks



Restart the WSO2 ELB instance and then the WSO2 Appserver node and then you will be able to access the Management Console successfully