I still get confused about the PAGE and the CONVERSATION (temp) scope. Maybe I get some help here.
As fas as I know, variables outjected to the PAGE scope live as long as the user only postbacks the same page. The temporary CONVERSATION scope instead even survives a redirect to the next page.
Here is a little example with two effects that are confusing to me:
First, component and outjections are in CONVERSATION scope and the tempUser data is displayed in a jsf page. But in the save method called from that jsf-page, the injected tempUser is null. Why?
Second, if I do the same but change component and #In/#Outs scopes to PAGE scope, the tempUser gets correctly injected on postback - but gets not saved, for wathever reason, although even the super.update()-method on userHome gets called. Or is there a problem in using the homeEntities that ways (the idea iwa to use them only as DAO wrapper)?
#Name("userAction")
#Scope(ScopeType.CONVERSATION)
public class UserAction implements Serializable {
private static final long serialVersionUID = -4852371546895918692L;
#In(create = true)
private UserHome userHome;
#Out(scope = ScopeType.CONVERSATION)
#In(required = false,scope = ScopeType.CONVERSATION)
User tempUser;
#RequestParameter
private Long userId;
#Factory("tempUser")
public User getUser() {
if (tempUser == null) {
userHome.setUserId(userId);
tempUser = userHome.getInstance();
userHome.clearInstance();
}
return tempUser;
}
public void save() {
userHome.setInstance(tempUser);
userHome.update();
}
}
The xhtml contains a a:form with
<a:commandButton
id="update"
styleClass="button admin"
action="#{userAction.save}"
value="#{messages['user.action.update']}"/>
Thanks for replies. Sorry, if this is two problems in one.
Related
I need a little direction understanding sessions in JSF (2.2.6). I've tried to find some documentation but am still missing something.
I have a #RequestScoped login bean which saves parameters in the session map for reference by other session scoped backing beans. They get the user info when they go through the PostConstruct method and everything works great.
However the logic fails when more than one window is used or the user doesn't logoff and goes directly back to the login page. JSF treats this as the same session and the #PostConstructs are not invoked.
I am pretty sure I could invalidate the session but that doesn't solve the problem of multiple users from different browser windows.
Any guidance or reference sites would be appreciated.
Thanks in advance!
John
session HAS to be same for every browser window, the only exception is when using anonymous mode: i.e. chrome behave like having two browsers opened at the same time.
another way to have multiple sesssions is to use different server name:
http://localhost:8080/app and http://127.0.0.1:8080/app may not share a single session.
however sessions never overlaps.
your problem, if i understand right, is when a logged user access login page and re-login, preserving his old session, that's why session beans are not PostConstructed again (independently from window used).
a general solution is to forbid access to login page for logged users.
and in general, container will throw an AlreadyAuthenticatedException or similar when user re-login without prior logout.
cut a long story short, just a preliminary example waiting for your code:
#ManagedBean
#SessionScoped
public class UserBean implements Serializable
{
private static final long serialVersionUID = 1L;
private User user;
public boolean isLoggedIn()
{
return user != null;
}
public void login(String username, String password)
{
// maybe you want to check isLoggedIn() and either call logout() or throw an exception
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
request.login(username, password);
user = someDAO.loadFromDatabase(username);
}
public void logout()
{
// maybe you want to check isLoggedIn() and either throw an exception or do nothing
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
request.logout();
user = null;
// suggested for another scenario
// request.getSession().invalidate();
}
// getters and setters
}
and
#ManagedBean
#SessionScoped
public class OperationBean implements Serializable
{
private static final long serialVersionUID = 1L;
#ManagedProperty("#{userBean}")
private UserBean userBean;
public void execute()
{
if(!userBean.isLoggedIn())
{
FacesContext.getCurrentInstance().getExternalContext().redirect("login.jsf");
return;
}
User user = userBean.getUser();
// do something
}
// getters and setters
}
with this combination, instead of using OperationBean's #PostContruct i used #ManagedProperty, so that OperationBean contains an always-up-to-date reference to user, without caring multiple re-logins.
Simple question from a beginner at JSF:
I have very simple JSF form:
<h:form>
<p>#{messages.loginTextfieldUsername}</p>
<h:inputText value="#{userServiceImpl.user.name}" />
<p>#{messages.loginTextfieldPassword}</p>
<h:inputSecret value="#{userServiceImpl.user.password}" />
<h:commandButton value="#{messages.loginButtonLogin}" action="#{userServiceImpl.authenticateUser}" />
</h:form>
The userServiceImpl class is:
#Named
#RequestScoped
public class UserServiceImpl implements UserService {
private UserSession userSession;
private User user;
#Inject
public UserServiceImpl(UserSession userSession) {
this.userSession = userSession;
}
#PostConstruct
public void prepareService() {
user = new User();
}
#Override
public View authenticateUser() {
userSession.setLoggedUser(user);
return View.MAIN;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
My goal is pretty simple: when the user hits the login button, I want to authenticate the user.
The problem is:
When the authenticate method is called, the User attributes are null. I debugged the application and the getUser method is called and the values are properly set, but at some point (which I did not find [yet]) before the authenticateUser is called the User attributes are set to null...
I'm aware that this is a pretty basic question... but are you able to point out where my mistake is?
Thanks.
Based on your previous question, you seem to have experimented with <managed-bean-scope> of none in faces-config.xml for some reason. The problem symptoms matches exactly when using #ManagedBean #NoneScoped. You seem to have configured this bean in faces-config.xml as well on a none scope which totally explains this problem. With the none scope, a brand new bean instance is been created everytime when #{userServiceImpl} is been evaluated in EL. Your form submit has thus effectively created 3 beans: one where the user name is set, another one where user password is set and another one where action is invoked.
You need to remove the managed bean configuration from faces-config.xml. You should not use it when you intend to use #Inject (or #ManagedBean). The faces-config.xml way of configuring beans is a leftover from old JSF 1.x ages when annotations weren't available. As of JSF 2.x it would only override any bean management annotations.
I'm trying to build a user profile page to show some details about my users.
The url of the page is something like profile.xhtml?username=randomString.
So, what I've to do is loading all the data of randomString's user.
It goes everything fine since it's the moment to show user's image.
I'm using PrimeFaces with graphicImage component, but the problem is that it causes a NEW request to get the image, so the request parameter is actually lost and the getAvatar() method receives a null parameter.
One solution may be making the bean SessionScoped, but it will take data from the first requested user and it will show them even if randomString will change, so I'm asking for help :
How can I show a dynamic image from database that depends on a request parameter?
Thanks :)
EDIT : New code following BalusC's reply
JSF Page :
<c:set value="#{request.getParameter('user')}" var="requestedUser"/>
<c:set value="#{(requestedUser==null) ? loginBean.utente : userDataBean.findUtente(request.getParameter('user'))}" var="utente"/>
<c:set value="#{utente.equals(loginBean.utente)}" var="isMyProfile"/>
<pou:graphicImage value="#{userDataBean.avatar}">
<f:param name="username" value="#{utente.username}"/>
</pou:graphicImage>
(I'm using this vars because I want the logged user's profile to be shown if page request il just profile.xhtml without parameters)
Managed Bean :
#ManagedBean
#ApplicationScoped
public class UserDataBean {
#EJB
private UserManagerLocal userManager;
/**
* Creates a new instance of UserDataBean
*/
public UserDataBean() {
}
public Utente findUtente(String username) {
return userManager.getUtente(username);
}
public StreamedContent getAvatar(){
String username = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("username");
System.out.println(username==null);
Utente u = findUtente(username);
return new DefaultStreamedContent(new ByteArrayInputStream(u.getFoto()));
}
}
What's wrong with it?
username is always null!
EDIT 2 : Added reply to BalusC
Yeah, because the getAvatar() method calls findUser() as I need to find the user's entity with the username passed as parameter (<f:param> won't allow me to pass an object!).
So findUser() throws an exception because I'm using entityManager.find() with a null primary key!
Btw, I'm absolutely sure that both #{utente} and #{utente.username} are not null because the panel that contains the image is rendered only if #{utente ne null} and username is its primary key!
So I can't really check the HTML output!
I'm afraid that #{utente} is lost when I call getAvatar() as getting an Image requires a new http request
Pass it as <f:param>. It will be added during render response.
<p:graphicImage value="#{images.image}">
<f:param name="id" value="#{someBean.imageId}" />
</p:graphicImage>
The #{images} helper bean can just look like this:
#ManagedBean
#ApplicationScoped
public class Images {
#EJB
private ImageService service;
public StreamedContent getImage() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getRenderResponse()) {
// So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
}
else {
// So, browser is requesting the image. Get ID value from actual request param.
String id = context.getExternalContext().getRequestParameterMap().get("id");
Image image = service.find(Long.valueOf(id));
return new DefaultStreamedContent(new ByteArrayInputStream(image.getBytes()));
}
}
}
As the above helper bean has no request based state, it can safely be application scoped.
I have a page which takes in request params for place, then generate information,
for example, http://example.com/xxx/weather.jsf?place=california.
The purpose of doing this is to let user bookmark the link.
In the weather.jsf, there are two outputtext and a commandlink:
Humidity : <ice:outputText value="#{weatherBean.humidity}"/>
Visibility : <ice:outputText value="#{weatherBean.visibility}"/>
<ice:commandLink id="likeButton"
value="Like"
actionListener="#{weatherBean.doLike}" />
In the managedBean:
#ManagedBean(name="weatherBean")
#RequestScoped
public class WeatherBean
{
String humidity;
String visibility;
int numLike;
#PostConstruct
public void init()
{
System.out.println("init called");
HttpServletRequest request= (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String place = request.getParameter("place");
setHumidity(WeatherDao.getHumidity(place));
setVisibility(WeatherDao.getVisibility(place));
setNumLike(GeneralDao.getNumLike());
}
public void doLike(ActionEvent event)
{
System.out.println("doLike called");
GeneralDao.addNumberLike();
}
}
Alright, the page generated perfectly.
However, when I click the doLike commandLink,
it always triggers the init method first, then call doLike method.
Since the request param is empty, all the other values reset.
Is there any way to prevent a refresh of the page or calling of init method?
I tried partialsubmit or immediate, but no luck.
Your bean is #RequestScoped, so after executing the JSF lifecycle, your bean instance is lost, until the next request comes in, at which point you get a new instance of your bean, and the PostContruct re-executes.
Try changing the scope of your bean to something longer lived, like #ViewScoped.
I have a problem I don't understand: Behind any View I have a controller ManagedBean that is RequestScoped and a data ManagedBean, that holds the data for the view and is SessionScoped.
So there are two views, which are login with loginData and loginController and overview with overviewData and overviewController.
The functionality should be like that:
The User logs into the application (loginController method)
If Authentication is successfull, there is a redirect to overview.xhtml (again in loginController method)
Then the overviewData gets its data by the overviewController, which retrieves them from business logic layer
The overview.xhtml shows the retireved data
So, the point is that I want to fill overviewData out of loginController, right after login! (???or if possible right befor overview view is constructed, if possible???).
I tried it with managedProperties, but the one I initiate in loginController is a different object than the managedProperty in overviewController, although they have the same name! How is that possible.
Oh boy, I doubt you guys understand what I mean, so I need to post some code:
LoginController.java
...
#ManagedBean
#RequestScoped
public class LoginController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
OverviewController overviewController;
public LoginController(){
overviewController = new OverviewController ();
}
String login() throws Exception {
UsernamePasswordToken token = new UsernamePasswordToken(loginData.getName(), loginData.getPw().trim());
try {
currentUser.login(token);
overviewController.fillProjects();
...
OverviewController.java
...
#ManagedBean
#RequestScoped
public class OverviewController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
public void fillProjects(){
if(overviewData == null){
overviewData = new OverviewData();
}
overviewData.setProjects(projectService.getProjects()); //retrieves data from business logic
}
...
OverviewData.java
...
#ManagedBean(name = "overviewData")
#SessionScoped
public class OverviewData {
private List<ProjectDTO> projects; //that's what the view needs to display the overview
public void setProjects(List<ProjectDTO> projects) {
this.projects = projects;
}
...
I hope that helps to show my problem, if you don't understand it, pls ask in a comment..
Would be nice if you can help me :-)
Cheers...
You're creating beans yourself using new instead of letting JSF do the job.
overviewController = new OverviewController ();
and
overviewData = new OverviewData();
This is wrong. JSF won't utilize any beans which you've created yourself this way. Remove those lines and add another #ManagedProperty on overviewController inside LoginController (and make the property private).
#ManagedProperty(value="#{overviewController}")
private OverviewController overviewController;
JSF will create the bean itself and set it as managed property directly after parent bean's construction. You just have to access it the usual Java way (without the need for nullchecks).