Problema: Web services JAX-WS não recebem injeção de dependência de beans do Spring quando executadas no Glassfish 3.1.X #

A injeção de dependência de beans do Spring em web services JAX-WS utilizando SpringBeanAutowiringSupport e @Autowired não ocorre quando a aplicação é implantada na versão 3.1 e superiores do Glassfish. A causa para a falha é que, a partir da versão 3.1, o Glassfish alterou o deploy dos webservices, inicializando o Metro (webservices stack) antes da aplicação. Assim, ao implantar um web service antes do Spring ser inicializado, não há beans registrados e suas referências não são injetadas nos endpoints.

Para solucionar o problema, deve-se usar uma extensão da implementação padrão da JAX-WS fornecida pelo próprio projeto Glassfish/Metro/JAX-WS Commons, que possibilita unificar a configuração dos web services e do Spring. Essa API é a JAX-WS Spring Extension (jaxws-spring-1.8.jar), e sua documentação está disponível em http://jax-ws-commons.java.net/spring/. Também é necessário habilitar no Spring a configuração por anotação, ou seja, permitir que o Spring procure classes da aplicação declaradas com @Autowired, @Service, @Repository, etc, para que a injeção de dependência continue a ocorrer nos web services que possuem propriedades anotadas com @Autowired.

Duas issues foram abertas no JIRA e concluídas entre dezembro/2012 e janeiro/2013, voltadas ao SNU. A SNU-146 tratou esse problema, e paralelamente a SNU-147 estudar o uso do plugin do Glassfish Embedded 3.1.X para o Maven.

Configuração da integração JAX-WS, Spring e Glassfish 3.1.X #

Para o caso do SNU, não foi necessário alterar código de classes. Portanto, os web services continuam com as anotações JAX-WS e do Spring, como no exemplo abaixo:

@WebService(serviceName = "numeracaoUnicaWS")
public class NumeracaoUnicaWebServiceImpl extends SpringBeanAutowiringSupport 
       implements NumeracaoUnicaWebService {
    @SuppressWarnings("unused")
    private static final long serialVersionUID = 1657540877779355351L;
    static Logger app = Logger.getLogger(NumeracaoUnicaWebServiceImpl.class.getName());


    @Autowired
    @Qualifier("numeracaoUnicaService")
    private NumeracaoUnicaService numeracaoUnicaService;
    @Autowired
    @Qualifier("unidadeOrigemService")
    private UnidadeOrigemService unidadeOrigemService;
    @Autowired
    @Qualifier("serventiaDomusService")
    private ServentiaDomusService serventiaDomusService;


...
 } 

Também não é necessário configurar o plugin do Glassfish Embedded para que a integração entre JAX-WS e Spring ocorra no ambiente de desenvolvimento. Os passos abaixo garantem que a aplicação seja construída e executada de maneira compatível ao Glassfish e ao Jetty.

Os passos para a configurar a aplicação para solucionar o problema descrito acima são:

1. Incluir a dependência da JAX-WS Spring Extension no pom.xml #

As exclusões são necessárias para que a aplicação também execute no ambiente de desenvolvimento com o Jetty:

<dependency>
    <groupId>org.jvnet.jax-ws-commons.spring</groupId>
    <artifactId>jaxws-spring</artifactId>
    <version>1.8</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.jws</groupId>
            <artifactId>jsr181-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.xml.soap</groupId>
            <artifactId>saaj-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.xml.messaging.saaj</groupId>
            <artifactId>saaj-impl</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.xml.stream.buffer</groupId>
            <artifactId>streambuffer</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.xml.stream</groupId>
            <artifactId>sjsxp</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.org.apache.xml.internal</groupId>
            <artifactId>resolver</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.jvnet.staxex</groupId>
            <artifactId>stax-ex</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

2. Alterar a declaração do servlet em web.xml #

Substituir a declaração do servlet padrão da JAX-WS, com.sun.xml.ws.transport.http.servlet.WSServlet, pelo servlet da nova biblioteca:

<servlet>
    <description>Servlet da extensão da jax-ws commons para gerenciamento dos web services diretamente pelo Spring</description>
    <servlet-name>wsservlet</servlet-name>
    <servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>wsservlet</servlet-name>
    <url-pattern>/webservices/meuServicoWS</url-pattern>
</servlet-mapping>
<!-- declarar aqui os demais mapeamentos para o servlet, se necessário -->

O listener com.sun.xml.ws.transport.http.servlet.WSServletContextListener não é mais necessário, e portanto sua declaração deve ser retirada do web.xml.

3. Habilitar a Configuração por Anotação e declarar os endpoints no Spring em application-context.xml #

A Annotation-Based Configuration (http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config) deve ser habilitada no application-context.xml. Observe a inclusão do namespace context.

Os webservices são declarados agora também no aplication-context.xml. Observe a inclusão dos namespaces wss e ws.

Observação: não é mais necessário manter no arquivo sun-jaxws.xml os endpoints declarados no application-context.xml. O arquivo sun-jaxws.xml pode ser excluído do projeto se todos os endpoints nele declarados forem migrados para o application-context.xml.

<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:ws="http://jax-ws.dev.java.net/spring/core"
	xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-2.5.xsd
		http://www.springframework.org/schema/util 
		http://www.springframework.org/schema/util/spring-util-2.5.xsd
		http://jax-ws.dev.java.net/spring/core
		http://jax-ws.dev.java.net/spring/core.xsd
		http://jax-ws.dev.java.net/spring/servlet
		http://jax-ws.dev.java.net/spring/servlet.xsd">

	<!-- Indica ao Spring que a configuração por anotação está habilitada, 
		 o que faz com que beans anotados com, por exemplo @Autowired,
		 sejam reconhecidos e tenham dependências injetadas. -->
	<context:annotation-config/>
	
	<!-- Web services da aplicação -->
	<wss:binding url="/webservices/meuServicoWS"> <!-- mesmo caminho do mapeamento do servlet em web.xml -->
	    <wss:service>
	        <ws:service bean="#meuServicoWS"/>
	    </wss:service>
	</wss:binding>
	<bean id="meuServicoWS" class="gov.tjpr.webservices.impl.MeuServicoWebServiceImpl"/>
	
</bean>

Considerações finais #

Esta configuração foi determinada a partir de um estudo realizado para o SNU. Paralelamente, foi determinada a configuração do plugin do Glassfish Embedded 3.1.2.2 para o Maven.

O SNU apresentou mais algumas condições, como por exemplo o uso de clients que dependem de versões mais antigas da JAX-WS (2.1, 2.0). Para tratar este problema, caso a aplicação também dependa de clients compilados com versões antigas da JAX-WS, dois jars devem ser endossados: jaxb-api-2.2.4.jar e jaxws-api-2.2.8.jar. Os dois jars devem ser copiados das para jdk/jre/lib/endorsed/.