Help

I'm the creator of Hibernate, a popular object/relational persistence solution for Java, and Seam, an application framework for enterprise Java. I've also contributed to the Java Community Process standards as Red Hat representative for the EJB and JPA specifications and as spec lead of the CDI specification. At Red Hat, I'm currently working on Ceylon, a new programming language for Java and JavaScript VMs.

I now blog at the Ceylon blog.

I also post stuff on G+.

Location: Barcelona, Spain
Occupation: Fellow at JBoss, a Division of Red Hat
Archive 'Weld'
My Books
Java Persistence with Hibernate
with Christian Bauer
November 2006
Manning Publications
841 pages (English), PDF ebook
Hibernate in Action
with Christian Bauer
August 2004
Manning Publications
408 pages (English), PDF ebook
08. Jan 2010, 02:32 CET, by Gavin King

Since the release of Weld and Java EE 6, there's been a heap of activity in the Weld user forum, and especially a lot of questions about problems related to framework development. You can do some kinds of generic programming in CDI just using managed beans, producer methods and InjectionPoint. But if you want to get serious, you're eventually going to have to embrace the portable extension SPI. Here's a couple of examples of how people are using this SPI.

Steven Verborgh has written a nice tutorial showing how to implement a custom JSF view scope for CDI. Henri Chen has integrated the ZK framework with CDI (hopefully other web frameworks won't be far behind). Matt Corey has been experimenting with environment configuration via JNDI.

Meanwhile, I've been working on compiling user feedback into a list of enhancements to the SPI. We plan to roll the most important items into the first CDI maintenance release.

UPDATE: for completeness, I should also link Pete Royle's Quartz scheduling extension.

05. Dec 2009, 00:52 CET, by Gavin King

I've seen a couple of folks wondering why CDI requires a beans.xml file in every bean archive. If there's no alternatives, interceptors, or decorators to declare, why do you need to have the empty file?

Well, there's two things about CDI that we need to keep in mind:

  • CDI does not require any special declaration for a Java class to be injected - that's right, no annotation or XML declaration at all!
  • CDI does not define any special kind of module - CDI beans can be deployed in a library jar, EJB jar, war, rar, or JVM classpath directory.

The CDI specification calls the process of identifying beans in modules bean discovery.

So there are potentially a lot of classes in the classpath which might be beans! We don't want to force the container to scan every one of those classes at startup and build its internal metamodel for each of them. This really could make bean discovery very slow.

But there's another reason we need to give the user some control over which classes are available for injection. We don't want every class in the classpath to potentially match an injection point, including classes that were never intended to be injected into things. This would force the developer to have to use qualifiers much more often to disambiguate injection points.

So we have two choices. We could have the developer:

  1. explicitly exclude modules which do not contain beans, or
  2. explicitly declare modules which do contain beans.

I think it's clear that the second option is a much better way to go. Thus, CDI has the notion of a bean archive. A bean archive is just a module that has a file named beans.xml in the metadata directory. The container looks for beans in bean archives. It ignores other modules.

Now, you might be wondering if we've got the granularity wrong here. Why should module be the right criteria to use for including/excluding a class. Why not consider:

  • a class-level annotation,
  • the package,
  • some type it implements or extends, or
  • some naming convention.

Well, I think we've got the first option covered. Annotate a bean @Alternative, or with an alternative stereotype, and it will be considered disabled by CDI, as long as you don't explicitly enable it in beans.xml. That's not quite the same thing as excluding the class from scanning altogether, but it's close. (One difference is that a portable extension with still get a ProcessAnnotatedType event for that class.)

Excluding a package makes sense to me, and a future version of CDI might allow you to declare excluded packages in beans.xml.

Excluding a bean by type or naming convention doesn't appeal to me at all. In the world of CDI, we use stereotypes for identifying architectural roles. We don't use marker interfaces or naming conventions. I strongly disapprove of having names affect functionality.

We'll experiment with giving you some finer grained control over bean discovery in Weld. However, my expectation is that the current solution is going to work great for most people.

17. Nov 2009, 23:54 CET, by Gavin King

Here's a CDI portable extension that reads values from properties files and configures fields of Java EE components. In Java EE 6, this works for any Java EE component supporting injection, including servlets, EJBs, managed beans, interceptors and more.

In this example, properties for a class such as org.mydomain.blog.Blogger go in a resource named org/mydomain/blog/Blogger.properties, and the name of a property must match the name of the field to be configured. So Blogger.properties would contain:

firstName=Gavin
lastName=King

The portable extension works by wrapping the containers InjectionTarget and setting field values from the inject() method.

public class ConfigExtension implements Extension {

	<X> void processInjectionTarget(ProcessInjectionTarget<X> pit) {
		
	    final InjectionTarget<X> it = pit.getInjectionTarget();
        final Map<Field, Object> configuredValues = new HashMap<Field, Object>();
        
        AnnotatedType<X> at = pit.getAnnotatedType();
        
        String propsFileName = at.getClass().getSimpleName() + ".properties";
        InputStream stream = at.getJavaClass().getResourceAsStream(propsFileName);
        if (stream!=null) {
            
            try {
                Properties props = new Properties();
                props.load(stream);
                for (Map.Entry<Object, Object> property : props.entrySet()) {
                    String fieldName = property.getKey().toString();
                    Object value = property.getValue();
                    try {
                        Field field = at.getJavaClass().getField(fieldName);
                        field.setAccessible(true);
                        if ( field.getType().isAssignableFrom( value.getClass() ) ) {
                            configuredValues.put(field, value);
                        }
                        else {
                            //TODO: do type conversion automatically
                            pit.addDefinitionError( new InjectionException("field is not of type String: " + field ) );
                        }
                    }
                    catch (NoSuchFieldException nsfe) {
                        pit.addDefinitionError(nsfe);
                    }
                }
            }
            catch (IOException ioe) {
                pit.addDefinitionError(ioe);
            }
            finally {
                stream.close();
            }
        }
        
        InjectionTarget<X> wrapped = new InjectionTarget<X>() {

            @Override
            public void inject(X instance, CreationalContext<X> ctx) {
                it.inject(instance, ctx);
                for (Map.Entry<Field, Object> configuredValue: configuredValues.entrySet()) {
                    try {
                        configuredValue.getKey().set(instance, configuredValue.getValue());
                    }
                    catch (Exception e) {
                        throw new InjectionException(e);
                    }
                }
            }

            @Override
            public void postConstruct(X instance) {
                it.postConstruct(instance);
            }

            @Override
            public void preDestroy(X instance) {
                it.dispose(instance);
            }

            @Override
            public void dispose(X instance) {
                it.dispose(instance);
            }

            @Override
            public Set<InjectionPoint> getInjectionPoints() {
                return it.getInjectionPoints();
            }

            @Override
            public X produce(CreationalContext<X> ctx) {
                return it.produce(ctx);
            }
            
        };
        
        pit.setInjectionTarget(wrapped);
        
    }
    
}

The portable extension must be deployed in a jar with a file named META-INF/services/javax.enterprise.inject.spi.Extension containing the fully qualified extension class name.

17. Nov 2009, 13:46 CET, by Gavin King

One of the really nice things about Weld is how nicely it works in Java SE. Of course, if you run Weld on its own, you won't get nice functionality like EJBs (you'll need an embeddable EJB container for that), but you do get a bunch of great stuff, including:

  • Managed beans with @PostConstruct and @PreDestroy lifecycle callbacks
  • Dependency injection with qualifiers and alternatives
  • @ApplicationScoped, @Dependent and @Singleton scopes
  • Interceptors and decorators
  • Stereotypes
  • Events
  • Portable extension support

The Weld distribution comes with an example console application, and an example Swing application.

Here's a very simple console application:

@Singleton
public class HelloWorld {

    @PostConstruct
    void init() {
        System.out.println("Initializing Hello World application");
    }

    void printHello(@Observes ContainerInitialized event, 
                    @Parameters List<String> parameters) {
        System.out.println("Hello " + parameters.get(0));
    }

}

The printHello() method is a CDI observer method for the ContainerInitialized event. This event is fired by the Weld Java SE extension when the console application starts. The command line parameters are available for injection using @Parameters List<String>.

As usual, we need a file named META-INF/beans.xml the the jar or classpath directory containing our HelloWorld class.

We run the application using:

java org.jboss.weld.environments.se.StartMain Gavin

Of course, if you're using CDI you probably want to make much more use of dependency injection, for example:

@Singleton
public class HelloWorld {

    void printHello(@Observes ContainerInitialized event, 
                    @Parameters List<String> parameters, 
                    Hello hello) {
        hello.say(parameters.get(0));
    }

}

Where Hello is a bean:

public class Hello {
    public void say(String name) {
        System.out.println("Hello " + name);
    }
}

The Weld SE extension was created by contributor Peter Royle.

Showing 1 to 5 of 14 blog entries tagged 'Weld'