Another nice portable extension

Posted by    |      

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.


Back to top