I am following a simple Java EE 6 annotations video on youtube. I have created a repo on github. There is a servlet to start with that creates a greeting string using annotation.
#WebServlet(urlPatterns = {"/HelloServlet"})
public class HelloServlet extends HttpServlet {
#Inject #Formal
String greetingMessage;
...
}
The formal qualifier is defined as follows:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface Formal {}
And the producer is:
public class ProduceFormalGreeting {
#Produces
#Formal
public String GetFormalGreeting(){
return "Good morning !";
}
}
At compile time I get the following message from NetBeans:
Unsatisfied Dependency: No bean matches the inject point
At runtime, the string is null in the servlet.
You have to add beans.xml file to WEB-INF. Now You have bean.xml file.
In ProduceFormalGreeting u have to use javax.enterprise.inject.Produces (not javax.ws.rs.Produces)
Related
The container is Glassfish 4.1
I am having a very weird issue with CDI right now. If I don't annotate my NumberGenerator services #Dependent, then I keep getting unsatisfied injection point error when I run the app. But if I explicitly annotate my NumberGenerator implementations, then everything will work. In one word, if I want dependency injection with the #Dependent which is the default scope, I must specify it explicitly.
public interface NumberGenerator {
String generateNumber();
}
The first implementation of NumberGenerator
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
#ThirteenDigits
#Dependent
public class IsbnGenerator implements NumberGenerator {
#Inject
private Logger logger;
#Override
public String generateNumber() {
String isbn = "13-84356-" + Math.abs(new Random().nextInt());
logger.log(Level.INFO, "Generated ISBN : {0}", isbn);
return isbn;
}
}
The second implementation of NumberGenerator
import java.util.Random;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
#EightDigits
#Dependent
public class IssnGenerator implements NumberGenerator {
#Inject
private Logger logger;
#Override
public String generateNumber() {
String issn = "8-" + Math.abs(new Random().nextInt());
logger.info("Generated ISSN : " + issn);
return issn;
}
}
This is where the NumberGenerator will be injected
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.interceptor.Interceptors;
#Dependent
public class BookService {
#Inject
#EightDigits
private NumberGenerator numberGenerator;
public Book createBook(String title, float price, String description) {
Book book = new Book(title, price, description);
book.setNumber(numberGenerator.generateNumber());
return book;
}
}
Finally, the BookService is injected into this JSF managed bean to create a Book instance.
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#Dependent /* if I leave this out, then this bean will not display
the book instance properties on the JSF page, I just see
a blank screen, but when I add this #Dependent annotation
the JSF page displays the dummy content below.
*/
public class MyBean {
#Inject
private BookService bookService;
public Book getBook() {
return bookService.createBook("Dummy Title", 21.05f, "Dummy Description");
}
}
As you can see, I have to use #Dependent for the default scope every time I want DI. Right now, I am injecting the IssnGenerator with the qualifier #EightDigits into the BookService class, and if I remove the #Dependent from the IssnGenerator, I receive this compile error.
Unsatisfied dependencies for type NumberGenerator with qualifiers #EightDigits at injection point [BackedAnnotatedField] #Inject #EightDigits private BookService.numberGenerator
Thank you for any suggestion.
If you do not specify a META-INF/beans.xml file, which seems to be your case, you got an implicit bean archive (see CDI-1.1 §12.1).
In an implicit bean archive, only beans with a bean defining annotation will be discovered by the CDI engine.
Any scope is a bean defining annotation (see CDI-1.1 §2.5). That's why if you add a scope, like #Dependent, you bean gets discovered by CDI and you don't have the unsatisfied dependency error.
The fact that #Dependent is the default scope is not relevant here because without the any scope your beans is simply not discovered.
If you add a META-INF/beans.xml file with the bean-discovery-mode set to all, then you got an explicit bean archive, this means that all beans in your archive will be discovered and will have the #Dependent scope by default.
I'm stuck with the infamous WELD-001408 that everyone programming with CDI has come across in one way or other. Using Oracle JDK 1.8.0_25, Wildfly 8.2.0.Final. My code is as follows:
module availability-service (war) depends on module hospital-user (jar). Former has a beans.xml in WEB-INF and later in META-INF, even though CDI 1.1 doesn't require a beans.xml.
In hospital-user:
#ApplicationScoped
public class Users {
#Produces
#Doctors
public List<Doctor> getDoctors() {
return getUsers("/doctors.json", Doctor.class);
}
#Produces
#Patients
public List<Patient> getPatients() {
return getUsers("/patients.json", Patient.class);
}
}
Doctors annotation (Patients is similar except for the name):
#Qualifier
#Retention(RUNTIME)
#Target({ FIELD, METHOD })
public #interface Doctors {
}
In availability-service:
#ApplicationScoped
public class AvailabilityService {
#Inject
#Doctors
private List<Doctor> doctors;
}
Error:
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type List<Doctor> with qualifiers #Doctors
at injection point [BackedAnnotatedField] #Inject #Doctors private name.abhijitsarkar.microservices.availability.AvailabilityService.doctors
at name.abhijitsarkar.microservices.availability.AvailabilityService.doctors(AvailabilityService.java:0)
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:372)
If I inject the Users instead of the List, it works. Here is a sample Maven project that demonstrates the problem with 4 classes. Just run mvn clean test.
In the sample Maven project that you provided, dependency injection in the test class isn't available as you specified your Arquillian deployment as #Deployment(testable = true).
When set to true, which is the default, the test passes.
Besides, adding the following injection point in the test method:
#Inject
#Employees
List<Employee> employees;
works fine which demonstrates that your bean deployment is valid.
It continues to work when the Arquillian deployment matches exactly the application WAR structure that you've depicted, i.e.:
#Deployment
public static WebArchive createDeployment() {
return create(WebArchive.class, "availability-service.war")
.addAsLibraries(create(JavaArchive.class, "hospital-user.jar")
.addPackages(true, Filters.exclude(".*Test.*"), Producer.class.getPackage())
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"))
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
my environment is: Maven 3, JSF 2, Tomcat 7, Java 6. I am trying to use the java #Named annotations for my beans, that I use in JSF. The project is simple, with one TestBean that has one getter and setter for one property called text.
#Named
public class TestBean {
String text = "Hello World"
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
}
In my jsf file when I try:
#{testBean.text}
I get no error but also nothing is shown. What do I have to do to be able to access the bean?
#Named annotation only exposes your bean in EL context, it doesn't give it a scope. This defaults to 'dependent' pseudo-scope. Dependent beans are not managed, they are just inserted at injection points and that's it.
Try explicitly adding scope. Also you can use built-in #Model stereotype, which is simply a combination of #Named and #RequestScoped.
When using CDI and JSF2 How can a HTTP request parameter be injected into a bean?
HINT: before reading any further have a look at http://showcase.omnifaces.org/cdi/Param.
Do it yourself is probably obsolete seeing how omnifaces is a de facto standard today. I would probably not have written this if omnifaces had this at the time
CDI does not solve specialized problems like injecting a request parameter. That's supposed to be solved by extensions.
This is already provided by solder. http://docs.jboss.org/seam/3/solder/latest/reference/en-US/html/injectablerefs.html
It will probably be included in Deltaspike 0.4-incubating or similar as well.
That said the code required is rather simple to implement yourself. Example below:
Annotation to use for the injection point (For example private String myParam;)
import javax.enterprise.util.Nonbinding;
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.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER })
public #interface RequestParam {
#Nonbinding
public String value() default "";
}
Now we have the annotation but we can't just ask the container to dependency inject a #RequestParam - we obviously need an implementation.
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
public class RequestParamProducer implements Serializable {
private static final long serialVersionUID = -4260202951977249652L;
#Inject
FacesContext facesContext;
// Producer for #RequestParam
#Produces
#RequestParam
String getRequestParameter(InjectionPoint ip) {
String name = ip.getAnnotated().getAnnotation(RequestParam.class)
.value();
if ("".equals(name))
name = ip.getMember().getName();
return facesContext.getExternalContext().getRequestParameterMap()
.get(name);
}
}
So how does it work? Well quite simply it first checks if you did specify what parameter you wanted as in #Requestparam("longAndTerribleFieldNameBestToSpecify");
If you didn't it will use the fieldName. So if you annoted a setter called setMyInstance it will look for a parameter called setMyInstance.
The normal use case would be to have a String variable that is named exactly like the parameter you want.
Note that we inject FacesContext, that must also be produced. A FacesContext producer could look like this:
class FacesContextProducer {
#Produces #RequestScoped FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
}
End usage:
#Inject
#RequestParam
private String session_secret;
Note that this will not work for Servlet or similar as it requires access to FacesContext. In those cases one need to wrap the injection with for example a bean that is #RequesScoped. You inject that bean instead.
By default, grails seems to return <class name>:<id> for toString() of an Java domain object. That's not at all what I want of course, so I tried to #Override
the toString() to return what I want. When I tried grails generate-all Tagtype, I got the following error:
java.lang.LinkageError: loader constraint violation: loader (instance of <bootloader>) previously initiated loading for a differen
t type with name "org/w3c/dom/NamedNodeMap"
My code is below. Any help would be greatly appreciated.
#Entity
#Table(name = "tagtype", catalog = "tigger")
#SuppressWarnings("serial")
public class Tagtype implements Serializable {
/**
* Attribute id.
*/
private Integer id;
/**
* Attribute tagtype.
*/
private String tagtype;
/**
* Attribute regexpression
*/
private Regexpression regexpression;
. . .
#Override public String toString() {
StringBuilder result = new StringBuilder();
result.append(this.tagtype);
return result.toString();
}
}
I've overriden toString() in Grails domain classes without any problems, so that can't be the reason. This blog suggests it could be a result of name collisions, either temporary (have you tried running "grails clean"?) or perhaps your class name Tagtype collides with some grails internals.
Another thing you could try is using different versions of Grails, especially the latest 1.1.1 if you aren't already using that. This ML post describes an identical error message that was apparently version dependant.