Search code examples
javacdiweldjboss-weldjava-ee-8

WELD - exclude application scoped bean from scanning / beans.xml


I have the following @ApplicationScoped bean:

@ApplicationScoped
public class ServiceProducer {

    private static final Logger logger = LoggerFactory.getLogger(ServiceProducer.class);

    @Default
    @Produces
    @PersistenceContext(unitName = "nemo")
    private EntityManager nemoEntityManager;

    private CacheManager cacheManager;

    @PostConstruct
    void init() {
        try {
            cacheManager = Caching.getCachingProvider().getCacheManager(
                    Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("/ehcache.xml")).toURI(),
                    Thread.currentThread().getContextClassLoader());
        } catch (URISyntaxException e) {
            logger.error(e.getMessage());
        }

    }


    @Produces
    public CacheManager produceCacheManager() {
        return cacheManager;
    }

The above class is contained in a common / shared module of my web applications. This is good for production, however I need an alternative for integration test, which are done with Cucumber with wildspike integration.

By configuring beans.xml, unfortunately I cannot disable the bean above, I tried with configuring beans.xml in this way:

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
    http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all">

    <scan>
        <exclude name="it.myapp.ecommerce.commons.resource.ServiceProducer"></exclude>
    </scan>
    

however nothing changes. Is it due because the class belongs from a dependency ?


Solution

  • You basically have two options.

    Firstly, and this is the usual go-to recommended option, it's the @Alternative plus @Priority combination on the secondary bean that you declare and only add in test environment. That way, it will be picked up for testing but not in production. Note that it is imperative that you only add the alternative in the test environment else it would be picked up at all times. You can, for instance, declare this bean directly under test project (or its sources) so that the main (production) JAR doesn't contain it. Furthermore, you don't need to alter beans.xml to achieve the above. Alternatives can be enabled either via @Priority or via beans.xml; the former is global, the latter is per-bean-archive.

    Second option is a CDI extension that will observe ProcessAnnotatedType<ServiceProducer> event and will veto() the proper class. That means the given class won't be picked up as bean. This can be done conditionally based on some parameter or whatever else you have at hand. Given you do the above, you then need to somehow add the new test implementation of ServiceProducer. You can again do that by declaring it in test sources (no need for alternative here) but you can also use the very same extension to register a synthetic bean in AfterBeanDiscovery via BeanConfigurator SPI.

    While CDI extension are pretty powerful, I'd recommend going with the first option that's way simpler and more understandable once you read up a bit on how alternatives and their enablement works.