This document is a guide on how to use Axis2 with the Spring Framework
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param>Next we will show two examples of Spring's /WEB-INF/applicationContext.xml referenced in the web.xml listener - one using a ServletContext, and one without.
<?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, but to Spring, its just another bean that has dependencies --> <bean id="springAwareService" class="spring.SpringAwareService"> <property name="myBean" ref="myBean"/> </bean> <!-- just another bean / interface with a wired implementation, that's injected by Spring into the Web Service --> <bean id="myBean" class="spring.MyBeanImpl"> <property name="val" value="Spring, emerge thyself" /> </bean> </beans>If the service is running in a Servlet Container, i.e., Axis2 will be able to get a hold of the ServletContext, the services.xml for the example would be using SpringServletContextObjectSupplier such as:
<service name="SpringAwareService"> <description> simple spring example </description> <parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter> <parameter name="SpringBeanName">springAwareService</parameter> <operation name="getValue"> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> </operation> </service>While the above example uses RawXMLINOutMessageReceiver as its messageReceiver class, all Message Receivers are currently supported, as would be any Message Receiver that extends org.apache.axis2.receivers.AbstractMessageReceiver .
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Configure spring to give a hook to axis2 without a ServletContext --> <bean id="applicationContext" class="org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder" /> <!-- Axis2 Web Service, but to Spring, its just another bean that has dependencies --> <bean id="springAwareService" class="spring.SpringAwareService"> <property name="myBean" ref="myBean" /> </bean> <!-- just another bean with a wired implementation, that's injected by Spring into the Web Service --> <bean id="myBean" class="spring.MyBeanImpl"> <property name="val" value="Spring, emerge thyself" /> </bean> </beans>If the service is not running in a Servlet Container, i.e., Axis2 will not be able to get a hold of ServletContext or you prefer not to, the services.xml for the example will be using SpringAppContextAwareObjectSupplier such as:
<service name="SpringAwareService"> <description> simple spring example </description> <parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringAppContextAwareObjectSupplier</parameter> <parameter name="SpringBeanName">springAwareService</parameter> <operation name="getValue"> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> </operation> </service>
While the above example uses RawXMLINOutMessageReceiver as its messageReceiver class, all Message Receivers are currently supported, as would be any Message Receiver that extends org.apache.axis2.receivers.AbstractMessageReceiver .
In an environment without a ServletContext, one way you could load the applicationContext.xml file is in a place that will be run once. Upon start-up, execute the following:
import org.springframework.context.support.ClassPathXmlApplicationContext; public void createSpringAppCtx(ClassLoader cl) throws Exception { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] {Constants.MY_PATH + "spring/applicationContext.xml"}, false); ctx.setClassLoader(cl); ctx.refresh();}
From here, it's just standard Axis2 coding. Only now the service has Spring wiring capabilities. The implementation is the same whether using either SpringServletContextObjectSupplier or SpringAppContextAwareObjectSupplier. The service is as follows:
package spring; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axiom.om.OMText; public class SpringAwareService { private MyBean myBean = null; //spring 'injects' this implementation public void setMyBean(MyBean myBean) { this.myBean = myBean; } // The web service public OMElement getValue(OMElement ignore) { OMFactory factory= OMAbstractFactory.getOMFactory(); OMNamespace payloadNs= factory.createOMNamespace( "http://springExample.org/example1", "example1"); OMElement payload = factory.createOMElement("string", payloadNs); OMText response = factory.createOMText(this.myBean.emerge()); payload.addChild(response); return payload; } }
For those who are new to Spring, one of the ideas is that you program an Interface, as the implementation is pluggable. This idea is referenced in the Spring config file above. Below is the interface:
package spring; /** Interface for Spring aware Bean */ public interface MyBean { String emerge(); }
Here's the implementation:
/** Spring wired implementation */ public class MyBeanImpl implements MyBean { String str = null; // spring 'injects' this value public void setVal(String s) { str = s; } // web service gets this value public String emerge() { return str; } }
Lastly here's the client - not really necessary for the example, other than for completeness:
package client; import java.io.StringWriter; import javax.xml.stream.XMLOutputFactory; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; public class TestClient { private static EndpointReference targetEPR = new EndpointReference( "http://localhost:8080/axis2/services/SpringAwareService"); /** * Simple axis2 client. * * @param args Main */ public static void main(String[] args) { try { OMFactory factory = OMAbstractFactory.getOMFactory(); OMNamespace omNs = factory.createOMNamespace( "http://springExample.org/example1", "example1"); OMElement method = factory.createOMElement("getValue", omNs); OMElement value = factory.createOMElement("Text", omNs); value.addChild(factory.createOMText(value, "Some String ")); method.addChild(value); ServiceClient serviceClient = new ServiceClient(); Options options = new Options(); serviceClient.setOptions(options); options.setTo(targetEPR); //Blocking invocation OMElement result = serviceClient.sendReceive(method); StringWriter writer = new StringWriter(); result.serialize(XMLOutputFactory.newInstance() .createXMLStreamWriter(writer)); writer.flush(); System.out.println("Response: " + writer.toString()); } catch (Exception ex) { ex.printStackTrace(); } } }
The examples above assume that both the spring framework .jar and the axis2-spring-*.jar are under WEB-INF/lib. In such a case, the classes shown in this tutorial need to be placed in a JAR under WEB-INF/lib. In this example the JAR layout is:
./mySpring.jar ./META-INF ./META-INF/MANIFEST.MF ./spring ./spring/MyBean.class ./spring/MyBeanImpl.class ./spring/SpringAwareService.class
Since all the user classes are in mySpring.jar in this example, the AAR merely contains the services.xml file:
./springExample.aar ./META-INF ./META-INF/MANIFEST.MF ./META-INF/services.xml
To run this example, make sure you have the axis2-spring*.jar that comes from the axis2-std-*-bin distro in the server side WEB-INF/lib, as well as the appropriate Spring jar - most will use the full spring.jar, but the minimum requirements are spring-core, spring-beans, spring-context, and spring-web. When running the client, you should see this output:
Response: <example1:string xmlns:example1="http://springExample.org/example1" xmlns:tns="http://ws.apache.org/axis2">Spring, emerge thyself</example1:string>
Axis2 users frequently want to run Spring inside the AAR. Here we show you how it is done. There are four points to be aware of:
(A) You need to configure Spring to use the Axis2 Service Classloader. See the Known issues running Spring inside the AAR area.
(B) It's up to you to load Spring, though we give an example below.
(C) For reasons such as classloader isolation, the SpringAppContextAwareObjectSupplier is the best choice.
(D) The springframework .jar and axis2-spring-*.jar will be placed inside the AAR under the lib directory. Please move the axis2-spring-*.jar from WEB-INF/lib to inside the AAR, as shown below - it will not work otherwise.
./springExample.aar ./META-INF ./META-INF/MANIFEST.MF ./META-INF/services.xml ./applicationContext.xml ./lib ./lib/axis2-spring-1.3.jar ./lib/spring.jar ./spring ./spring/MyBean.class ./spring/MyBeanImpl.class ./spring/SpringAwareService.class ./spring/SpringInit.class
<!-- Configure spring to give a hook to axis2 without a ServletContext --> <bean id="applicationContext" class="org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder" />One way to initialize Spring inside the AAR is to use the org.apache.axis2.engine.ServiceLifeCycle interface. Here we give an example:
package spring; import org.apache.axis2.engine.ServiceLifeCycle; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.description.AxisService; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringInit implements ServiceLifeCycle { /** * This will be called during the deployement time of the service. * irrespective * of the service scope this method will be called */ public void startUp(ConfigurationContext ignore, AxisService service) { try { System.out.println("Starting spring init"); ClassLoader classLoader = service.getClassLoader(); ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"}, false); appCtx.setClassLoader(classLoader); appCtx.refresh(); System.out.println("spring loaded"); } catch (Exception ex) { ex.printStackTrace(); } } /** * This will be called during the system shut down time. * irrespective * of the service scope this method will be called */ public void shutDown(ConfigurationContext ctxIgnore, AxisService ignore) { } }
Here's the services.xml that now includes SpringInit and the SpringAwareService shown above. There is also the composite parameter which is needed when loading Spring in the AAR - see the Known issues running Spring inside the AAR area.
<serviceGroup> <!-- Invoke SpringInit on server startup and shutdown --> <service name="SpringAwareService" class="spring.SpringInit"> <description> simple spring example - inside the AAR </description> <!-- need the TCCL param when using spring inside the AAR --> <parameter name="ServiceTCCL">composite</parameter> <parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringAppContextAwareObjectSupplier</parameter> <parameter name="SpringBeanName">springAwareService</parameter> <operation name="getValue"> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> </operation> </service> </serviceGroup>
A common requirement is to run Hibernate along with Spring with Axis2 web services. It is easier to run Hibernate as well as Spring outside the AAR as shown in the ServletContext example, ie, place the Spring and Hibernate jars in WEB-INF/lib and the hibernate config files under WEB-INF/classes. With that advisement, Spring provides an API that allows Spring to load Hibernate under the contraints of an AAR.
Hibernate by default looks for its config files in the classpath. By running Hibernate inside the AAR, Hibernate won't be able to find its config files. The way to get around this limitation is either to expand the AAR or place the hibernate config files in a specific directory under WEB-INF/classes - and then use Spring's setMappingDirectoryLocations for several options.
By placing Spring into DEBUG mode you can look at the logs to see where Spring will look for your jar / class locations. Use the wildcards in the following example to list your mapping locations as shown:
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="mappingLocations"> <value>classpath*:**/MyEntity.hbm.xml</value> </property> ... </bean>