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, JPA, JSF specifications and as spec lead of the CDI specification. At Red Hat, I'm currently working on Ceylon, a new programming language for the JVM.

I also post stuff on G+.

Location: Guanajuato, Mexico, cabrones!
Occupation: Fellow at JBoss, a Division of Red Hat
Archive 'Portable Extensions'
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.

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.

16. Nov 2009, 14:35 CET, by Gavin King

One of the nicest features of CDI is the portable extension SPI. According to the spec:

A portable extension may integrate with the container by:
  • Providing its own beans, interceptors and decorators to the container
  • Injecting dependencies into its own objects using the dependency injection service
  • Providing a context implementation for a custom scope
  • Augmenting or overriding the annotation-based metadata with metadata from some other source

We've got plenty of examples of CDI application code floating about, but not much in the way of examples for framework developers who want to integrate with CDI, or augment the built-in functionality. So here's a little taste of the what a CDI portable extension looks like.

Let's start with an example of an extension that provides support for the use of @Named at the package level. The package-level name is used to qualify the EL names of all beans defined in that package.

Portable extensions implement the marker interface Extension and observe container lifecycle events, in this case ProcessAnnotatedType, an event that is fired by the container when it discovers a class or interface in a bean archive. The portable extension wraps the AnnotatedType object and overrides the value() of the @Named annotation of the bean.

public class QualifiedNameExtension implements Extension {

    <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> pat) {

        //wrap this to override the annotations of the class
        final AnnotatedType<X> at = pat.getAnnotatedType();

        AnnotatedType<X> wrapped = new AnnotatedType<X>() {

            @Override
            public Set<AnnotatedConstructor<X>> getConstructors() {
                return at.getConstructors();
            }

            @Override
            public Set<AnnotatedField<? super X>> getFields() {
                return at.getFields();
            }

            @Override
            public Class<X> getJavaClass() {
                return at.getJavaClass();
            }

            @Override
            public Set<AnnotatedMethod<? super X>> getMethods() {
                return at.getMethods();
            }

            @Override
            public <T extends Annotation> T getAnnotation(final Class<T> annType) {
                if ( Named.class.equals(annType) ) {
                    class NamedLiteral 
                            extends AnnotationLiteral<Named> 
                            implements Named {
                        @Override
                        public String value() {
                            Package pkg = at.getClass().getPackage();
                            String unqualifiedName = at.getAnnotation(Named.class).value();
                            final String qualifiedName;
                            if ( pkg.isAnnotationPresent(Named.class) ) {
                                qualifiedName = pkg.getAnnotation(Named.class).value() 
                                      + '.' + unqualifiedName;
                            }
                            else {
                                qualifiedName = unqualifiedName;
                            }
                            return qualifiedName;
                        }
                    }
                    return (T) new NamedLiteral();
                }
                else {
                    return at.getAnnotation(annType);
                }
            }

            @Override
            public Set<Annotation> getAnnotations() {
                return at.getAnnotations();
            }

            @Override
            public Type getBaseType() {
                return at.getBaseType();
            }

            @Override
            public Set<Type> getTypeClosure() {
                return at.getTypeClosure();
            }

            @Override
            public boolean isAnnotationPresent(Class<? extends Annotation> annType) {
                return at.isAnnotationPresent(annType);
            }
            
        };

        pat.setAnnotatedType(wrapped);
    }
    
}

We need to deploy this portable extension in a jar containing a file named javax.enterprise.inject.spi.Extension in the META-INF/services directory. This file should contain the full name of our QualifiedNameExtension class.

Here's a second example, of a portable extension that creates Bean objects for each class in a set of classes and registers the beans with the container. This is similar to what the container does for classes it discovers in bean archives.

public class RegisterExtension implements Extension {
    
    List<Class<?>> getBeanClasses() { 
        //get some classes from somewhere
    }
    
    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) {
        for ( final Class c: getBeanClasses() ) {
            
            //use this to read annotations of the class
            AnnotatedType at = bm.createAnnotatedType(c); 

            //use this to create the class and inject dependencies
            final InjectionTarget it = bm.createInjectionTarget(at); 

            abd.addBean( new Bean() {
    
                @Override
                public Class<?> getBeanClass() {
                    return c;
                }
    
                @Override
                public Set<InjectionPoint> getInjectionPoints() {
                    return it.getInjectionPoints();
                }
    
                @Override
                public String getName() {
                    return null;
                }
    
                @Override
                public Set<Annotation> getQualifiers() {
                    Set<Annotation> qualifiers = new HashSet<Annotation>();
                    qualifiers.add( new AnnotationLiteral<Default>() {} );
                    qualifiers.add( new AnnotationLiteral<Any>() {} );
                    return qualifiers;
                }
    
                @Override
                public Class<? extends Annotation> getScope() {
                    return Dependent.class;
                }
    
                @Override
                public Set<Class<? extends Annotation>> getStereotypes() {
                    return Collections.emptySet();
                }
    
                @Override
                public Set<Type> getTypes() {
                    Set<Type> types = new HashSet<Type>();
                    types.add(c);
                    types.add(Object.class);
                    return types;
                }
    
                @Override
                public boolean isAlternative() {
                    return false;
                }
    
                @Override
                public boolean isNullable() {
                    return false;
                }
    
                @Override
                public Object create(CreationalContext ctx) {
                    Object instance = it.produce(ctx);
                    it.inject(instance, ctx);
                    it.postConstruct(instance);
                    return instance;
                }
    
                @Override
                public void destroy(Object instance, CreationalContext ctx) {
                    it.preDestroy(instance);
                    it.dispose(instance);
                    ctx.release();
                }
                
            } );
        }
    }
    
}

UPDATE: I've finally got a proper chapter on portable extensions written up in the Weld reference documentation. Check out chapter 16 of this version if this post piqued your interest.