Skip navigation

Someone asked about "Can I write a method producer method without knowing the type until runtime?" and I was looking at something similar, trying to figure out what needs and Extension, so I mocked a little solution that uses a general producer method based on a specific qualifier annotation and a wrapping parameterized interface. See the forum thread for the original question, and this possible approach. While this may not be as transparent and generic as the user was asking for, it is a pretty simple way to achieve some form of dynamic production of injection values.

 

1. Create an qualifier annotation ConfigType:

package test.com.si.weld.dynproducer;


import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;


/**
 * @author Scott Stark
 * @version $Revision:$
 */
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface ConfigType {
}

 

2. Create a simple parameterized interface and wrapper implementation to hold the user defined configuration class types:

package test.com.si.weld.dynproducer;

public interface IStoreSettings<T> {
    T getSettings();
}
package test.com.si.weld.dynproducer;


public class StoreSettingsWrapper<T> implements IStoreSettings<T> {
    private T settings;


    StoreSettingsWrapper(T settings) {
        this.settings = settings;
    }
    @Override
    public T getSettings() {
        return settings;
    }
    @Override
    public String toString() {
        return settings.toString();
    }
}

 

3. Create a general producer method that takes an InjectionPoint:

package test.com.si.weld.dynproducer;


import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionPoint;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;


/**
 * @author Scott Stark
 * @version $Revision:$
 */
public class GenericConfigFactory {
    @Produces @ConfigType
    public IStoreSettings configProducer(InjectionPoint ip, BeanManager beanManager) throws Exception {
        dumpInjectionPoint(ip);
        ParameterizedType type = (ParameterizedType) ip.getType();
        Type[] typeArgs = type.getActualTypeArguments();
        Class<?> settingsClass = (Class<?>) typeArgs[0];
        Constructor ctor = settingsClass.getConstructor();
        Object settings = ctor.newInstance();
        StoreSettingsWrapper wrapper = new StoreSettingsWrapper(settings);
        return wrapper;
    }


    private void dumpInjectionPoint(InjectionPoint ip) {
        StringBuilder tmp = new StringBuilder("InjectionPoint");
        tmp.append("\n\tgetAnnotated:"+ip.getAnnotated());
        tmp.append(";\n\t getType:"+ip.getType());
        ParameterizedType type = (ParameterizedType) ip.getType();
        Type[] typeArgs = type.getActualTypeArguments();
        Type rawType = type.getRawType();
        tmp.append("\n\t\ttypeArgs: "+ Arrays.asList(typeArgs));
        tmp.append("\n\t\trawType: "+ rawType);
        tmp.append(";\n\t getQualifiers:"+ip.getQualifiers());
        tmp.append(";\n\t getBean:"+ip.getBean());
        tmp.append(";\n\t getMember:"+ip.getMember());
        tmp.append(";\n\t isDelegate:"+ip.isDelegate());
        tmp.append(";\n\t isTransient:"+ip.isTransient());
        System.out.println(tmp.toString());
    }
}

 

 

Now one can create different type of configuration objects and have them injected into a consumer:

 

package test.com.si.weld.dynproducer;


public @interface Config {
    public String value();
}

package test.com.si.weld.dynproducer;


import javax.inject.Inject;


public class ConfigUser {
    @Inject @ConfigType
    private IStoreSettings<MyDataStoreSettings> settings;
    @Inject @ConfigType
    private IStoreSettings<AnotherDataStoreSettings> settings2;


    @Override
    public String toString() {
        return "ConfigUser{" +
                "settings2=" + settings2 +
                ", settings=" + settings +
                '}';
    }
}

package test.com.si.weld.dynproducer;


import java.io.File;


public class MyDataStoreSettings {
    @Config("number.of.threads.key")
    int numberOfThreads = 1;
    @Config("root.folder.key")
    File rootFolder = new File("/tmp");


    @Override
    public String toString() {
        return "MyDataStoreSettings{" +
                "numberOfThreads=" + numberOfThreads +
                ", rootFolder=" + rootFolder +
                '}';
    }
}

package test.com.si.weld.dynproducer;


public class AnotherDataStoreSettings {
    @Config("thread.count.key")
    int threadCount = 2;
    @Config("tmp.path.key")
    String tmpPath = "/tmp";


    @Override
    public String toString() {
        return "AnotherDataStoreSettings{" +
                "threadCount=" + threadCount +
                ", tmpPath=" + tmpPath +
                '}';
    }
}

 

A little Arquillian testcase illustrates the behavior:

 

package test.com.si.weld.dynproducer;


import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;


import javax.inject.Inject;


@RunWith(Arquillian.class)
public class DynProducerTest {
    @Deployment
    public static JavaArchive createDeployment()
    {
        JavaArchive archive = ShrinkWrap.create(JavaArchive.class)
          .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
        archive.addPackages(true, "test/com/si/weld/dynproducer");
        return archive;
    }


    @Inject
    ConfigUser configUser;


    @Test
    public void testDynProducer() {
        System.out.printf("configUser=%s\n", configUser);
    }
}

 

Outputs:

 

34 [main] INFO org.jboss.weld.Version - WELD-000900 1.1.9 (Final)
InjectionPoint
          getAnnotated:[field] @ConfigType @Inject private test.com.si.weld.dynproducer.ConfigUser.settings;
           getType:interface test.com.si.weld.dynproducer.IStoreSettings<class test.com.si.weld.dynproducer.MyDataStoreSettings>
                    typeArgs: [class test.com.si.weld.dynproducer.MyDataStoreSettings]
                    rawType: interface test.com.si.weld.dynproducer.IStoreSettings;
           getQualifiers:[@test.com.si.weld.dynproducer.ConfigType()];
           getBean:Managed Bean [class test.com.si.weld.dynproducer.ConfigUser] with qualifiers [@Any @Default];
           getMember:private test.com.si.weld.dynproducer.IStoreSettings test.com.si.weld.dynproducer.ConfigUser.settings;
           isDelegate:false;
           isTransient:false
InjectionPoint
          getAnnotated:[field] @ConfigType @Inject private test.com.si.weld.dynproducer.ConfigUser.settings2;
           getType:interface test.com.si.weld.dynproducer.IStoreSettings<class test.com.si.weld.dynproducer.AnotherDataStoreSettings>
                    typeArgs: [class test.com.si.weld.dynproducer.AnotherDataStoreSettings]
                    rawType: interface test.com.si.weld.dynproducer.IStoreSettings;
           getQualifiers:[@test.com.si.weld.dynproducer.ConfigType()];
           getBean:Managed Bean [class test.com.si.weld.dynproducer.ConfigUser] with qualifiers [@Any @Default];
           getMember:private test.com.si.weld.dynproducer.IStoreSettings test.com.si.weld.dynproducer.ConfigUser.settings2;
           isDelegate:false;
           isTransient:false
configUser=ConfigUser{settings2=AnotherDataStoreSettings{threadCount=2, tmpPath=/tmp}, settings=MyDataStoreSettings{numberOfThreads=1, rootFolder=/tmp}}

 

See the attached dynproducer.zip for the full source.

 

ironmaiden:SITesting starksm$ jar -tf dynproducer.zip

src/test/java/test/com/si/weld/dynproducer/

src/test/java/test/com/si/weld/dynproducer/AnotherDataStoreSettings.java

src/test/java/test/com/si/weld/dynproducer/Config.java

src/test/java/test/com/si/weld/dynproducer/ConfigType.java

src/test/java/test/com/si/weld/dynproducer/ConfigUser.java

src/test/java/test/com/si/weld/dynproducer/DynProducerTest.java

src/test/java/test/com/si/weld/dynproducer/GenericConfigFactory.java

src/test/java/test/com/si/weld/dynproducer/IStoreSettings.java

src/test/java/test/com/si/weld/dynproducer/MyDataStoreSettings.java

src/test/java/test/com/si/weld/dynproducer/StoreSettingsWrapper.java

Filter Blog

By date: By tag: