Skip to content

Instantly share code, notes, and snippets.

@antoinesd
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antoinesd/3097661ca99fa61900fb to your computer and use it in GitHub Desktop.
Save antoinesd/3097661ca99fa61900fb to your computer and use it in GitHub Desktop.
Implementing type conversion with CDI 1.1+
package org.cdispec.converter;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
/**
* @author Antoine Sabot-Durand
*/
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Convert {
@Nonbinding
String value() default "";
}
package org.cdispec.converter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.BeanAttributes;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.ProcessBeanAttributes;
import javax.enterprise.inject.spi.ProcessInjectionPoint;
/**
* @author Antoine Sabot-Durand
*/
public class ConverterExtension implements Extension {
private static class ConverterBeanAttribute implements BeanAttributes<Object> {
private BeanAttributes<Object> delegate;
private Set<Type> types;
public ConverterBeanAttribute(BeanAttributes<Object> delegate, Set<Type> types) {
this.delegate = delegate;
this.types = types;
}
@Override
public Set<Type> getTypes() {
return types;
}
@Override
public Set<Annotation> getQualifiers() {
return delegate.getQualifiers();
}
@Override
public Class<? extends Annotation> getScope() {
return delegate.getScope();
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return delegate.getStereotypes();
}
@Override
public boolean isAlternative() {
return delegate.isAlternative();
}
}
private Set<Type> types = new HashSet<>();
public void retrieveTypes(@Observes ProcessInjectionPoint<?,?> pip) {
InjectionPoint ip=pip.getInjectionPoint();
if(ip.getAnnotated().isAnnotationPresent(Convert.class))
types.add(ip.getType());
}
public void addTypeToConverter(@Observes ProcessBeanAttributes<Object> pba) {
if(pba.getAnnotated().isAnnotationPresent(Convert.class))
pba.setBeanAttributes(new ConverterBeanAttribute(pba.getBeanAttributes(),types));
}
}
package org.cdispec.converter;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
/**
* @author Antoine Sabot-Durand
*/
public class ConverterProducer {
@Produces
@Convert
public Object produceConverter(InjectionPoint ip) {
String toConvert = ip.getAnnotated().getAnnotation(Convert.class).value();
Class<?> toType = (Class<?>) ip.getAnnotated().getBaseType();
System.out.println("I have to convert "+ toConvert+ " to type: " +toType.toString());
if(toType.equals(Integer.class))
return new Integer(toConvert);
else if (toType.equals(StringBuffer.class))
return new StringBuffer(toConvert);
else
return null;
}
}
package org.cdispec.converter;
import junit.framework.Assert;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.FileNotFoundException;
import javax.enterprise.inject.spi.Extension;
import javax.inject.Inject;
/**
* @author Antoine Sabot-Durand
*/
@RunWith(Arquillian.class)
public class ConvertProducerTest {
@Deployment
public static Archive<?> createTestArchive() throws FileNotFoundException {
WebArchive ret = ShrinkWrap
.create(WebArchive.class, "test.war")
.addPackage("org.cdispec.converter")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsServiceProvider(Extension.class, ConverterExtension.class);
return ret;
}
@Inject
@Convert("126")
Integer converted;
@Inject
@Convert("Me/Hungry")
StringBuffer converted2;
@Test
public void shoulHaveInjectedConvertedInteger() {
Assert.assertEquals(new Integer(126), converted);
}
@Test
public void shoulHaveInjectedConvertedStringBuffer() {
Assert.assertEquals("Me/Hungry", converted2.toString());
}
}
@antoinesd
Copy link
Author

This code works for CDI 1.1 and 1.2. It was tested with Weld 2.2.6 and should work with OWB 1.5.x. I could port it to CDI 1.0 using Deltaspike if you need.

The extension build the list of types for each InjectionPoint qualified with @Convert qualifier, it thens change Bean Attributes of the produce to add all the types discovered.

@struberg
Copy link

struberg commented Jun 1, 2015

edit Just saw that this gist is rather old and we might have discussed this already in the final code.


This is imo perfectly non -portable code. Please read spec section 12.4.3. Bean discovery
„For every type in the set of discovered types (as defined in Section 12.4.1, “Type discovery”), the container must:
• if the class is a managed bean, session bean, or other Java EE component class supporting injection, fire an event of type ProcessInjectionPoint for each injection point in the class, as defined in Section 11.5.7, “ProcessInjectionPoint event”, and THEN
• if the class is a managed bean, session bean, or other Java EE component class supporting injection, fire an event of type ProcessInjectionTarget, as defined in Section 11.5.8, “ProcessInjectionTarget event”, and then
• determine which alternatives, interceptors and decorators are enabled, according to the rules defined in Section 5.1.2, “Enabled and disabled beans”, Section 9.4, “Interceptor enablement and ordering” and Section 8.2, “Decorator enablement and ordering”, and THEN
• if the class is an enabled bean, interceptor or decorator, fire an event of type ProcessBeanAttributes, as defined in Section 11.5.9, “ProcessBeanAttributes event”, and THEN

„So the CDI spec clearly specifies that the whole loop is ‚for every type in the set of discovered types, THEN THEN THEN“

OWB impls it exactly as it is specified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment