2 Replies Latest reply on May 24, 2016 8:00 AM by darshan.garg

    CXF Endpoint not autowiring spring beans in JBoss 7.1.1

    skunjumohamed

      Hi,

       

      CXF endpoint is not properly integrated with spring. I am using JBoss AS 7.1.1 to deploy my cxf webservice integrated with spring. The log messages shows that both CXF and spring creates two different instances of the endpoint bean, and only the spring-created bean is autowired.

       

      The CXF created instance does not have the dependencies set and it is the one who listens to the client requests, and throws NPEs at dependencies when serving the client requests inside the WebMethods.

       

      I do not use the JBoss bundled CXF modules as dependencies, rather I am adding the CXF and dependent jars in the WB-INF lib and excluding the jboss-cxf modules in the jboss-deployment-structure.xml.

       

      The war file works perfectly in tomcat, but fails in JBoss.

       

      Here is the code..

       

      Service Interface..

      package test.cxf.service;
      
      import javax.jws.WebMethod;
      import javax.jws.WebParam;
      import javax.jws.WebService;
      
      
      @WebService(name = "EchoService", targetNamespace = "http://service.cxf.test/")
      public interface EchoService {
      
          @WebMethod(operationName = "echo", action = "urn:Echo")
          String echo(@WebParam(name = "message") String message);
      
      }
      
      

       

      Service implementation (Endpoint)

       

      package test.cxf.service;
      
      import javax.jws.WebMethod;
      import javax.jws.WebParam;
      import javax.jws.WebResult;
      import javax.jws.WebService;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.stereotype.Service;
      
      @Service("EchoService")
      @WebService(endpointInterface = "test.cxf.service.EchoService", serviceName = "EchoService")
      public class EchoServiceImpl implements EchoService {
      
          @Autowired
          private SystemInfoBean sysInfoBean;
      
          public EchoServiceImpl() {
              System.out.println(">>>>>>>>> " + this.getClass().getName() + "-constructed.");
          }
      
          @Value("Fooooooooooooo Baaaaaaaaaar")
          public void setTestInjection(String value) {
              System.out.println(">>>>>>>> Test Injection value = " + value);
          }
      
          @WebMethod
          public @WebResult(name="result") String echo(@WebParam(name="message") String message) {
              System.out.println(">>>>>>>> Inside " + this.getClass().getClass().getName() + "-echo - message = " + message);
              System.out.println(" Echoing " + message);
      
               // Next line throws NullPointerException since sysInfoBean is not injected in the instance created by @WebService annotation
              sysInfoBean.printOSInfo();
      
              return message;
          }
      
      }
      

       

      SystemInfoBean - A dependent bean

       

      package test.cxf.service;
      
      import java.lang.management.ManagementFactory;
      import java.lang.management.OperatingSystemMXBean;
      
      import org.springframework.stereotype.Component;
      
      @Component
      public class SystemInfoBean {
      
          public SystemInfoBean() {
              // TODO Auto-generated constructor stub
          }
      
          public void printOSInfo() {
              OperatingSystemMXBean osXBean = ManagementFactory.getOperatingSystemMXBean();
              System.out.println("============ Operating System Information=========");
              System.out.println("Operating System: " + osXBean.getName());
              System.out.println("Version: " + osXBean.getVersion());
              System.out.println("Architecture: " + osXBean.getArch());
              System.out.println("Available Processors: " + osXBean.getAvailableProcessors());
              System.out.println("==================================================");
          }
      
      }
      

       

       

      Spring configuration (beans.xml)

       

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:jaxws="http://cxf.apache.org/jaxws"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      
          <context:component-scan base-package="test.cxf.service" />
      
          <import resource="classpath:META-INF/cxf/cxf.xml" />
          <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
      
          <jaxws:endpoint id="echoservice" implementor="#EchoService"
           address="/services/echoservice">
              <jaxws:features>
                  <bean class="org.apache.cxf.feature.LoggingFeature" />
              </jaxws:features>
          </jaxws:endpoint>
      </beans>
      

       

       

      web.xml

       

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
        <display-name>JBossCXFWeb1</display-name>
        <servlet>
          <description>Apache CXF Endpoint</description>
          <display-name>cxf</display-name>
          <servlet-name>cxf</servlet-name>
          <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
          <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
          <servlet-name>cxf</servlet-name>
          <url-pattern>/services/*</url-pattern>
        </servlet-mapping>
        <session-config>
          <session-timeout>60</session-timeout>
        </session-config>
        <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>WEB-INF/beans.xml</param-value>
        </context-param>
        <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
      </web-app>
      

       

      I am excluding the JBoss CXF modules or implementation jars in the jboss-deployment-structure.xml

      <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
          <ear-subdeployments-isolated>true</ear-subdeployments-isolated>
          <sub-deployment name="JBossCXFWeb1.war">
              <exclusions>
                  <module name="org.apache.cfx" slot="main" />
              </exclusions>
              <dependencies>          
          </dependencies>
          </sub-deployment>    
      </jboss-deployment-structure>
      
      
      

       

       

      Thanks for any help in advance.

        • 1. Re: CXF Endpoint not autowiring spring beans in JBoss 7.1.1
          strickjb

          CXFServlet creates a new child ApplicationContext which it uses to load beans (e.g. Service Endpoints).  The nice thing about this is that it's a child context to your application's spring context (the parent)... meaning it's already aware of the rest of the beans in your application.

           

          The problem is the Implementor object is a member of the EndpointImpl class so it's bypassed for dependency injection (DI).  As you can see here:

           

          package org.apache.cxf.jaxws;
          public class EndpointImpl extends javax.xml.ws.Endpoint {
              ...
              private Object implementor;
              ...
          }
          

          The lightest weight solution that I could come up with was to get access to the child context within CXFServlet, iterate over the endpoints, and use the BeanFactory of the parent context to autowire dependencies in to the endpoint implementors.

           

          So without further ado, the SOLUTION is to use a custom subclass of CXFServlet:

           

          /**
          * Dependency Injection for CXF endpoints.
          *
          * @author John Strickler
          */
          public class CXFSpringServlet extends CXFServlet {
          
            private static final long serialVersionUID = 1L;
          
              @Override
              protected void loadBus(ServletConfig servletConfig) {
          
                  super.loadBus(servletConfig);
          
                  XmlWebApplicationContext ctx = getContext();
                  AutowireCapableBeanFactory beanFactory = ctx.getParent().getAutowireCapableBeanFactory();
          
                  Collection<Endpoint> endpoints = ctx.getBeansOfType(Endpoint.class).values();
          
                  for(Endpoint endpoint : endpoints) {
                      beanFactory.autowireBean(endpoint.getImplementor());
                  }
              }
          
              private XmlWebApplicationContext getContext() {
                  try {
                      Field field = CXFServlet.class.getDeclaredField("createdContext");
                      field.setAccessible(true);
                      return (XmlWebApplicationContext) field.get(this);
                  } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                      throw new RuntimeException("Unable to autowire endpoint impementors registered by CXFServlet.", e);
                  }
              }
          }
          

           

          Use it by replacing CXFServlet with CXFSpringServlet in your web.xml

           

          That's it, you now have dependency injection in to your service endpoints.

          • 2. Re: CXF Endpoint not autowiring spring beans in JBoss 7.1.1
            darshan.garg