Saving and using a cookie with CDI - jsf

I am trying to save a Cookie upon user request, and to later use that cookie to fill in a text field. I am using Java CDI and a login bean. I am new to all three. For online resources all I can find is
#Inject #CookieParam
private String username;
and
#Inject #CookieParam("username")
private Instance<String> usernameResolver;
...
String username = usernameResolver.get();
For the first one the error message says " Unsatisfied dependencies for type [String] with qualifiers [#Default]"
For the second one the only error I get says "Failed to start context"
How should I fix this problem?
Thanks

As the #CookieParam package name hints, this is specific to JAX-RS, Java EE's other framefork for RESTful web services. This would only work in a JAX-RS managed resource as annotated by #Path. This won't work in a JSF or CDI managed bean as annotated by #ManagedBean or #Named.
If you're using JSF's #ManagedBean to manage the bean, then it's available by EL-evaluating #{cookie.username} as #ManagedProperty.
#ManagedBean
public class Bean {
#ManagedProperty("#{cookie.username}")
private String username;
// ...
}
If you're using CDI's #Named to manage the bean, then you've resort to either a custom annotation, or grabbing it as Cookie from the current FacesContext. As the former is not trivial (but actually a nice idea for OmniFaces though), I'll show only the latter:
#Named
public class Bean {
private String username;
#PostConstruct
public void init() {
Cookie cookie = (Cookie) FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap().get("username");
if (cookie != null) {
username = cookie.getValue();
}
}
// ...
}
Then, in order to save it, the only way is using ExternalContext#addResponseCookie().
FacesContext.getCurrentInstance().getExternalContext().addResponseCookie("username", username, properties);
Don't forget to take into account that a cookie value is very restrictive as to allowed characters. You might want to URL-encode and -decode it upon save and retrieval. JSF utility library OmniFaces offers helper methods which do that implicitly.
username = Faces.getRequestCookie("username");
Faces.addResponseCookie("username", username, -1);
Unrelated to the concrete problem, storing something sensitive like "user name" as a cookie is scary. Are you aware that cookies are manipulatable by the enduser? That the person who's visiting your webpage can easily edit a cookie value representing an "user name" to that of someone else?

Related

CDI Bean Scoping during Injection is not working

I want to give on-the-go scope to a pojo bean in CDI during injection.
I created a plain bean and injected the same as #javax.enterprise.context.ApplicationScoped in a #javax.faces.bean.ViewScoped Managed Bean like this:
#Inject
#ApplicationScoped
Pojo pojo;
// POJO Class
Class Pojo {
private String var;
public Pojo() {
}
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
}
The Pojo bean's populated values could not be restored in a new view bean when I inject using the same syntax.
But it works when I use #ApplicationScoped in the class declaration instead, followed by non-scoped injection, like this:
#ApplicationScoped
Class Pojo {
private String var;
Injection:
#Inject
Pojo pojo;
The former case gets resolved when I make a producer and qualifier but I feel this would be an overhead I should do without. Being new to CDI, I want to ask what I am really missing here.
Scope and Context management are a very powerful feature in CDI. It is also part of the business logic of the components (an #ApplicationScoped bean won't be developed the same way than a #RequestScoped), that's why the scope is link to bean definition.
An injection point is only a place where you consume a bean and not a place where you define it, so there is no way to define the scope of a bean at the injection point at spec level.
Now if you really want to use this feature you could develop a portable extension to add this possibility. But you'll probably have to work on a qualifier system as well since scope is not used in bean resolving process (i.e. 2 beans with the same type in different scope will be in conflict for a given injection point if they don't have specific qualifier).

How to get all instances of a particular cdi session scoped bean

I have a #SessionScoped cdi bean which is used to track user session information in my web application. Is there any way to find all objects of this bean from another #ApplicationScoped bean?
You cannot do this out of the box. Java EE forbid this kind of things for security reason.
Now you can imagine a more elaborate approaches to keep track of these session beans at your application scope level. The cleanest way would be to produce them from an #ApplicationScoped bean :
#ApplicationScoped
public class Registry {
private List<SessionData> data = new ArrayList<>;
#Produces
#SessionScoped
public SessionData produceSessionData() {
SessionData ret = new SessionData();
data.add(ret);
return ret;
}
public void cleanSessionData(#Disposes SessionData toClean) {
data.remove(toClean);
}
}
Note the #Dispose method which will be called when your produced bean has ended its lifecycle. A convenient way to keep your list up to date and avoid extra memory usage.

How to add text in Facelets

Im new to web programming so this is a beginner question.
In my web application which is a maven project with JSF framework(university project), I have some pages with just text that displays various information about my fake air line company(only consists of <p> and <h1>). Now, to my question. Should I just "hard code" the information on the JSF Page or should I use beans to get my text and titles from?
The information that will be on my info pages will remain the same and never change.
If this question is inappropriate to ask here, please let me know and Ill remove it.
Since you've stated that the information will never change, storing it in a string in the bean class would work, and use getter methods to retrieve the data
#ManagedBean
#SessionScoped
public final class Airlineimplements Serializable
{
private static final long serialVersionUID = 47493274L;
private String title = "Air Canada";
private String headquarters = "Toronto Ontario Canada";
public Airline()
{
}
public String getTitle()
{
return title;
}
public String getHeadquarters()
{
return headquarters;
}
}
This is #RequestScoped so that you retrieve the information on each request and the information is garbaged after the request.
A #RequestScoped bean will be garbaged by end of every request and recreated on every new request.
Full answer here about #ViewScoped vs #RequestScoped
Difference between View and Request scope in managed beans
Although this should be #SessionScoped which keeps the information for the life of the session.
For the Serializable UID, the serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to ensure that the caller and receiver of a Serialized object have the same loaded classes.
More information about Serializable
http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html
Here are some additional tutorials on JSF for beginners
http://www.tutorialspoint.com/jsf/
http://www.vogella.com/tutorials/JavaServerFaces/article.html

Update a view scoped JSF beans' injected bean

#SessionScoped public class User {
... //settings, attributes, etc
}
#ViewScoped public class FooController {
#ManagedProperty(value="#{user}")
private User user;
...
}
#RequestScoped public class LoginController {
#ManagedProperty(value="#{user}")
private User user;
public String login() {
//handle Servlet 3.0 based authenticate()... if success, make new User object and slap it into context
request.getSession().setAttribute("user", user);
return "?faces-redirect=true";
}
...
}
xhtml pages include the login controls on pretty much every page. The ideal would be that they are able to login, and the page will refresh and the existing FooController would have the reference to the currently logged in user, which conditionally renders buttons / elements. The behavior is that the login occurs, but the FooController view is still "valid", so the managed bean never is attempted to be injected again. If I navigate off the page, and back onto it [reconstructing the view scoped bean] the user bean is reinjected nicely... but I'd prefer not to have that interim step. Any ideas?
I've tried various forms of FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("user"); in hopes that it will re-pull it from session but no avail. I do not want to tightly couple code in my LoginController to reference specifically invalidating a FooController or BarController or any other that refs the user bean.
Why are you trying to remove user (a #SessionBean), from the viewMap (a map of #ViewScoped objects in the current session)?
You should be removing FooController, the #ViewScoped, bean from the viewMap, i.e.
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("fooControl‌​ler");.
This is what will get rid of the viewscoped bean and force a new one to be created. Then of course, you'll need to refresh the page anyway.
If you're intent on removing the session bean however, you should be accessing the session directly:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove("user");.
That gets rid of the user object
Ok, I figured this out on the drive home, it was a lifecycle problem. I was trying to get JSF to do something it shouldn't be doing to managed beans.
Instead of new'ing up a User object and reassigning it to the managed instance of user in LoginController, i changed the login method to look like this:
public String login() {
//handle Servlet 3.0 based authenticate()... if success...
User loadedFromDB = someDao.load(principal.getName());
user.setDefaultPage(loadedFromDB.getDefaultPage()); // NOTE: The user object IS THE MANAGED BEAN
user.setDefaultScheme(loadedFromDB.getDefaultScheme()); // This is the same object the view scoped bean has a ref on, so directly setting that object's fields proliferates that to any other bean that has the user in scope.
... //etc... not calling NEW, not reassigning object ref to user
loadedFromDB = null;
return "?faces-redirect=true";
}
This accomplishes what was needed. Who knew, if you stop fighting the framework for a minute and just use it, it helps you out :)

Bean annotations like #ManagedProperty and #PostConstruct doesn't work when manually instantiating the bean from another bean

I instantiated a request bean from another request bean,
new LoginManager();
But the property which is annotated with #ManagedProperty doesn't get the value from the asked reference, only in case of instantiation through the above way. It just contains null, causing NPE later in code. Also #PostConstruct won't be invoked. Why is it so & how should I deal with this?
#ManagedBean(name = "loginManager")
#RequestScoped
public class LoginManager {
private String userid;
private String password;
#ManagedProperty(value="#{currentSession}")
private UserSessionManager userSession;
}
But userSession can't read from the session scoped bean when this bean was instantiated using: new LoginManager();
However I can read the value using FacesContext!
You should not manually instantiate (manage) beans using new operator. You should let JSF do the job of managing the beans and instead grab the JSF-managed (JSF-instantiated) instance.
Either by #ManagedProperty in the bean where you need it:
#ManagedProperty("#{loginManager}")
private LoginManager loginManager;
Or by invoking EL programmatically (which is pretty borderline in your particular case):
LoginManager loginManager = context.getApplication().evaluateExpressionGet(context, "#{loginManager}", LoginManager.class);
// ...
If you insist in instantiating and managing the bean yourself, you should do all dependency injections yourself, also invoking the #PostConstruct yourself, if any, and finally also putting the bean in the desired scope yourself. E.g.
LoginManager loginManager = new LoginManager();
loginManager.setUserSession(userSession);
// Now use reflection to find and invoke #PostConstruct method.
// Finally store in the map which conforms the bean's scope.
externalContext.getRequestMap().put("loginManager", loginManager);
This boilerplate is exactly what JSF is supposed to take away from your hands. Make use of it.

Resources