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+.
| Recent Entries |
|
10. Jan 2012
|
|
|
12. Aug 2011
|
|
|
11. Aug 2011
|
|
|
06. Aug 2011
|
|
|
02. Aug 2011
|
|
|
01. Aug 2011
|
|
|
24. Jul 2011
|
|
|
22. Jul 2011
|
|
|
21. Jul 2011
|
|
|
20. Jul 2011
|
|
|
19. Jul 2011
|
|
|
17. Jul 2011
|
|
|
13. Jul 2011
|
|
|
07. Jul 2011
|
|
|
09. Jun 2011
|
| Contexts and Dependency Injection | (44) |
| Ceylon | (41) |
| Web Beans | (41) |
| Seam News | (29) |
| Seam | (28) |
| Weld | (14) |
| Java EE 6 | (13) |
| Introduction to Ceylon | (12) |
| Hibernate | (6) |
| > JavaServer Faces < | (6) |
| JPA | (5) |
| JPA 2 | (5) |
| Web Beans Sneak Peek | (5) |
| Criteria Queries | (4) |
| Bean Validation | (3) |
| EE6 Wishlist | (3) |
| Portable Extensions | (3) |
| Seam Wiki | (3) |
| Web Frameworks | (3) |
| Interceptors | (2) |
| JBoss Tools | (2) |
| Payasos | (2) |
| XML Hell | (2) |
| Ceylon IDE | (1) |
| EJB | (1) |
| Granite DS | (1) |
| JDO | (1) |
| Persistence | (1) |
| Photography | (1) |
| RichFaces | (1) |
|
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 |
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.
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.
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.