News - tagged as "draft"

Programmatic lookup improvements

2016-5-18   api , draft   Martin Kouba

javax.enterprise.inject.Instance is sometimes invaluable companion. For example, Instance allows you to handle unsatisfied and ambiguous dependencies gracefully. E.g. it’s possible to avoid unnecessary deployment problems:

interface OrderProcessor {
      void process(Order order);
      int getPriority();
    }
    
    class OrderService {
    
      @Inject
      Instance<OrderProcessor> instance;
    
      void create(Order order) {
        if (!instance.isUnsatisfied() && !instance.isAmbiguous()) {
            instance.get().process(order);
        } else {
            // Log a warning or throw an exception
        }
      }
    }

It is less known that Instance extends Iterable and so it allows to iterate through contextual references of beans with the specified combination of required type and qualifiers. This might be useful if the set of beans satisfying the given type and qualifiers is not known beforehand. We may also need to resolve ambiguities manually, e.g. inspect all the instances and choose only those matching our needs.

@ApplicationScoped
    class OrderService {
    
      @Inject
      @Any
      Instance<OrderProcessor> instance;
    
      void create(Order order) {
        for (OrderProcessor processor : instance) {
            if (processor.getPriority() > 10) {
                processor.process(order);
            }
        }
      }
    }

Another interesting use case might be selecting exactly one implementation:

@ApplicationScoped
    class OrderService {
    
      @Inject
      @Any
      Instance<OrderProcessor> instance;
    
      void create(Order order) {
        List<OrderProcessor> processors = new ArrayList<>();
        for (OrderProcessor processor : instance) {
            processors.add(processor);
        }
        Collections.sort(processors, Comparator.<OrderProcessor> comparingInt(p -> p.getPriority()).reversed());
        // Use the processor with highest priority
        processors.get(0).ping();
      }
    }

This works nice. But we have to pay extra attention to the scopes. If a Processor implementation is @Dependent a similar usage results in memory leaks. What’s the reason? The Processor instance is the dependent object of Instance<OrderProcessor> which is the dependent object of OrderService. And so each Instance.get() will produce a new Processor bound to the lifecycle of the OrderService. To avoid the leak we should always call Instance.destroy() method in similar cases. However, Instance.destroy() always destroys the underlying contextual instance! Even if you pass e.g. a client proxy of an @ApplicationScoped bean. This is not always desirable. Currently, it’s quite complicated to detect a dependent bean inside the loop.

To extend the possibilities the Weld team is experimenting with org.jboss.weld.inject.WeldInstance - an enhanced version of javax.enterprise.inject.Instance. There are three new methods we find useful. The first one - getHandler() - allows to obtain a contextual reference handler which not only holds the contextual reference but also allows to inspect the metadata of the relevant bean and to destroy the underlying contextual instance. Moreover, the handler implements AutoCloseable:

import org.jboss.weld.inject.WeldInstance;
    
    class Foo {
    
      @Inject
      WeldInstance<Bar> instance;
    
      void doWork() {
        try (Handler<Bar> bar = instance.getHandler()) {
            bar.get().doBusiness();
            // Note that Bar will be automatically destroyed at the end of the try-with-resources statement
        }
    
        Handler<Bar> bar = instance.getHandler()
        bar.get().doBusiness();
        // Calls Instance.destroy()
        bar.destroy();
      }
    
    }

The next method - handlerIterator() - returns an iterator over contextual reference handlers. This might be useful if you need more control over contextual references inside the loop:

@ApplicationScoped
    class OrderService {
    
      @Inject
      @Any
      WeldInstance<OrderProcessor> instance;
    
      void create(Order order) {
        for (Iterator<Handler<OrderProcessor>> iterator = instance.handlerIterator(); iterator.hasNext();) {
                Handler<OrderProcessor> processor = iterator.next();
                processor.get().process(order);
                if (processor.getBean().getScope().equals(Dependent.class)) {
                    // Destroy only dependent processors
                    processor.destroy();
                }
            }
      }
    }

The last one is just a convenient method - isResolvable() - a replacement for !isUnsatisfied() && !isAmbiguous() which is the expression most users are interested in:

class OrderService {
    
      @Inject
      Instance<OrderProcessor> instance;
    
      void create(Order order) {
        if (instance.isResolvable()) {
            instance.get().process(order);
        } else {
            // Log a warning or throw an exception
        }
      }
    }

Weld team is considering adding org.jboss.weld.inject.WeldInstance to the Weld API (2.4 and 3.0). WeldInstance would be automatically available in Weld SE and Weld Servlet where the Weld API is always on the class path. It would be also available in Weld-powered EE containers - in this case, users would have to compile their application against the Weld API and exclude the Weld API artifact from the deployment (e.g. use provided scope in Maven).

See also WELD-2151 and the work in progress: https://github.com/mkouba/core/tree/WELD-2151. And feel free to add comments to this blog post. Any feedback is appreciated!


Weld SE and synthetic container lifecycle event observers

2016-2-8   api , draft   Martin Kouba

Last week Weld 3.0.0.Alpha15 was released and so it’s time to reveal the features that should go into the next experimental release. The main goal of Weld 3.0.0.Alpha16 is to reflect the output of CDI-558. However, we would also like to continue to deliver experimental prototypes so that users could test a new functionality (that we find interesting and useful) and the CDI EG could eventually include this into CDI 2.0.

It should be easier to start playing with extensions in Weld SE. Right now, it’s possible to pass an Extension instance to the Weld builder so that it’s not necessary to configure service providers (i.e. META-INF/services/javax.enterprise.inject.spi.Extension file). In such case, the extension class is automatically scanned for observer methods and the instance is used as the receiver of the notifications. We would like to make this even easier. It is now possible to add a synthetic container lifecycle event observer without declaring an extension class. The observer logic is represented as a lambda expression. This might be especially useful for prototyping and/or for discovering how extensions work.

The starting point is the org.jboss.weld.environment.se.ContainerLifecycleObserver class. There are few static methods, each corresponding to a specific container lifecycle event (e.g. beforeBeanDiscovery()). Some variants return a builder so that it’s possible to customize the observer (observed type, priority, etc.). Others accept a lambda and return an observer instance direcly.

A synthetic observer may be added to the builder directly - see Weld.addContainerLifecycleObserver() - or grouped in a synthetic extension - see ContainerLifecycleObserver.extensionBuilder().

Extension testExtension = ContainerLifecycleObserver.extensionBuilder()
        .add(afterBeanDiscovery((e) -> System.out.println("Bean discovery completed!")))
        .add(processAnnotatedType().notify((e) -> {
                if (e.getAnnotatedType().getJavaClass().getName().startsWith("com.foo")) {
                    e.veto();
                }
            })).build();
    
    try (WeldContainer container = new Weld().addExtension(testExtension).initialize()) {
        // Use the container...
    }

This feature is merged in the master branch. If you want to try it out, you’ll have to use a 3.0.0-SNAPSHOT version or build the Weld from source: mvn clean install. Do you find it useful? Any feedback is appreciated! Feel free to add comments to this blog post or to the corresponding issue: WELD-2012.