Greetings dear Stackoverflow users, I have been lately in lots of pain with one specific problem with axis2 web services with Spring framework. I have read lots of different guides and read different forums but found people with the same problems but with no solutions. Basically ended up holding the monitor with both of my hands and yelling "What did you find out BudapestHacker938?". Anyway my axis2 web service class needs Spring beans and therefore they are autowired inside the web service class. Everything works so well inside the jetty server where I have servletContext. Just define needed listeners in web.xml and it works. Such a bliss. But unfortunately all good things come to the end in some point, for me, the devil is CICS environment inside of mainframe. There is no servletcontext like in Jetty/Tomcat, luckily it still has axis2 support. So according to the different user-guides I decided to archive my web-service into .aar and added it under the services folder. Axis2 folder structure is the following:
When I am building this .aar archive then I am also generating my own wsdl, not using axis2 inbuilt wsdl generator which according to services.xml generates the services out of the given class (when I am running the axis2server, not using because doesn't like JAX-WS annotations as far as I know). To initialize Spring framework, I needed to write little SpringInit class which initializes Spring beans. Unfortunately it also for some reason initializes my web-service class according to its annotations and then occupies the main port(suspect that SpringInit intializes by its own the web service class since it is also defined as a Spring bean and SpringInit extends Axis2 class ServiceLifeCycle) and I get JVM BIND exception where it is stating that address is already in use. I would like to have the service built up according to the wsdl which is stored inside of the WSDL rather than generate new one, because I have various environments: 1) local machine - Jetty 2) mainframe. Anyway I give an insight to my services.xml:
<service name="Absence" class="org.services.SpringInit">
<description>
random description
</description>
<parameter name="ServiceTCCL">composite</parameter>
<parameter name="useOriginalwsdl" locked="false">true</parameter>
<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringAppContextAwareObjectSupplier</parameter>
<parameter name="ServiceClass">org.services.Absence</parameter>
<parameter name="SpringBeanName">absence</parameter>
<parameter name="SpringContextLocation">META-INF/applicationContextAar.xml</parameter>
</service>
Spring applicationContextAar.xml, little bit refactored it for dear Stack community:
<beans>
<bean id="applicationContext" class="org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder" />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:tcp://localhost/~/devDb" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="absence" class="org.services.Absence"></bean>
<bean id="jtemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="ds"></constructor-arg>
</bean>
<bean id="datasetFactory" class="org.vsam.DataSetFactory"></bean>
<bean id="dataManagerFactory" class="org.datamanager.DataManagerFactory"></bean>
<bean id="absenceFactory" class="org.services.AbsenceFactory"></bean>
<bean id="h2Database" class="org.dataset.H2Database"><constructor-arg ref="jtemplate"></constructor-arg>
</bean>
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"></bean>
</beans>
My SpringInit class looks something like that:
public class SpringInit implements ServiceLifeCycle {
public void startUp(ConfigurationContext ignore, AxisService service) {
try {
ClassLoader classLoader = service.getClassLoader();
ClassPathXmlApplicationContext appCtx = new
ClassPathXmlApplicationContext(new String[] {"applicationContextAar.xml"}, false);
appCtx.setClassLoader(classLoader);
appCtx.refresh();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void shutDown(ConfigurationContext ctxIgnore, AxisService ignore) {}
}
Now we are moving to org.services.Absence.class, it is an ordinary JAX-WS web-service class with following header (contains JAX-WS annotations):
@WebService(name = "AbsenceService", serviceName = "Absence", portName = "Absence",
targetNamespace = "http://www.something.org/Absence")
public class Absence extends ServiceHandlerBase {
@Autowired
private AbsenceFactory absenceFactory;
@Autowired
private DataManagerFactory dataManagerFactory;
@Autowired
private DataSetFactory dataSetFactory;
...
}
Containing methods like that:
@WebMethod
@WebResult(name = "AbsenceResponse")
public SearchAbsenceRecordsResponse invokeSearchAbsenceRecords(
@WebParam ServiceRequest request,
@WebParam SearchAbsenceRecordsRequest absenceRequest) {...}
One alternative is to add "servicejars" folder into "repository" folder and populate it with absence.jar which has all its dependencies in the sub-folder "lib". Axis2 then automatically runs absense.jar since it has JAX-WS annotation. But in there when I call out the web-service for example with SOAP-UI, it doesn't have Spring initialized since I don't know how to initialize Spring in that solution. Maybe someone has any expertise about that.
TL;DR
How do I get my Spring beans initialized in manner that it doesn't start the services in the web service class according to the annotation and would rather build up services according to the wsdl?
You are welcome to ask questions.
How I initialized Spring inside of CICS without servletcontext?
Basically until today the SOAP web services have been published through servicejars which means into the repository folder has been created "servicejars" folder which cointains jars which have been built from the web service classes. "servicejars" subfolder "lib" contains all the dependencies which web service jars need.
At first I learnt from the web(Axis2 homepage, there was an instruction about axis2 and spring integration) for initializing Spring in Axis2 web service I need .aar archive and SpringInit service defined in services.xml. But this brought lots of problems since having old architecture built on jaxws and jaxb there was a huge need for refactoring the web services layer. Axis2 tolerated jaxws annotations only with "servicejars" solution. Initing Spring with SpringInit class meant that it initializes Spring beans according to the application context. This now runs web service bean(absence bean in previous post) as a separate web service and occupied 8080 port, when time came for the web service creation according to WSDL I got an error "JVM bind address already in use". So after that I figured I should create the service according to the absence Spring bean and let axis2server generate the WSDL, but axis2server didn't like jaxws annotation and even without them it didn't like my jaxb DTOs.
Therefore, I decided to drop .aar architecture and went back to the "servicejars" architecture. Unfortunately in there I didn't have services.xml support, to define the potential SpringInit service.
Since jaxws web services are the only entrypoints then I decided do the following (initialize Spring beans in the web service layer):
@WebService(name = "AbsenceService", serviceName = "Absence", portName = "Absence",
targetNamespace = "http://www.something.org/Absence")
public class Absence extends ServiceHandlerBase {
private static AbsenceFactory absenceFactory;
private static DataManagerFactory dataManagerFactory;
private static DataSetFactory dataSetFactory;
static {
try {
ClassPathXmlApplicationContext appCtx = new
ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"}, false);
appCtx.refresh();
absenceFactory = (AbsenceFactory) appCtx.getBean("absenceFactory", AbsenceFactory.class);
dataManagerFactory = (DataManagerFactory) appCtx.getBean("dataManagerFactory", DataManagerFactory.class);
dataSetFactory = (DataSetFactory) appCtx.getBean("datasetFactory", DataSetFactory.class);
} catch (Exception ex) {
ex.printStackTrace();
}
}
...
}
As you can see when this class is being called out, it will initialize applicationcontext and since it is static, all the spring beans will stay in the memory until the end(when service is closed). In other classes autowiring works perfectly, no need to get these beans wired manually.
In the end, I didn't find the possiblity to initialize Spring in the matter as I hoped through .aar architecture, but I found a work around with the guidance of a senior programmer. Huge thanks to him! And now the possible solution is visible for all StackOverFlow users.
EDIT:
In applicationContext.xml I had:
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"/>
Tries to create web services with Absence.class(absence bean). Removed it since I can in local machine as well use pre-generated WSDL with Jetty (originally was used for creating web service in the local machine, like I said before, I have local development environment and it should be also compatible with CICS, now it is solved).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With