jBeret + Weld SE - Inject managed bean from Batchlet - jsr352

I'm trying to make CDI work on JBeret SE.
This is my code:
SampleBatchlet class
#Named
public class SampleBatchlet extends AbstractBatchlet
{
#Inject
#BatchProperty(name = "foo")
String foo;
#Inject
StepContext stepContext;
#Inject
Logger logger;
#Override
public String process() throws Exception {
final String say = stepContext.getProperties().getProperty("say");
System.out.println("hello foolish");
return null;
}
}
SampleBatchletTest class
#EnableWeld
class SampleBatchletTest {
#Inject
Logger logger;
#WeldSetup
public WeldInitiator weld = WeldInitiator
.from(
LoggerProducer.class
)
.activate(
RequestScoped.class,
ApplicationScoped.class
)
.build();
#Test
void app() throws InterruptedException {
final JobOperator jobOperator = BatchRuntime.getJobOperator();
long id = jobOperator.start("simplebatchlet", null);
final JobExecutionImpl jobExecution = (JobExecutionImpl) jobOperator.getJobExecution(id);
jobExecution.awaitTermination(5, TimeUnit.SECONDS);
Assertions.assertEquals(BatchStatus.COMPLETED, jobExecution.getBatchStatus());
}
}
Server class
#ApplicationScoped
public class Server {
#Inject
private Logger logger;
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) throws InterruptedException {
logger.info("init");
}
LoggerProducer class
public class LoggerProducer {
#Produces
public Logger produceLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
The issue is Logger instance is not injected on SampleBatchlet, whereas is correctly injected either on test and server class above.
Any hints?
LITTLE UPDATE
By reading this reference
https://jberet.gitbooks.io/jberet-user-guide/content/batch_properties/
I discovered java.util.logging.Logger can be injected.
Therefore I added
<batchlet ref="it.infocert.shop.main.SampleBatchlet" >
<properties>
<property name="logger" value="java.util.logging.Logger" />
</properties>
</batchlet>
where value can be actually anything..
On SampleBatchlet I added
#Inject
#BatchProperty
Logger logger;
and now it is injected. I'm a bit perplexed by the way, because I wish to use another logger implementation..

When injecting via #BatchProperty, JBeret tries to check the type of injection field and match it up with the injection value, and instantiate an instance for injection. That's why the logger created by JBeret, instead of your own logger, is injected. For details, see JBeret BatchBeanProducer.java
To inject your own logger via a producer method, you may need to add a qualifier to disambuiguise it. For example,
public class LoggerProducer {
#Produces
#Named("myLogger")
public Logger produceLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
#Inject
#Named("myLogger")
Logger logger;

I changed batchet ref on my xml from:
<batchlet ref="it.infocert.shop.main.SampleBatchlet">
to:
<batchlet ref="sampleBatchlet">
now it works

Related

WeldSE with JUnit5 - Can't inject dependencies

This is my service class:
#Path("/foo")
#RequestScoped
public class FooService {
#Inject #Log
Logger logger;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String ping() {
return "pong";
}
}
In the same module I have an util class that provides the logger using a Log interface with #Qualifier annotation
public class WebResources {
#Produces
#RequestScoped
public FacesContext produceFacesContext() {
return FacesContext.getCurrentInstance();
}
#Produces #Log
public Logger produceLog(InjectionPoint injectionPoint) {
return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
Then I wrote a UnitTest using JUnit 5 + Weld SE
#EnableWeld
public class FooTest {
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(FooService.class)
.activate(RequestScoped.class)
.build();
#Test
public void ping(FooService fooService) {
fooService.ping();
}
}
This produces the following error:
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type Logger with qualifiers #Log
at injection point [BackedAnnotatedField] #Inject #Log it.infocert.shop.rest.FooService.logger
at it.infocert.shop.rest.FooService.logger(FooService.java:0)
How can I add the #Log dependency to Weld SE properly in a unit test?
The problem is that class WebResources isn't known to Weld once it starts the container for tests. The way it works is that you define what beans are in the container - you can add classes, packages and so on.
In your case, it should be enough to change the WeldInitiator creation like this:
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(FooService.class, WebResources.class)
.activate(RequestScoped.class)
.build();
Note that there are various way how to start the WeldInitiator ranging from the simplest one such the one you use, all the way to providing your own fully configured org.jboss.weld.environment.se.Weld object.

How to inject a bean into custom argument resolver?

Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}
Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}
This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}

Spring Data Neo4j "Hello, World" standalone app

I'm trying to write a "Hello, World" with Spring Data Neo4j in a standalone app. It runs and actually creates the Neo4j database, but my #Autowired repo is not being initialized. I suspect the problem is in my main class, but I don't know what to try. Unsurprisingly, almost all the Spring tutorials I've found are about web apps.
What am I doing wrong?
config bean:
#Configuration
#EnableNeo4jRepositories(basePackages = "test2")
public class ConfigBean extends Neo4jConfiguration {
private static final String DB_PATH = "/home/kevin/tmp/hello-spring-data-neo4j/";
public ConfigBean() {
setBasePackage("test2");
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH);
}
}
node entity:
#NodeEntity
public class Foo {
#GraphId
private Long id;
}
repository:
public interface FooRepository extends GraphRepository<Foo> { }
main class:
#Component
public class Test2 {
#Autowired
FooRepository repo;
public void doStuff() {
System.out.println("repo: " + repo); // null!
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext("test2");
new Test2().doStuff();
}
}
It logs about 350 lines of output. These are the last few lines. I searched for this error message, but the impression I got is that it's unrelated to my problem.
20:44:30.630 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Searching for key 'spring.liveBeansView.mbeanDomain' in [systemProperties]
20:44:30.631 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Searching for key 'spring.liveBeansView.mbeanDomain' in [systemEnvironment]
20:44:30.635 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source. Returning [null]
repo: null
Via the magical "ask a question, find the answer" effect, my main class now looks like this, and the repo is being assigned:
#Component
public class Test2 {
#Autowired
FooRepository repo;
public void doStuff() {
System.out.println("repo: " + repo);
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext("test2");
Test2 test2 = context.getBean(Test2.class);
test2.doStuff();
}
}

Message routing/handling with custom annotations and CDI injection

I have a Java EE 6 web application and use the WebSocket protocol to communicate with browsers. The browser can send various types of messages and in the servers onMessage method I would like to route (or dispatch) the message to a specific message handler class depending on the message type. I would like to configure or register these message handlers via annotations, similar to the mechanism of servlets (#WebServlet("/there")). And like in servlets, I would like to be able to use CDI injection in the message handlers.
For now I have a MessageType annotation, a MessageHandler interface and 3 implementations.
#Documented
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface MessageType
{
String value();
}
public interface MessageHandler
{
public void processMessage(String inputMesssage);
}
#MessageType("first")
public class FirstMessageHandler implements MessageHandler
{
#Inject
ResourceBundleProvider resourceBundleProvider;
#Override
public void processMessage(String inputMesssage)
{
System.out.println("FirstMessageHandler#processMessage: " + inputMesssage);
System.out.println("InjectionTest: " + resourceBundleProvider.getValue("label.language"));
}
}
#MessageType("second")
public class SecondMessageHandler implements MessageHandler
{
#Override
public void processMessage(String inputMesssage)
{
System.out.println("SecondMessageHandler#processMessage: " + inputMesssage);
}
}
public class DefaultMessageHandler implements MessageHandler
{
#Override
public void processMessage(String inputMesssage)
{
System.out.println("DefaultMessageHandler#processMessage: " + inputMesssage);
}
}
I also have a class MessageDispatcher which uses reflections to scan the classpath for the annotated message handlers, instantiates them and puts them into a map:
#ApplicationScoped
public class MessageDispatcher
{
private Map<String, MessageHandler> messageHandlerMap = new HashMap<String, MessageHandler>();
#Inject
DefaultMessageHandler defaultMessageHandler;
public MessageDispatcher()
{
registerAnnotatedHandlers();
}
private void registerAnnotatedHandlers()
{
Reflections reflections = new Reflections("namespace");
try
{
for (Class<?> annotatedClass : reflections.getTypesAnnotatedWith(MessageType.class))
{
String annotationValue = annotatedClass.getAnnotation(MessageType.class).value();
for (Class<?> interfaceClass : annotatedClass.getInterfaces())
if (!annotationValue.isEmpty() && interfaceClass.equals(MessageHandler.class))
messageHandlerMap.put(annotationValue, (MessageHandler) annotatedClass.newInstance());
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public MessageHandler getMessageHandler(String key)
{
MessageHandler messageHandler = messageHandlerMap.get(key);
return messageHandler != null ? messageHandler : defaultMessageHandler;
}
}
And finally in my websocket servlet's onMessage method I extract the key from the inbound message and use it for the message routing:
public synchronized void onMessage(String data)
{
String[] message = data.split(":");
// Choose the message handler from the message
MessageHandler messageHandler = messageDispatcher.getMessageHandler(message[0]);
// Process the message by the message handler
messageHandler.processMessage(message[1]);
}
My 3 incoming sample messages are:
"first:Message to handle with FirstMessageHandler"
"second:Message to handle with SecondMessageHandler"
"third:Message to handle with DefaultMessageHandler"
This works fine, The first and second messages are processed by FirstMessageHandler and SecondMessageHandler respectively. The third message is processed by the default message handler since there is no other handler registered for handling the key "third".
My Problem: I cannot use injection in the message handlers because they are created using Java reflection. Does anybody know how to get annotation processing and CDI injection 'married'? Or does anybody think this approach is bullshit and has another solution for that?
Best Regards
Sebastian
This is my final approach:
I spend a PostConstruct method to my MessageDispachter where I look for all message handler beans. For each of these beans I get their annotation value and a reference to the bean (which also includes creation of the bean). Then I store both, the annotation value and the bean reference into my messageHandlerMap. There is a lot of CDI delegating and interception involved, but it works:
public class MessageDispatcher
{
private Map<String, MessageHandler> messageHandlerMap = new HashMap<String, MessageHandler>();
#Inject
DefaultMessageHandler defaultMessageHandler;
#Inject
BeanManager beanManager;
#PostConstruct
public void registerHandlers()
{
Set<Bean<?>> messageHandlerBeans = beanManager.getBeans(MessageHandler.class, new MessageTypeLiteral());
for (Bean<?> bean : messageHandlerBeans)
{
String key = bean.getBeanClass().getAnnotation(MessageType.class).value();
if (!key.isEmpty())
{
CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean);
MessageHandler messageHandler = (MessageHandler) beanManager.getReference(bean, MessageHandler.class, creationalContext);
messageHandlerMap.put(key, messageHandler);
}
}
}
public MessageHandler getMessageHandler(String key)
{
MessageHandler messageHandler = (MessageHandler) messageHandlerMap.get(key);
return messageHandler != null ? messageHandler : defaultMessageHandler;
}
}
#Documented
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface MessageType
{
#Nonbinding
String value();
}
#SuppressWarnings("all")
public class MessageTypeLiteral extends AnnotationLiteral<MessageType> implements MessageType
{
private static final long serialVersionUID = 1L;
#Override
public String value()
{
return "";
}
}
public class DefaultMessageHandler implements MessageHandler
{
#Inject
ResourceBundleProvider resourceBundleProvider;
#Override
public void processMessage(String inputMesssage)
{
...
#MessageType("first")
public class FirstMessageHandler implements MessageHandler
{
#Inject
ResourceBundleProvider resourceBundleProvider;
#Override
public void processMessage(String inputMesssage)
{
...
The #NonBinding annotation in the #MessageType annotation seems to be important to find all beans annotated with #MessageType("xxx") independent of the actual annotation value (here: xxx).
I hope this explains the important things. For further details please ask me
Sebastian
I think your simplest solution to this would be to keep what you have, strip out the scanning because you don't need it, change your annotation to be a qualifier and fire a CDI event with the qualifier (you'll need to create an AnnotationLiteral for each of three different qualifiers because the value is binding) and the message as the payload.
I can explain more if you need it.
See and adjust Dynamically fire CDI event with qualifier with members
It is a CDI way for dynamic runtime selecting services by runtime decision. The TypeEnum can also be a String.

The JSR 303 bean validation, The extended ConstraintValidator cannot use the CDI

I've tried to learn the JSF 2.0 with bean validation at the class level as the following: -
The utility
#Singleton
public class MyUtility {
public boolean isValid(final String input) {
return (input != null) || (!input.trim().equals(""));
}
}
The constraint annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.ANNOTATION_TYPE,
ElementType.FIELD
})
#Constraint(validatedBy = Validator.class)
#Documented
public #interface Validatable {
String message() default "Validation is failure";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The constraint validator
public class Validator extends ConstraintValidator<Validatable, MyBean> {
//
//----> Try to inject the utility, but it cannot as null.
//
#Inject
private MyUtility myUtil;
public void initialize(ValidatableconstraintAnnotation) {
//nothing
}
public boolean isValid(final MyBean myBean,
final ConstraintValidatorContext constraintContext) {
if (myBean == null) {
return true;
}
//
//----> Null pointer exception here.
//
return this.myUtil.isValid(myBean.getName());
}
}
The data bean
#Validatable
public class MyBean {
private String name;
//Getter and Setter here
}
The JSF backing bean
#Named
#SessionScoped
public class Page1 {
//javax.validation.Validator
#Inject
private Validator validator;
#Inject
private MyBean myBean;
//Submit method
public void submit() {
Set<ConstraintViolation<Object>> violations =
this.validator.validate(this.myBean);
if (violations.size() > 0) {
//Handle error here.
}
}
}
After running I've faced the exception as java.lang.NullPointerException at the class named "Validator" at the line return this.myUtil.isValid(myBean.getName());. I understand that the CDI does not inject my utility instance. Please correct me If I'm wrong.
I'm not sure if I'm doing something wrong or it is a bean validation limitation. Could you please help to explain further?
Your right, Hibernate Constraint Validator is not registered as a CDI-Bean by default (and though cannot receive dependencies).
Just put the Seam-Validation module on your classpath, and everything should run fine.
BTW: studying the source-code of the module is an excellent example of the elegance and simplicity of CDI extension. It's doesn't need more than a few dozens lines of code to bridge from CDI to hibernate validations...

Resources