Help

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.

10 comments:
 
18. Nov 2009, 21:10 CET | Link
I Don't Know Jack

Don't mean to be picky, but the code is missing a close on the input stream acquired from the class loader. If I'm not mistaken, the load method of the Properties leaves the stream opened. I've seen this problem (leaving streams opened) in many different places in Seam (most of which have been fixed), which caused me lots of headaches while in development mode and constantly redeploying applications. Just thought I'd point it out before this piece of code starts spreading.

ReplyQuote
 
18. Nov 2009, 21:49 CET | Link

OK, I fixed that. Note that this is just an example. It is not supposed to be production code.

 
18. Nov 2009, 21:58 CET | Link
I Don't Know Jack

I know, I know. I said I was being picky. It's just that I'm pretty sure lots of people don't bother reading and simply copy/paste whatever they see on the interweb and then complain, Hey, how come this doesn't work??? Thought I'd do my part so it doesn't happen in this case.

 
01. Apr 2010, 23:47 CET | Link

Hi, Gavin; first, a quick compliment to you on a really nice specification. JSR 299 is just mind-bendingly powerful.

A question launching from your examples above: how does JSR 299 play with the traditional EJB prohibitions on accessing the filesystem? Obviously your example code reads properties files. Is this permitted in a Java EE 6 spec-compliant application?

I ask only because I know that JSR 299 spans the web application side of the EE world and the EJB side of the EE world. Till now, there haven't been many other areas in Java EE that do that kind of spanning, so questions about whether threads, files etc. are allowed tend not to come up.

Again thanks for this spec and the hard work of getting it into the Java EE 6 specification.

 
02. Apr 2010, 01:58 CET | Link
Laird Nelson wrote on Apr 01, 2010 17:47:
Hi, Gavin; first, a quick compliment to you on a really nice specification. JSR 299 is just mind-bendingly powerful.

Thanks. But I didn't do it alone! :-)

A question launching from your examples above: how does JSR 299 play with the traditional EJB prohibitions on accessing the filesystem? Obviously your example code reads properties files. Is this permitted in a Java EE 6 spec-compliant application?

Rrrm, I guess I've never read that prohibition as outlawing access to classpath resources.

 
10. May 2010, 23:24 CET | Link

Two corrections to make this work:

Instead of line

String propsFileName = at.getClass().getSimpleName() + ".properties";
it should be:
String propsFileName = at.getJavaClass().getSimpleName() + ".properties";

And instead of line

Field field = at.getJavaClass().getField(fieldName);
it should be:
Field field = at.getJavaClass().getDeclaredField(fieldName);

Adam

 
18. Sep 2010, 20:00 CET | Link

Hi all,

Is this extension packaged yet or is this something we have to write ourselves (or copy from above)? I was hoping to simply include this as a dependency and go.

Walter

 
25. Sep 2010, 00:45 CET | Link

Pete,

I was a little too anxious to wait and copied your example up here: http://github.com/walterjwhite/cdi.extension.configurer

I added support for primitives, nothing too exciting. I wanted to get something up and running so I can configure my beans that way.

Walter

 
16. Aug 2011, 02:51 CET | Link
bob

field.getType().isAssignableFrom( value.getClass() )

should be written as

field.getType().isInstance( value )

Post Comment