Friday, January 25, 2013

Setting HTTP basic security in Maven Jetty plug-in

Recently I had to test HTTP basic security for one of my web application project, just to save my time I didn't want to use any standalone server during development instead I tried to configure Maven Jetty plug-in to support HTTP basic security. Here I have given the procedure which I followed.

1.) Configure Maven Jetty plugin in project pom.xml file.

It's required to add a UserRealm under plug-in configuration section. Jetty provides number of in-built UserRealms, here I used HashUserRealm for simplicity which use in-memory HashMaps to store users and roles.

 <plugin>  
                     <groupId>org.mortbay.jetty</groupId>  
                     <artifactId>maven-jetty-plugin</artifactId>  
                     <configuration>  
                          <scanIntervalSeconds>3</scanIntervalSeconds>  
                          <webAppConfig>  
                               <contextPath>/</contextPath>  
                          </webAppConfig>  
                          <userRealms>  
                               <userRealm implementation="org.mortbay.jetty.security.HashUserRealm">  
                                    <name>basic security</name>  
                                    <config>jetty-users.properties</config>  
                               </userRealm>  
                          </userRealms>  
                     </configuration>  
                </plugin>  
           </plugins>  



Then add a property file called jetty-users.properties which contains user names, passwords and user roles.

 sagara=sagara,ADMIN  



2.) Configure web.xml file of the web application.

In this example we allow users with ADMIN role to access any URL within the application. 

 <security-constraint>  
           <display-name>authorizedUsers</display-name>  
           <web-resource-collection>  
                <web-resource-name>ALL URLs</web-resource-name>  
                <url-pattern>/*</url-pattern>  
           </web-resource-collection>  
           <auth-constraint>  
                <role-name>ADMIN</role-name>  
           </auth-constraint>  
      </security-constraint>  
      <login-config>  
           <auth-method>BASIC</auth-method>  
           <realm-name>basic security</realm-name>  
      </login-config>  
      <security-role>  
           <description>administrator access</description>  
           <role-name>ADMIN</role-name>  
      </security-role>  

Friday, May 04, 2012

Release of Axis2 1.6.2 and Sandesha2 1.6.2 , Rampart 1.6.2

The Apache Axis2 team is pleased to announce the general availability of the following releases:


  • Axis2 1.6.2
  • Sandesha2 1.6.2
  • Rampart 1.6.2


Apache Axis2 is a complete re-design and re-write of the widely used Apache Axis engine and is a more efficient, more scalable, more
modular and more XML-oriented Web services framework. It is carefully designed to support the easy addition of plug-in "modules" that extend
its functionality for features such as security and reliability.

Apache Rampart is an Axis2 module that implements the specifications in the WS-Security stack.

Apache Sandesha2 provides WS-ReliableMessaging support for Axis2.

Axis2 1.6.2 is a maintenance release that contains more than 45 fixes.

Rampart 1.6.2 is a maintenance release containing number of bug fixes and compatible with the Axis2 1.6.2 release.

Sandesha2 1.6.2 is a maintenance release that is compatible with the Axis2 1.6.2 release.

The new versions are available for download at the following locations:

http://axis.apache.org/axis2/java/core/download.cgi
http://axis.apache.org/axis2/java/rampart/download/1.6.2/download.cgi
http://axis.apache.org/axis2/java/sandesha/download.cgi

As always, we welcome any and all feedback at:

java-dev@axis.apache.org - for developer-related questions/concerns
java-user@axis.apache.org - for general questions, usage, etc.


NOTE  - Axis2 1.6.2 compatible with Axis2 Transports 1.0.0 version


Thursday, May 03, 2012

Code generation and WSDL exposed over HTTPS


In one of my previous post I explained how to use WSDL2JAVA tool  behind a proxy together with basic authentication. Another such  useful scenario is code generation for a WSDL which exposed over HTTPS protocol.  In this case we have to provide key store details to the WSDL2JAVA tool. Basically it expect following two Java system properties.

 javax.net.ssl.trustStore  
 javax.net.ssl.trustStorePassword  
Once you know those properties you can use WSDL2JAVA tool as follows.
    
 wsdl2java.sh -uri https://localhost:8443/services/SimpleService?wsdl -Djavax.net.ssl.trustStore=path/keystorename.keystore -Djavax.net.ssl.trustStorePassword=keypassword  

Wednesday, May 02, 2012

How easy to test your web service over HTTPS


I have seen many times people having issues with testing web services over HTTPS. This is not an unexpected behavior because number of improvements have been introduced from Axis2 1.5 release for HTTPS transport. With older versions it is possible to use HTTPS transport with Simple HTTPServer and some people already familiar with it too, but recent Axis2 releases no longer support for this option and it is compulsory to use Servlet transport  in order to enable HTTPS.

Above modifications doesn't effect much on production systems because it's always recommend to use an Application server for production systems. When it come to development  stage this changes required to use a Servlet container to test HTTPS services, Apache Tomcat is one of the good choice for this.

In this post I will discuss how to use Maven Jetty Plug-in to test Axis2 HTTPS transport very easily, all you need is to follow few steps that I have given below.

Step -1  Configure Axis2 for HTTPS transport.

It is required to define AxisServletListener in your axis2.xml for HTTPS transport, in case  if you want to use both HTTP ad HTTPS it is possible to define AxisServletListener as two entries with two ports this guide provide more details about this. If you don't have axis2.xml file copy it from binary distribution and replace existing  " transportReceiver " with following entries.

  <transportReceiver name="http"  
     class="org.apache.axis2.transport.http.AxisServletListener">  
     <parameter name="port">8080</parameter>  
   </transportReceiver>  
   <transportReceiver name="https"  
     class="org.apache.axis2.transport.http.AxisServletListener">  
     <parameter name="port">8443</parameter>  
   </transportReceiver>   

Step - 2  Generate KeyStore.

Usually this is kind of a time wasting task but fortunately you can use keytool-maven-plugin to auto-generate keystore in each run. The only required step is add following entries into your POM file and it will save your time a lot.

       <plugin>  
         <groupId>org.codehaus.mojo</groupId>  
         <artifactId>keytool-maven-plugin</artifactId>  
         <executions>  
           <execution>  
             <phase>generate-resources</phase>  
             <id>clean</id>  
             <goals>  
               <goal>clean</goal>  
             </goals>  
           </execution>  
           <execution>  
             <phase>generate-resources</phase>  
             <id>genkey</id>  
             <goals>  
               <goal>genkey</goal>  
             </goals>  
           </execution>  
         </executions>  
         <configuration>  
           <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>  
           <dname>cn=localhost</dname>  
           <keypass>axis2key</keypass>  
           <storepass>axis2key</storepass>  
           <alias>axis2key</alias>  
           <keyalg>RSA</keyalg>  
         </configuration>  
       </plugin>  


Spacial Note - Above approach is not an replacement for production server configurations . It is highly recommend to follow standard procedures to configure production servers for HTTPS.

Step - 3  Configure and Run the service.

Now you need to add SSL Connectors for the Jetty configuration by adding following entries.

  <plugin>  
         <groupId>org.mortbay.jetty</groupId>  
         <artifactId>maven-jetty-plugin</artifactId>  
         <configuration>  
           <webAppConfig>  
             <contextPath>/</contextPath>  
           </webAppConfig>  
           <connectors>  
             <connector  
               implementation="org.mortbay.jetty.security.SslSocketConnector">  
               <port>8443</port>  
               <maxIdleTime>60000</maxIdleTime>  
               <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>  
               <password>axis2key</password>  
               <keyPassword>axis2key</keyPassword>  
             </connector>  
             <connector  
               implementation="org.mortbay.jetty.nio.SelectChannelConnector">  
               <port>8080</port>  
               <maxIdleTime>60000</maxIdleTime>  
             </connector>  
           </connectors>  
         </configuration>  
       </plugin>  

Run "mvn Jetty:run " this will start jetty server with both HTTP and HTTPS connectors . Now you can access to the  WSDL content over HTTPS. According to above example you can fine WSDL file here.


Step- 04 - Configure  Client.

Since now you can access to the WSDL file you are free to use any of your client side preferences to create a WS client , you may generate stub or possible to write service/operation clients. Before you invoke your service you need to perform one more extra step, that is provide your key store details to Java run time . In simply we can use above generated keystore with our client too.

Add following two lines before you invoke your client.

 System.setProperty("javax.net.ssl.trustStore","[location]/jetty-ssl.keystore");  
 System.setProperty("javax.net.ssl.trustStorePassword", "axis2key");  


You can download full source code for this sample from here.








Tuesday, May 01, 2012

Axis2 clustering on Tomcat


In this post I will discuss how to setup a Axis2 cluster using two Tomcat servers.  It's not my intention to describe Axis2 cluster architecture or cluster configuration language, if you need such details refer provided references at the end of this post. Axis2 cluster implementation is based on pure API which you can implement using any Java multicast communication framework, by default Axis2 provide a cluster implemantation based on Tomcat Cluster Communication Module also known as Apache Tribes. Following diagram illustrate the design we are going to discuss in this post but I skip load balancer setup for simplicity but in real world scenario you could use load balancer such as Apache2 server or WSO2 Load Balancer that support for more advanced options.


Pre-requirements   

1. Apache Tomcat server. ( Version 7.x.x preferable. )
2. Apache Axis2 WAR distribution (Version 1.6.2 preferable.)


Here I use same machine to setup two Tomcat server instances hence it's required to change server configuration of one instance. Let's say Node-1 having default configuration and Node-2 having custom configuration. Open the server.xml file and change server port, port nubers of HTTP and AJP Connectors as follows.    

Node-1 (default configuration)
 <Server port="8005" shutdown="SHUTDOWN">  
 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />  
 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />  

Node-2 
 <Server port="9005" shutdown="SHUTDOWN">  
 <Connector port="9090" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="9443" />  
 <Connector port="9009" protocol="AJP/1.3" redirectPort="9443" />  

Now install Axis2 WAR distribution on both server instances and stop both servers to edit axis2 configuration file. Open axis2.xml file and edit cluster settings as follows. 

 Node -1

     <parameter name="AvoidInitiation">false</parameter>   
     <parameter name="domain">sample.cluster.domain</parameter>  
     <parameter name="mcastBindAddress">127.0.0.1</parameter>  
     <parameter name="localMemberHost">127.0.0.1</parameter>  
     <parameter name="localMemberPort">4000</parameter>  


  Node -2 

     <parameter name="AvoidInitiation">false</parameter>   
     <parameter name="domain">sample.cluster.domain</parameter>  
     <parameter name="mcastBindAddress">127.0.0.1</parameter>  
     <parameter name="localMemberHost">127.0.0.1</parameter>  
     <parameter name="localMemberPort">4001</parameter>   

Note that in a real network setup it's required to edit mcastBindAddress, localMemberHost settings in addition to  localMemberPort value but in my local machine only localMemberPort has changed.  After this has done start the Node -1, if there is no issue in your setup you could able to see log messages as follows on node-1 server console. 

 [INFO] Initializing cluster...  
 [INFO] Cluster domain: sample.cluster.domain  
 [INFO] Using multicast based membership management scheme  
 Apr 30, 2012 6:37:13 PM org.apache.catalina.tribes.transport.ReceiverBase bind  
 INFO: Receiver Server Socket bound to:/127.0.0.1:4000  
 Apr 30, 2012 6:37:13 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket  
 INFO: Setting cluster mcast soTimeout to 500  
 Apr 30, 2012 6:37:13 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:4  
 Apr 30, 2012 6:37:14 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Done sleeping, membership established, start level:4  
 Apr 30, 2012 6:37:14 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:8  
 Apr 30, 2012 6:37:15 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Done sleeping, membership established, start level:8  
 [WARN] Local member advertising its IP address as 127.0.0.1. Remote members will not be able to connect to this member.  
 [INFO] Local Member 127.0.0.1:4000(sample.cluster.domain)  
 [INFO] No members in current cluster  
 [INFO] Cluster initialization completed.  
 Apr 30, 2012 6:37:15 PM org.apache.catalina.startup.HostConfig deployDirectory  
 INFO: Deploying web application directory /home/sagara/dev/servers/axis2-clustering/tomcat/node1/webapps/ROOT  
 Apr 30, 2012 6:37:15 PM org.apache.coyote.AbstractProtocol start  
 INFO: Starting ProtocolHandler ["http-bio-8080"]  
 Apr 30, 2012 6:37:15 PM org.apache.coyote.AbstractProtocol start  
 INFO: Starting ProtocolHandler ["ajp-bio-8009"]  
 Apr 30, 2012 6:37:15 PM org.apache.catalina.startup.Catalina start  
 INFO: Server startup in 3068 ms  


Now start the Node -2 and monitor log messages on Node-2 console 
 [INFO] Initializing cluster...  
 [INFO] Cluster domain: sample.cluster.domain  
 [INFO] Using multicast based membership management scheme  
 Apr 30, 2012 6:37:50 PM org.apache.catalina.tribes.transport.ReceiverBase bind  
 INFO: Receiver Server Socket bound to:/127.0.0.1:4001  
 Apr 30, 2012 6:37:50 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket  
 INFO: Setting cluster mcast soTimeout to 500  
 Apr 30, 2012 6:37:50 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:4  
 [INFO] New member 127.0.0.1:4000(sample.cluster.domain ) joined cluster.  
 Apr 30, 2012 6:37:51 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Done sleeping, membership established, start level:4  
 Apr 30, 2012 6:37:51 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:8  
 Apr 30, 2012 6:37:51 PM org.apache.catalina.tribes.io.BufferPool getBufferPool  
 INFO: Created a buffer pool with max size:104857600 bytes of type:org.apache.catalina.tribes.io.BufferPool15Impl  
 Apr 30, 2012 6:37:52 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers  
 INFO: Done sleeping, membership established, start level:8  
 [WARN] Local member advertising its IP address as 127.0.0.1. Remote members will not be able to connect to this member.  
 [INFO] Local Member 127.0.0.1:4001(sample.cluster.domain )  
 [INFO] Members of current cluster  
 [INFO] Member1 127.0.0.1:4000(sample.cluster.domain )  
 [INFO] Trying to send initialization request to 127.0.0.1:4000(sample.cluster.domain )  
 [INFO] Received configuration initialization message  
 [INFO] Trying to send initialization request to 127.0.0.1:4000(sample.cluster.domain )  
 [INFO] Received state initialization message  
 [INFO] Cluster initialization completed.  
 Apr 30, 2012 6:37:52 PM org.apache.catalina.startup.HostConfig deployDirectory  
 INFO: Deploying web application directory /home/sagara/dev/servers/axis2-clustering/tomcat/node2/webapps/ROOT  
 Apr 30, 2012 6:37:52 PM org.apache.coyote.AbstractProtocol start  
 INFO: Starting ProtocolHandler ["http-bio-9090"]  
 Apr 30, 2012 6:37:52 PM org.apache.coyote.AbstractProtocol start  
 INFO: Starting ProtocolHandler ["ajp-bio-9009"]  
 Apr 30, 2012 6:37:52 PM org.apache.catalina.startup.Catalina start  
 INFO: Server startup in 13334 ms   


Additionally now you should able to see following log messages on Node-1 server console. 
 Apr 30, 2012 6:37:50 PM org.apache.catalina.tribes.io.BufferPool getBufferPool  
 INFO: Created a buffer pool with max size:104857600 bytes of type:org.apache.catalina.tribes.io.BufferPool15Impl  
 [INFO] New member 127.0.0.1:4001(sample.cluster.domain) joined cluster.  
 [INFO] Received GetConfigurationCommand initialization request message from 127.0.0.1:4001(sample.cluster.domain)  
 [INFO] Received GetStateCommand initialization request message from 127.0.0.1:4001(sample.cluster.domain) 


If you have followed me up to this point you have successfully setup a Axis2 cluster with two nodes. Now you can deploy any cluster aware web service on this cluster. For testing purposes let's write following POJO service and will deploy on both servers. I have given service code and service.xml  below. 

 package sample;  
 import org.apache.axis2.context.MessageContext;  
 public class Count {  
   public int count() {  
     int count;  
     MessageContext mc = MessageContext.getCurrentMessageContext();  
     Object ob = mc.getConfigurationContext().getProperty("count");  
     if (ob == null) {  
       count = 1;  
     } else {  
       count = (Integer) ob;  
       count++;  
     }  
     mc.getConfigurationContext().setProperty("count", count);  
     return count;  
   }  
 }  

Note that we use  ConfigurationContext to store our count values, ConfigurationContext is replicate among cluster members.


 <service name="count">   
   <parameter name="ServiceClass">sample.Count</parameter>  
   <operation name="count">  
   <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" />  
   </operation>  
 </service>  

In order to test our cluster aware service we need a Web service client, for the simplicity I will use a browser and Axis2 REST support.  Since we skip the load balancer setup following two different URLs can be use to invoke services . Once you invokes services on both servers you can notice that the count value is shared among two Axis2 instances clearly.

 http://localhost:8080/axis2/services/count/count  
 http://localhost:9090/axis2/services/count/count   

References


Saturday, April 21, 2012

Axis2 JMS transport and ActiveMQ

In this post I will describe how to configure Axis2 JMS transport properly and test web services through JMS transport. For the simplicity I will use Apache ActiveMQ as the JMS server and will use ActiveMQ admin console as a JMS client to send and receive messages. In my future posts I will describe how to use ActiveMQ Maven plug-in with Axis2 and also how to test Axis2 JMS transports with few other implementations such as Apache QPID and WSO2 Message Broker.

First, If you don't have a ActiveMQ installation already, download the binary distribution from here and start the ActiveMQ server. If it's started properly make sure you can access to admin console through the following URL, we will use this admin console as a JMS client.
 http://0.0.0.0:8161/admin  
The next step is add requited dependencies and configure JMS transport in Axis2. Here I use Axis2 Simple HTTP server but same steps can be used with any other application server too.

(1) Add following dependencies to the "lib" directory of Axis2.

1.  axis2-transport-jms-1.x.x  (axis2-transport-jms-1.7.0-SNAPSHOT.jar or axis2-transport-jms-1.7.0 )
2.  axis2-transport-base-1.x.x (axis2-transport-base-1.7.0-SNAPSHOT.jar or axis2-transport-base- 1.7.0)  
3. geronimo-j2ee-management_1.1_spec-1.0.x  (geronimo-j2ee-management_1.1_spec-1.0.1.jar)
4. geronimo-jms_1.1_spec-1.1.x  (geronimo-jms_1.1_spec-1.1.1.jar)
5. activemq-core-5.1.x (activemq-core-5.1.0.jar)
6. coomons-io-2.1 (coomons-io-2.1.jar)

You can find above dependencies on Axis2 transport project here or you can find latest development snapshots from Apache build server here.

(2) Like in any other Axis2 transport your next task is to configure particular transport through the axis2.xml file by adding underline TransportListener  and  TransportSender. For the JMS transport you can add following settings.

 <transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">  
   <parameter name="default" locked="false">             
     <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>  
     <parameter name="java.naming.provider.url" locked="false">tcp://localhost:61616</parameter>      
     <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>  
   </parameter>  
 </transportReceiver>  

 <transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender">  
   <parameter name="default" locked="false">             
     <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>  
     <parameter name="java.naming.provider.url" locked="false">tcp://localhost:61616</parameter>      
     <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>  
   </parameter>  
 </transportSender>  

Axis2 Transport user guide provide advanced configuration details such as separate configuration for JMS Queue and Topic etc.

(3) Start Axis2 server, If JMS transport is configured properly it is possible to see log message about JMS transport as follows.
 [INFO] JMS Sender started  
 [INFO] JMS ConnectionFactory : default initialized  
 [INFO] JMS Transport Sender initialized...  
 ..................  
 [INFO] JMS ConnectionFactory : default initialized  
 [INFO] JMS Transport Receiver/Listener initialized...  
 [INFO] Listening on port 8080  
 [INFO] JMS listener started  
 [INFO] Task manager for service : Version [re-]initialized  
 [INFO] Started to listen on destination : Version of type Queue for service Version  
 [INFO] Task manager for service : CalculatorService [re-]initialized  
 [INFO] Started to listen on destination : CalculatorService of type Queue for service CalculatorService  
 [INFO] Task manager for service : mtomService1Axis [re-]initialized  
 [INFO] Started to listen on destination : mtomService1Axis of type Queue for service mtomService1Axis  
 [INFO] [SimpleAxisServer] Started  
 [SimpleAxisServer] Started  

Now you have configured Axis2 JMS transport properly and it's possible to use any JMS client to invoke web services deployed on Axis2.  Let's try to invoke getVersion operation on Version web service through ActiveMQ admin console. Again in a browser go to the ActiveMQ admin console through the http://0.0.0.0:8161/admin URL, now  you have to select "Send" tab to reach the send wizard.  This wizard expect 3 basic inputs from you as input message, JMS destination ( in this case destination queue) and reply-to location. By looking at Axis2 server startup logs you can find hint about JMS destination bind to each web service, you can ensure availability of this JMS destination in ActiveMQ console too.
  [INFO] Started to listen on destination : Version of type Queue for service Version  

For our test scenario let's use following properties.
  •  Destination - Version
  •  Reply-To    - VersionResponse ( This destination is not available on ActiveMQ at this point, it will be created when the repose messages arrive to the JMS server)
  • In put message - Use following message
  •   - Add a random number
 <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:axis="http://axisversion.sample">  
   <soap:Header/>  
   <soap:Body>  
    <axis:getVersion/>  
   </soap:Body>  
 </soap:Envelope>  



Now you can see the response message by browsing "VersionResponse" Queue as follows.


Friday, April 13, 2012

How to use Axis2 WSDL2JAVA with proxy authentication

Securing WSDL using basic authentication is a common practice for number of enterprise web service providers. Also in some business domains it's required to secure various WSDL URLs among business partners so that partners can access service contracts according to their business agreements by providing a user name and a password.  As the most popular Java web service stack lot of people keep asking to enable basic  authentication to WSDL2JAVA tool so that they can access WSDL files secured through basic authentication.

Now you can use following two options to specify proxy user name and password.

 -http-proxy-user [user name]      - Proxy user name for basic authentication.  
 -http-proxy-password [password]   - Proxy password for basic authentication.  

The following examples show how to use above option in command line.

 wsdl2java.sh -http-proxy-user user-name -http-proxy-password password -uri http://localhost/axis2/services/Version?wsdl  

 wsdl2java.sh -http-proxy-user user-name -http-proxy-password password -wv 2.0 -uri http://localhost/axis2/services/Version?wsdl2   

Also these two options supported in Wsdl2Java Maven plug-in too.

 axis2.wsdl2code.http-proxy-user  - User name for proxy server.  
 axis2.wsdl2code.http-proxy-password - Password for proxy server.