WeldSE with JUnit5 - Can't inject dependencies - java-ee-8

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.

Related

Problems in mocking a factory in micronaut

I am fairly new to micronaut and currently i am stuck in a strange situation where i am unable to mock a factory for a value.
So i have an interface
#Qualifier
#Retention(RUNTIME)
public #interface SomethingToInject {
}
and then i have a factory for this where i am annotating a method with #SomethingToInject
::
#Factory
public class MyFactory {
private String someValue;
#Singleton
#SomethingToInject
public String getSomeValue(){
return someValue;
}
#Scheduled(fixedDelay = "24h", initialDelay = "5s")
public void refreshSomeValue() throws IOException {
// An api call to fetch this new SomeValue
SomeValue = apiResponse;
}
}
Now I am injecting this to wherever i wish to use this someValue
For example
#Singleton
public class MyService{
#Inject
#SomethingToInject
private String someValue;
// service functions...
}
but when i am writing tests for MyService functions it says :
io.micronaut.context.exceptions.BeanInstantiationException: Error instantiating bean of type myService
Message: Bean Factory MyFactory returned null
how do i mock the factory? I have tried to mock the factory with mockbean and also with replaces but maybe i am missing a trick here.
I am guessing the problem here is that the #SomethingToInject is has retention runtime?
my tries in MyServiceTest:
#MockBean(SomethingToInject)
SomethingToInject somethingToInject(){
return mock(SomethingToInject.class)
}
#Factory
#Replaces(MyFactory.class)
public class CustomFactory {
private String someValue;
#Singleton
#SomethingToInject
public String getSomeValue(){
return "dummyValue";
}
}
but it doesn't work as it throws the same error of injection not working with additional message of :
CustomFactory: method 'void <init>()' not found

Micronaut #Replaces with declarative Client

I am going to use the code from Micronaut Documentation (Declarative Http Client)- And I'm using Spock
PetOperations.java
#Validated
public interface PetOperations {
#Post
Single<Pet> save(#NotBlank String name, #Min(1L) int age);
}
I have a declarative client:
#Client("/pets")
public interface PetClient extends PetOperations {
#Override
Single<Pet> save(String name, int age);
}
My goal is when I run a test class, I want to call (#Replaces) another class (PetDummy) instead of the PetClient, PetDummy class is located in my test folder
#Primary
#Replaces(PetClient.class)
#Singleton
public class PetDummy implements PetOperations {
#Override
public Single<Pet> save(String name, int age) {
Pet pet = new Pet();
pet.setName(name);
pet.setAge(age);
// save to database or something
return Single.just(pet);
}
}
test class:
class PetTest extends Specification {
#Shared
#AutoCleanup
ApplicationContext applicationContext = ApplicationContext.run();
//EmbeddedServer server = applicationContext.getBean(EmbeddedServer.class).start();
PetClient client = applicationContext.getBean(PetOperations.class);
def 'test' (){
given: 'name and age'
when:
client.save("Hoppie", 1);
then:
noExceptionThrown()
}
}
However, at the end PetClient is called, I have as well tried with the #Factory annotation, but no success
PetClient extends PetOperations and PetDummy implements PetOperations, if they both implement then it will make sense to use #Replaces ...
Is there something else I can try out?
Thank you!
Another Issue:
Now that it works, the PetClient is a dependency in my PetService. When I test my PetService, it still calls the PetClient instead of the PetDummy.
I assume it has to do with the applicationContext, you will see
PetService:
PetService {
#Inject
PetClient client;
buyFood(){
//...
Single<Pet> pet = client.save("Hoppie", 1));
}
}
PerService Test:
class PetServiceTest extends ApplicationContextSpecification {
#Subject
#Shared
PetService petService = applicationContext.getBean(PetService)
PetOperations client = applicationContext.getBean(PetOperations.class) //client is not used here
def 'test' (){
given:
when:
petService.buyFood()
then:
noExceptionThrown()
}
}
I think that I need to "get into" the applicationContext from the PetService, to tell "use the PetDummy" implementation (Inside the test class, because the ApplicationContextSpecification belong to another module
The ApplicationContextSpecification is:
abstract class ApplicationContextSpecification extends Specification implements ConfigurationFixture {
#AutoCleanup
#Shared
ApplicationContext applicationContext = ApplicationContext.run(configuration)
/* def cleanup() {
assert !hasLeakage()
}*/
}
The ConfigurationFixture contains the properties for the database(hibernate)
You are already retrieving the PetClient bean implementation:
PetClient client = applicationContext.getBean(PetOperations.class);
Which should provide the replacing dummy bean implementation if called with the appropriate type:
PetOperations client = applicationContext.getBean(PetOperations.class);

jBeret + Weld SE - Inject managed bean from Batchlet

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

multiple ejb injections which implement the same interface

i am very new to this ejb stuff. is there any possibility that in a single file i can have multiple injections based on some criteria.
for eg
public interface common(){
public void sayhello();
}
beanA
implements common()
beanB
implements common()
both are stateless beans
now i have a client which needs to trigger hello method based on some criteria. for eg. say based on console input if string contains A then beanA should be injected otherwise beanB.
Is there any possibility? and again my next question is , can i say this dynamic injection is not managed by container? if so how can i let container take the control? i need a sample code or atleast any tutorial ref.
thanks in advance!!
No, this is not really possible. You might be able to get close with a custom CDI scope that uses a thread local or session attribute, but I wouldn't recommend it. Instead, just inject a reference to both EJBs, and select the one to use as needed:
#EJB(beanName="BeanA")
Common beanA;
#EJB(beanName="BeanB")
Common beanB;
private Common getCommon(String input) {
return isBeanAInput(input) ? beanA : beanB;
}
you could do something like this:
public interfaces ICommon {
public void sayhello();
}
#Stateless
#LocalHome
public class BeanA implements ICommon {
public void sayhello() {
// say hallo
}
}
#Stateless
#LocalHome
public class BeanB implements ICommon {
public void sayhello() {
// say hallo
}
}
and here the CDI "client" which uses the EJB services
#Model
public void MyJSFControllerBean {
#Inject
private BeanA beanA;
#Inject
private BeanB beanB;
public String sayhello(final String input) {
if("a".equals(input)) {
beanA.sayhello();
} else {
beanB.sayhello();
}
return "success";
}
}
Or the other solution would be that you create a CDI producer to create this. but then you are mixing two different concepts. but i think it depends ou your concrete usecase.
dynamic injection does not exist! with #Produce and #Qualifier you can control the creation of the required CDI beans to inject. but this is only for CDI not for EJB.
here the CDI producer example:
public void ICommonProducer {
#EJB
private BeanA beanA;
#EJB
private BeanB beanB;
#Produces
public ICommon produce() {
final String input = "?????";
// but here you have the problem that must get the input from elsewhere....
if("a".equals(input)) {
beanA.sayhello();
} else {
beanB.sayhello();
}
}
}
#Model
public void MyJSFControllerBean {
#Inject
private ICommon common;
public String sayhello(final String input) {
common.sayhello();
return "success";
}
}
i have not teseted this code...

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