utorak, 12. veljače 2008.

Java and Web Services Security: Axis2- part 1


For the first article in this series i have chosen Apache Axis2. Today, Axis2 is one of the most popular Java Web Service Api's, its battle tested, robust and as you will see quite simple to use. At the moment of writing of this article current version was 2.1.3, which is therefore the one used for building all examples you can find here. More information about Axis2 can be found at its homepage.

1. Environment and IDE setting up
First step is to download Axis2 binary distribution (here), after downloading it follow instructions at Axis2 web site on how to install it.
Web Services built with Axis2 can be run either in "Stand alone Axis2 Server" or within some Application Server, for my environment I have opted for the later and used Tomcat Application server (version 5.5.20). In this scenario, Axis2 function as a Java Web Application in which you deploy your web services as service jars, jars with axis2 specific structure and with extenstion ".aar".
Now its time to open up your favorite IDE and Set up a project. My favorite weapon of choice is MyEclipse IDE, whichis is derivate of popular Eclipse IDE. MyEclipse comes with lots of tools, many of which reassemble those usually find in eclipse WTP.

After building Axis2 war, as is described in installation instructions, instead of depolying to Application server i have created new WebProject in my IDE, and unzped war in its WebContent folder! This way I ended upa with a web application I could easily manage (add new, change existing files... ) through my IDE. After unzipping files and refreshing, my Project looked like this:

By no means I hold that the approach described above, is the recommended way to organize your Axis2 project. Everyone is entitled to its own way of doing things, but since there are several ways you can do this I though it to be good idea to share my approach in dealing with Axis2 and my Java IDE.
The main reason I have taken this approach is because I wanted to have Spring IoC capabilities available in my Web Services, of which more will be said later.

2. Service Creation
Creating service from WSDL is pretty simple with Axis2. First, take Shipping Service WSDL from initial post of this series of articles. Create new wsdl folder in your project and put shipping-service.wsdl file there. Then, open shell and position yourself in root folder of your project. Now execute following line:
%AXIS2_HOME%\bin\wsdl2java.bat -uri file:///{rootOfYourProject}/wsdl/shipping-services.wsdl -o {rootOfYourProject} -d adb -s -wv 1.1 -ss -sd -ssi
Do not forget to substitute value for {rootOfYourProject} with correct path of your own Project. If all goes well after wsdl2java script finishes its work your project should look like this:

As you can see wsdl2java script has generated, based on definitions from wsdl, Java files for both model and skeleton of your service. In addition it has also created folder resources/, containing necessary axis2 configuration files and build.xml with helper tasks used to build Axis2 service jar (aar).
Most important file to notice is: ShippingServiceSkeleton, that is file you should modify and put your bussines logic in it.
I have decided not to put bussines logic in it, but rather to create separate Java classes which implement bussines logic and is called from ShippingServiceSkeleton.
To do so I have created new simple interface:
ShippingOrderManager (which represents entry point to bussines implementation) and Dummy implementation of this interface (ShippingOrderManagerDummyImpl).

Following listing give content of both files:


package net.codah.sm.bussines;

import net.codah.sm.ws.schemas.ShippingOrderRequest;
import net.codah.sm.ws.schemas.ShippingOrderResponse;

public interface ShippingOrderManager {

ShippingOrderResponse processOrder(ShippingOrderRequest shippingRequest);
}

[ShippingOrderManager.java]

package net.codah.sm.bussines;

import java.math.BigDecimal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.codah.sm.ws.schemas.AmmountType;
import net.codah.sm.ws.schemas.ShippingOrderRequest;
import net.codah.sm.ws.schemas.ShippingOrderResponse;

public class ShippingOrderManagerDummyImpl implements ShippingOrderManager {

protected final Log logger = LogFactory.getLog(ShippingOrderManager.class);

public ShippingOrderResponse processOrder(ShippingOrderRequest shippingOrderRequest) {
logger.debug("RECIEVED: " + shippingOrderRequest);
ShippingOrderResponse response = new ShippingOrderResponse();
response.setShippingId(new Long(System.currentTimeMillis()).toString());
response.setShippingAgencyId("SomeAgencyID");
AmmountType cost = new AmmountType();
cost.setAmmount(new BigDecimal(43.59));
cost.setCurrencyCode("EUR");
response.setCost(cost);

return response;
}

}

[ShippingOrderManagerDummyImpl]
And finally ShippingServiceSkeleton.java now look like this:


package net.codah.sm.ws.services;

import net.codah.sm.bussines.ShippingOrderManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* ShippingServiceSkeleton java skeleton for the axisService
*/
public class ShippingServiceSkeleton implements ShippingServiceSkeletonInterface {

protected final Log logger = LogFactory.getLog(ShippingServiceSkeleton.class);

protected ShippingOrderManager shippingOrderManager;
/**
* Auto generated method signature
* @param shippingOrderRequest0
*/
public net.codah.sm.ws.schemas.ShippingOrderResponse processOrder(
net.codah.sm.ws.schemas.ShippingOrderRequest shippingOrderRequest0) {

return shippingOrderManager.processOrder(shippingOrderRequest0);
}

public void setShippingOrderManager(ShippingOrderManager shippingOrderManager) {
this.shippingOrderManager = shippingOrderManager;
}
}

As can be seen from the code above ShippingServiceSkeleton has setter for ShippingOrderManager, in order to wire it up with ShippingOrderManagerDummyImpl I have set up Axis2 to cooperate with spring.

3. Axis2 and Spring Configuration
Few years ago I have hooked up to Spring, and today can not any more imagine Java development without it. So I decided to integrate Spring IoC with my Axis2 Web Service. There are several ways how you can integrate Spring into Axis2, detailed instructions can be found here,
I have chosen the simples approach, one which relies on web.xml to load up Application Context over listener.
First put spring.jar file into WEB-INF/lib fodler, then modify web.xml file by adding following lines:


<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Then create applicationContex.xml like the one bellow and put it to WEB-INF/ folder.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<!-- Axis2 Web Service -->
<bean id="shippingService" class="net.codah.sm.ws.services.ShippingServiceSkeleton">
<property name="shippingOrderManager" ref="shippingOrderManager"/>
</bean>

<bean id="shippingOrderManager" class="net.codah.sm.bussines.ShippingOrderManagerDummyImpl">
</bean>

</beans>

Now open file service.xml from resources folder. This is a file created by wsdl2java script and is used by Axis2 to describe the service. Now change it so it looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- This file was auto-generated from WSDL -->
<!-- by the Apache Axis2 version: 1.3 Built on : Aug 10, 2007 (04:45:47 LKT) -->
<serviceGroup>
<service name="ShippingService">
<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
<parameter name="SpringBeanName">shippingService</parameter>
<parameter name="useOriginalwsdl">true</parameter>
<parameter name="modifyUserWSDLPortAddress">true</parameter>
<operation name="processOrder" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://codah.net/ShippingOrderRequest</actionMapping>
<outputActionMapping>http://codah.net/sm/ws/services/ShippingServicesPortType/ShippingOrderResponse</outputActionMapping>
</operation>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="net.codah.sm.ws.services.ShippingServiceMessageReceiverInOut"/>
</messageReceivers>
</service>
</serviceGroup>

Two key lines in the configuration above are:

<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
<parameter name="SpringBeanName">shippingService</parameter>

which tell axis to take Service object from Spring Application context.

Now that we have Spring appliction context set up, and integrated with axis the only remaining thing to do is to change ant script that builds service aar file which is deployed in Axis2 Web Application, in WEB-INF/services/ folder.

4. Change Ant build
The problem is that default ant task "jar.server" which is used to create aar file will compile into it also the Java classes, which since they are in src/ folder now we do not need there. All we need in ShippingService.arr file now are the following files:
./ShippingService.aar
./META-INF
./META-INF/MANIFEST.MF
./META-INF/services.xml
To do so all you need to do is exclude java classes from jar creation prcess, locata and change following lines in build.xml file (jar.server task):

<jar destfile="${lib}/${name}.aar">
<fileset excludes="**/Test.class" dir="${classes}"/>
</jar>

so they would look like this:

<jar destfile="${lib}/${name}.aar">
<fileset excludes="**/*.class" dir="${classes}"/>
</jar>

Now execute ant task "jar.server" and drop newly created ShippingService.arr file from the build/lib/ folder to the WEB-INF/services/ folder and you should have fully functional service.
Now its time to add some security to this service, we will do this in next article of this series, until then...

1 komentar:

Tarek Mamdouh kaže...

Great old article, but did u continue this series or not yet?