1

I understood that classes that were not JSP-299 compliant could be made available for injection if instantiated via a producer method.

This I have interpreted as meaning that if I want to make injectable a bean with a constructor that takes a parameter I can do so by the use of a producer method.

However when I do this I get the following exception on deployment:

2015-11-11T21:35:12.099+0000|Grave: Exception during lifecycle processing
org.glassfish.deployment.common.DeploymentException: CDI deployment failure:Exception List with 2 exceptions:
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001435 Normal scoped bean class org.....MongoConfiguration is not proxyable because it has no no-args constructor - Producer Method [MongoConfiguration] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces @ApplicationScoped public org.....PropertiesProducer.produceMongoConfiguration()]. 

Here is the producer:

public class PropertiesProducer {

    private static final String PROPERTIES_FILE = "mongo.properties";

    private Properties properties = new Properties();

    public static final String DATABASE_NAME        = "database.name";
    public static final String PORT                 = "database.port";
    public static final String HOST                 = "database.host";
    public static final String USERNAME             = "database.username";
    public static final String PASSWORD             = "database.password";

    @Produces
    @ApplicationScoped
    public MongoConfiguration produceMongoConfiguration(){

        final InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTIES_FILE);
        if (in == null) {
            return new MongoConfiguration(properties);
        }
        try {
            properties.load(in);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load properties", e);
        }
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                // don't care
            }
        }

        return new MongoConfiguration(properties);
    }
}

Here is the usage:

public class MongoDatastore {

    @Inject
    MongoConfiguration mongoConfiguration;

    @Inject
    MongoClient mongoClient;
    Datastore datastore;

    @PostConstruct
    private void setupDatastore() {
        Morphia morphia = new Morphia();
        datastore = morphia.createDatastore(mongoClient, mongoConfiguration.getDatabaseName());
    }

 }

Have I missed something really obvious?

Alex Theedom
  • 1,604
  • 15
  • 16
  • Try annotating the class PropertiesProducer with @ApplicationScoped too, or add META-INF/org.jboss.weld.enableUnsafeProxies file to your deployment. – Rafael Guillen Nov 12 '15 at 04:56
  • 2
    The way I see it, this exception is reasonable: The `MongoConfiguration` is normal scoped, thus will be proxied by CDI (specs ch.6.3 "Normal scopes and pseudo-scopes"), but cannot be, because of the reason displayed in the exception (specs, ch.3.15 "Unproxyable bean classes"). If you control this class, just add a `protected` or package-private no-args constructor. It might be better to define an interface and use that instead of the concrete bean. – Nikos Paraskevopoulos Nov 12 '15 at 09:37
  • Good point. Another solution is to use a `Singleton` pseudo-scope. – G. Demecki Nov 13 '15 at 09:56
  • I resolved it by removing the @ApplicationScoped from the producer method. Therefore it has Dependent scope and, if I am correct, will inherit its scope from the client. Anyone see any issues with this? – Alex Theedom Nov 15 '15 at 21:30

1 Answers1

0

The simplest solution is to change the scope from the @ApplicationScoped to @Singleton:

import javax.inject.Singleton;

@Produces
@Singleton
public MongoConfiguration produceMongoConfiguration(){
     // ...
     return new MongoConfiguration(properties);
}

For clarification you can see this SO answer.

BTW: normal scope @ApplicationScoped is usually preferred over the @Singleton pseudo-scope. Why? E.g. because a serialization + deserialization of such bean will work flawlessly. But in a daily work, sometimes we encounter a 3rd part class that is unproxyable, and we cannot change it. So possible solutions we have:

  • Use @Singleton pseudo-scope.
  • Create an interface and use that instead of the concrete bean (inside producer method return type and normal code).
Community
  • 1
  • 1
G. Demecki
  • 10,145
  • 3
  • 58
  • 58