Building Upon Context and Dependency Injection and Dependency Injection for Java
The new Java EE 6 plaftorm specifation has lots of new thing wort having a look at. In an attempt to write a JSF 2 application, where i didn't want to use the optional ManagedBean, ManagedProperty and related annotation, I needed to look into JSR 330 and JSR 299. There is some documentation available on both of these specs if you look arround on the internet.
In this small example I'll demonstrate how we can change a
To execute this project I installed a
JSF2 brings even without JSR 330 and JSR 299 lots of advantages to the
table, one of them being XML-less configuration of managed beans and managed
properties. The only problem is, is that the annotations are actually left
as optional because an attempt was already being made to unify dependency
injection in JSF. That is what convinced me to look a little bit deeper
in JSF.
Down under you see a JSF 2 managed bean using the optional annotations.
We are going to try and replace the
I think the above code is relatively straightforward for someone who has some experience with
As a side-note I might note that the same could be achieved by a
Anyway! The first thing we will accomplice is replacing the
Replacing the
Since at the moment we don't need the lifecycle management and the dependency injection mechanism is specified in JSR 330 it's suffices to specify an
Notice I'm using the
It is also worth noting that there is a
Now, on to the more difficult part. Replacing the injection of the request parameters. I was inspired to try this based on the mentioning of the The sample code for the annotation The sample code for a producer and a sample on how to use it
Lets build the solution step by step, so we can get this working. You'll probably have to read through the entirety because there will be many gaps in your knowledge that will only be explained later on. (Or you could read the almost unfindable updated specification.) Lets first see where we want to end. This wil be the end result, and will work as far as I know like the managed bean we started with.
There are something you should immediately see, and be able to gasp. First of all there is an
The magic here is in the
JSR 299 inherited the concept of "Producers"/"Managers"/... from Seam. Lets dive into the code first and then explain.
As you see, we made a request scoped class with one special method, which will need lots of explaining. The first thing is the
The thing to get, is that every producer method is also a injection point, meaning the same as
The implementation itself is easy to comprehend. I ask for the annotated method/field/whatever, get the specific annotation i want, and the get the value of the value property of the annotation. I use that value to get a certain parameter from the request object.
Two thing need more explaination. WTF is the
It is a basic annotation, with two extra's. First of all, annotations that are going to be used for dependency injection, need the
So! Last! Magic! Trick! Where does the HttpServletRequest come from? Lets look at an extra piece of code. I used it purely for the lolz.
So actually, as you should see and understand from the previous explanation: a producer method is also an injection point. And here you have a method that produces a HttpServletRequest. There are no annotations here, the moment it sees the need for a HttpServletRequest, this method gets called and injection happens.
Ok, thats it, the last part went a bit faster and was shorter in explanation, but I'm from Belgium and we have some seriously wicked beer ;). Contact me if you want more info. (Or read the specification and the rest of the internet while you're on it.)
@ManageProperty(value="#{params.id}") annotation on a managed bean with an @ManagedBean("demo") annotation to a custom Qualifier annotation as specified in JSR 330: "Dependency Injection for Java". We'll also look at how we van replace the @ManagedBean annotation to the correct javax.inject annotations.
Glassfish v3 b72
. Previous builds seem to have a problem with classloading, in specific
the loading of classes proxied bij javaassist, in the OSGI environment
that glassfish is nowadays. As a Netbeans user, using Netbeans 6.8 beta
all I had to do was create a new project with JSF support and add the
weld-osgi-bundle.jar
to the libraries. If you wanted to do this in Eclipse or ant you would
have to add the other Glassfish libraries as you would normally do.
@ManagedBean
and
@ManagedProperty
annotation. I personally like to try and keep my managed beans as simple
as possible, and especially try to avoid dependency in specific ways of
accessing parameters and other objects.
package be.verborgh.demo;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
@ManagedBean(name="loadSomething")
@RequestScoped
public class LoadSomethingAction {
@ManagedProperty(value="#{params.id}")
private String id;
public String getId() {
return (String) id;
}
public void setId(String id) {
this.id = id;
}
public Object getSomething() {
/* LOAD SOMETHING BY IT ID */
return null;
}
}
faces-config.xml.
The only thing most people don't use is the #{param.id} value expression. "param" is an implicit object available in JSP and the expression language. It allows access to the keys that are submitted through a form or a querystring. Here it makes sure that when the bean is instantiated, the id parameter which is passed in the querystring by for example the new <h:link> tag in JSF2.
viewParam in a metadata section of a page. But as mention I personally prefer not to have any "lifecycle bindings" in my code. (This might be due to the fact that I'm "Missing-The-Point" there.)
@ManagedBean and the @RequestScoped annotations from JSF.
Replacing the @RequestScoped annotation is as simple as replacing the import of javax.faces.bean.RequestScoped by an import of javax.enterprise.context.RequestScoped. This is part of the Context and Dependency Injection specification. This specification was known as web beans( which came forth from seam etc... .) It specified among many things a basis for unifying Enterprise contexts; of which you probably know application , request, session, conversation and none scope; and away of injecting objects from this scope
@ManagedBean annotation also isn't that difficult but I have some remarks on the specification here(, but then again, who am I?). A managed bean did three things in the old specification. First it allowed the use of the very basic JSF Dependency Injection mechanism; second, it allowed the use of lifecycle methods like @PreDestroy and @PostConstruct; lastly, it provided a means of accessing the bean by a name from for example a value or method expression in EL.
@Named annotation. The only thing this annotation does is binding a name to the bean. If we don't specify a value, it just takes the class name. Since I prefer my value expressions with a lowercase I use @Named("loadSomething"). That leaves us with the following type declaration.
package be.verborgh.demo;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named("loadSomething")
@RequestScoped
public class LoadSomethingAction {
/* ... */
}
@Named annotation from javax.inject. Depending on what specification you're reading this might differ. As far as my reading comprehension and knowledge goes, this is the right choice.
@ManagedBean annotation that is defined in the maintance review of JSR 250, common annotations, that says a bean with this annotation will support basic lifecycle management and dependency injection. That last part, the dependency injection seems incorrect at the moment, since in this case, as you will see, we are already able to use the dependency injection from JSR 330 without the managed bean definition. I can only conclude that JSR 330 will define dependency injection, and doesn't have a type annotation for it, and we will get a lifecycle annotation in JSR 250 update. (It is on my todo list to see if i already can use this annotation and the lifecyle annotations like @PreDestroy and @PostConstruct.)
@HttpParam annotation in the specification of web beans. This seemed to be nothing more than a mere unimplemented example. It led me to go through the seam 3 and noticing they where nowhere yet! this is the example they give. For those that write programs by means of the copy and the paste: of course this won't work since it is outdated, and only part of the old solution
@BindingType
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface HttpParam {
@NonBinding public String value();
}
class HttpParams
@Produces @HttpParam("")
String getParamValue(ServletRequest request, InjectionPoint ip) {
return request.getParameter(ip.getAnnotation(HttpParam.class).value());
}
}
@HttpParam("username") String username;
package be.verborgh.demo;
import be.verborgh.project.framework.Parameter;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
@Named("loadSomething")
@RequestScoped
public class LoadSomethingAction {
@Inject
@Parameter("id")
private String id;
public String getId() {
return (String) id;
}
public Object getSomething() {
/* LOAD SOMETHING BY IT ID */
return null;
}
}
@Inject annotation from JSR 330. As you might have guessed: this is the way you define that something needs to be injected. Related to that you might also notice I removed the setter. (Shame on me for destroying testability, - no realy, but I won't go into the details - ) JSF still needs a setter to inject something, JSR 330 doesn't. The weld implementation as far as I have noticed on the initial problems I had on old Glassfish v3 builds is that I uses javaasist to inject the fields.
@Parameter annotation.JSR 330 is inspired on Guice(, or if you talk to Gavin King on Seam.) But anyway, Guice had its influence, and the members of the JSR wanted a typesafe way of working. The 1 million dollar question here is: "what gets injected?". Anyone with a half decent pea for a brain will notice it's going to be a String! But where do we get the String from?(another million dollar question - but to be honest, while writing this I might have started with an easier example.)
package be.verborgh.project.framework;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.servlet.http.HttpServletRequest;
@RequestScoped
public class ParameterManager {
@Produces
@Parameter("")
public String getParameter(HttpServletRequest request, InjectionPoint ip) {
return request.getParameter(ip.getAnnotated().getAnnotation(Parameter.class).value());
}
}
@Produces annotation. The most easy part! The method will produce something, in this case a String, which will be placed in the same scope of containing object, (you could specify a different scope on the method itself, e.g. @SessionScoped, to indicate that the object should end up in a different scope.)
@Inject would have been specified. It is forbidden to explicitly specify @Inject, even though to me, it would have been clearer(, and it's java, we have IDE's, code completion, and we know how to type with ten fingers, not like those other "scripting" languages :-) ). Here, we get a HttpServletRequest object and an InjectionPoint. The request object is more magic, the ip object is specified. Each Producer can get an InjectionPoint which allows the method to interrogate to "point-of-injection".
@Parameter annotation for on both places and where does the HttpServletRequest come from? Lets start by looking at the annotation. It is clear here that a field of the type string annotated with a @Parameter annotation get injected by something that comes from a producer of a string marked with a @Parameter annotation. Lets have a look at the annotation.
package be.verborgh.project.framework;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.enterprise.inject.Nonbinding;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Parameter {
@Nonbinding public String value();
}
@Qualifier annotation. (I personally don't see why we can't just use any annotation we want.) The second thing you notice is the @Nonbinding annotation on the value attribute. This is just a way of indicating that this attribute should not be used when deciding what to inject. In normal cases the decision proces includes the value of the annotation attributes. This allows as the specification indicates to use e.g. @PayBay(CHEQUE) in the decision.
package be.verborgh.project.framework;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
@RequestScoped
public class RequestManager {
@Produces
@RequestScoped
public HttpServletRequest getRequest() {
return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
}
}