javaspringcxfjboss7.xjbossws

how to bootstrap Spring in a JbossWS-CXF web service deployed in jboss AS 7.2


TLDR at the bottom:

As per the JBossWS-cxf user guide, for a web service, the web.xml should contain the following

    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" 
    version="2.4">
        <servlet>
            <servlet-name>MyWebService</servlet-name>
            <servlet-class>com.sgb.MyWebService</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>MyWebService</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>
    </web-app>

Jboss also expects a descriptor file named jboss-cxf.xml in WEB-INF directory (instead of cxf.xml) which should contain the jaxws:endpoint tag like so:

            <beans xmlns='http://www.springframework.org/schema/beans'
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 
        xmlns:beans='http://www.springframework.org/schema/beans'
        xmlns:jaxws='http://cxf.apache.org/jaxws'
        xsi:schemaLocation='http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://cxf.apache.org/jaxws >

        <bean id="MyWebService" class="com.sgb.MyWebService" />

        <jaxws:endpoint id="POJOEndpoint" implementor="#MyWebService" wsdlLocation="WEB-INF/wsdl/XYZ.wsdl" address="/warfilename">
            <jaxws:invoker>
                <bean class="org.jboss.wsf.stack.cxf.InvokerJSE" />
            </jaxws:invoker>
        </jaxws:endpoint>
    </beans>

I then create my service implementation class thusly:

    package com.sgb;

    @javax.jws.WebService(... ... ... )
    public class MyWebService implements IMyWebService
    {
        public CreateResponse create(CreateRequest request)
        {
            ... ... ... <-- an instance of createService is created
            return createService.serve(request)
        }
    }

So far so good. It works fine.

However, as per Spring's reference documentation, the convenient way to instantiate an application context for web applications is by adding a ContextLoaderListener in the web.xml like so.

    <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>

So, I could add the above in my web.xml and then annotate MyWebService class with @Service and make sure the package is set up for component-scaning. And it should become a spring managed bean too.

Problem is, it doesn't. JbossWS-CXF seems to be instantiating MyWebService due to which the dependencies are not injecte resulting in a nullpointer.

I am able to get the applicationContext programmatically using ClassPathXmlApplicationContext("/WEB-INF/applicationContext.xml") And then inject/create my dependencies using appContext.getBean()

But I was hoping to inject/autowire the dependencies directly using annotations instead.

TLDR:

What I currently have is this. (This bean is created by jboss and not spring):

    @javax.jws.WebService(... ... ... )
    public class MyWebService implements IMyWebService
    {
        private ApplicationContext appContext;
        public MyWebService(){
            appContext = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext-ws.xml");
        }

        public CreateResponse create(CreateRequest request)
        {
                            *** Use getBean() here to get my dependency. ***
            IXyzService createService = appContext.getBean("createService",IXyzService.class);
            return createService.serve(request)
        }
    }

What I want is this:

    @javax.jws.WebService(... ... ... )
    @Service    <-- <-- <-- ** This is Spring managed bean**
    public class MyWebService implements IMyWebService
    {
        @Resource <-- <-- <-- **Dependency Injected by Spring**
        IXyzService createService;

        public CreateResponse create(CreateRequest request)
        {
            return createService.serve(request)
        }
    }

What is the best way to accomplish this ???


Solution

  • I found out a few days back that this is indeed possible, so editing my previous answer. The magic glue is this:

    @PostConstruct
    public void postConstruct(){
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }
    

    So to summarize, the following pieces are needed:

    1) Load the spring context via web.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/META-INF/spring/appContext.xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    

    2) Annotate a method with @PostConstruct in the class that implements the interface generated by CXF like so:

    @javax.jws.WebService(... ... ... )
    public class MyWebService implements IMyWebService
    {
        @Resource <-- <-- <-- **Dependency Injected by Spring**
        IXyzService createService;
    
    
        @PostConstruct
        public void postConstruct(){
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        }
    
        public CreateResponse create(CreateRequest request)
        {
            return createService.serve(request)
        }
    }
    

    The above info is courtesy of this link:How to initialize Spring Framework inside CXF JAX-WS service Hope this helps...

    === PREVIOUS ANSWER ===

    Well. Turns out, it cannot be done - The version I have now working is the correct way - as far as I can tell.

    Explanation:

    Apache CXF can be used with OR WITHOUT Spring.

    JbossCXF (in AS 7.x or EAP 6.x) uses CXF as the default web service stack WITHOUT Spring. So when Jboss invokes the class that implements the web service interface (MyWebService above in my example annotated with @WebService(), the spring container is NOT yet initiated.... as the configuration in web.xml or jboss-cxf.xml does not allow for it.

    So, the Spring container needs to be manually started inside the SEI impl class due to which the service class itself cannot be a spring managed bean (obviously).

    Hence, the need to instantiate service beans inside the method using the getBean() method.

    Once the service Beans are instantiated, their dependencies are automatically managed by the spring container as they are all now spring managed bean.

    Hope this helps someone.

    SGB