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.
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.
OK, I fixed that. Note that this is just an example. It is not supposed to be production code.
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, Thought I'd do my part so it doesn't happen in this case.
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.
Thanks. But I didn't do it alone! :-)
Rrrm, I guess I've never read that prohibition as outlawing access to classpath resources.
Two corrections to make this work:
Instead of line
it should be:And instead of line
it should be:Adam
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
https://jira.jboss.org/browse/WELDX-146
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
field.getType().isAssignableFrom( value.getClass() )
should be written as
field.getType().isInstance( value )