Saying in a nutshell I would like to put in custom scope particular instance of Configuration class from rest request. Main problem is that custom scope (JobScoped from JBeret h
I think this question consists of several parts:
I will try to answer all individual questions, but keep in mind that I've only very recently started using CDI/Weld, and have no experience with JBeret.
The reason I am adding this question, is because I think Configuration may not need to be a scoped entity. If Configuration has nothing specific to the scope, it could be @Singleton or @Stateless as well. Think for example from configuration files, resources, or environment variables, that will not change on runtime. Non-scoped (or Singleton-scoped) dependencies can be injected into batchlets just fine, using regular @Inject fields, without any need for a @JobScoped annotation.
So what if the actual value depends on the context and cannot be injected in a @Singleton fashion? Based from the JBeret documentation, it is preferred to pass all configuration by Properties. These can then be read from the JobContext, or injected using the @BatchProperty annotation. This only works for a predefined list of types that are serialisable from a String.
@Named
public class MyBatchlet extends AbstractBatchlet {
@Inject
@BatchProperty(name = "number")
int number;
}
@RequestScope in a batch job?I think you shouldn't. The @RequestScope is for requests solely. If you have dependencies dependent on @RequestScope that should be accessible outside of a request, consider to introduce a custom scope.
If you really need to enter the
@RequestScopeprogramatically, you can define your own context for it and enter that context (see part 4 below) or enter the context by default, as addressed in this blogpost by Dan Haywood, in his attempt to get into the@RequestScopein Java SE.
It is fairly easy to create a custom scope. A custom scope however requires an implementation for the scope context. I found this to be a little unclear in the documentation. Luckily there is the library microscoped library. For this example, you only need the microscoped-core dependency, which provides a ScopeContext implementation that is used in their custom scopes. We will use that ScopeContext for our simple scope as well.
First we have to create the Scope annotation:
@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface CustomScoped {}
Secondly, we have to create an extension:
public class CustomScopedExtension implements Extension, Serializable {
public void addScope(@Observes final BeforeBeanDiscovery event) {
event.addScope(CustomScoped, true, false);
}
public void registerContext(@Observes final AfterBeanDiscovery event) {
event.addContext(new ScopeContext<>(CustomScoped.class));
}
}
Note that we're using the ScopeContext from microscoped here. Furthermore, you should register your extension by adding the full classname toMETA-INF/services/javax.enterprise.inject.spi.Extension`.
Now we need to enter our scope. We can do this with a little bit of code, that you can place for example in a web Filter or method interceptor. The code uses an BeanManager instance, which can be obtained with @Inject:
ScopeContext> context = (ScopeContext>) beanManager.getContext(CustomScoped.class);
context.enter(key);
try {
// continue computation
} finally {
context.destroy(key);
}
I have been asking myself the very same question, and this is the solution I came up with. See also my question on how to properly seed from custom Weld CDI scopes: Seed value in Weld CDI custom scope . I do have a workaround for your issue though:
@Singleton
public class ConfigurationProducer {
private final InheritableThreadLocal threadLocalConfiguration =
new InheritableThreadLocal<>();
@Produces
@ActiveDataSet
public ConfigurationConfiguration() {
return threadLocalConfiguration.get()
}
public void setConfiguration(Configuration configuration) {
threadLocalConfiguration.set(configuration);
}
}
Now from your the interceptor written above, you can inject ConfigurationProducer and use ConfigurationProducer #setConfiguration(Configuration) to set the Configuration for the current thread. I am still looking for better options here.