DEV Community

binbingoloo
binbingoloo

Posted on

"No qualifying bean of type 'jakarta.xml.ws.WebServiceContext' available"

Description

We recently undertook a major technology stack upgrade, migrating from:

  • JDK 8JDK 21
  • Spring 3Spring 6.x
  • WebSphere (WAS)Tomcat 10
  • CXFCXF 4.x

During this migration, we encountered the following error:

No qualifying bean of type 'jakarta.xml.ws.WebServiceContext' available

Root Cause

After extensive investigation, we discovered that Spring 6 removed the built-in JAX-WS support that was previously available in Spring 5 and earlier versions. This change wasn't prominently documented in the Spring 6 migration guide, making it a hidden pitfall for teams upgrading legacy applications.

The issue occurs because:

  1. Spring's CommonAnnotationBeanPostProcessor tries to inject WebServiceContext as a regular Spring bean
  2. WebServiceContext is not a Spring-managed bean - it should be handled by the JAX-WS runtime (CXF in our case)
  3. Spring 6 no longer automatically excludes WebServiceContext from dependency injection

Research and References

During our investigation, we found several relevant resources:

Key insights from these resources:

Solution

For Spring Framework Applications (Our Case)

Since we're using Spring Framework without Spring Boot, we adapted the CxfJaxwsAutoConfiguration from Spring Boot for our application.

Here's how Spring Boot handles it:

@Configuration
@ConditionalOnClass({ Resource.class, WebServiceContext.class })
public class CxfJaxwsAutoConfiguration {
    @Bean
    static BeanFactoryPostProcessor jaxwsBeanFactoryPostProcessor() {
        return new BeanFactoryPostProcessor() {
            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                final var beans = beanFactory.getBeansOfType(CommonAnnotationBeanPostProcessor.class, true, false);
                // The {@code javax.xml.ws.WebServiceContext} interface should be ignored since it will 
                // be resolved by the CXF's JAX-WS runtime.
                beans.forEach((name, bean) -> bean.ignoreResourceType(WebServiceContext.class.getName()));
            }
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Our Implementation

We created a custom configuration class for our Spring Framework application:

package com.yourcompany.config;

import jakarta.annotation.Resource;
import jakarta.xml.ws.WebServiceContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CxfJaxwsConfiguration {

    @Bean
    public static BeanFactoryPostProcessor jaxwsBeanFactoryPostProcessor() {
        return new BeanFactoryPostProcessor() {
            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                // Check if the required classes are available
                if (!isClassPresent("jakarta.annotation.Resource") || 
                    !isClassPresent("jakarta.xml.ws.WebServiceContext")) {
                    return;
                }

                final var beans = beanFactory.getBeansOfType(CommonAnnotationBeanPostProcessor.class, true, false);
                // Tell Spring to ignore WebServiceContext for dependency injection
                // CXF's JAX-WS runtime will handle it instead
                beans.forEach((name, bean) -> bean.ignoreResourceType(WebServiceContext.class.getName()));
            }
        };
    }

    private static boolean isClassPresent(String className) {
        try {
            Class.forName(className, false, CxfJaxwsConfiguration.class.getClassLoader());
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

How It Works

  1. The BeanFactoryPostProcessor runs early in the Spring context initialization
  2. It finds all CommonAnnotationBeanPostProcessor instances
  3. It instructs them to ignore WebServiceContext when processing @Resource annotations
  4. This allows CXF's JAX-WS runtime to properly inject WebServiceContext instead of Spring trying to find it as a bean

I hope this helps others facing the same issue during their Spring 6 migration!

Top comments (0)