How to globally inform sessionscoped beans to reload their data? - jsf

I have a session scoped bean which stores the selected employee.
An always visible input offers the ability to change the selected employee at any time.
Because there are a plenty of adjustable settings (and views) a couple of session scoped beans hold their #PostConstruct loaded data inside member variables, and I wouldn't like to reload this data from storage any time the view is opened (#ViewScoped).
I don't have an idea how to inform all active beans about the new selected employee.
Any hint is welcome ... ;)
I use Tomcat 8, Primefaces 5, JSF 2.2.

Since you don't want to reload this data every time the page is loaded, you could at least search the selected employee and if it's the same, do the update. If not, you don't need to do a thing.

Please notice: this solution is a workaround for a poor design!
The design insufficiency is to cache the data inside a session scoped backing bean instead of using 2nd level JPA cache in backend (see comment BalusC on question).
The workaround:
my session scoped SessionManager keeps the selectedPerson. On changing this selection a timestamp is updated:
#Named
#SessionScoped
public class SessionManager implements Serializable {
private Date lastUpdateSelectedPerson; // +getter
public void setSelectedPerson(Person selectedPerson) {
this.selectedPerson = selectedPerson;
lastUpdateSelectedPerson = new Date();
}
}
Hence other backing beans may compare this timestamp to a local update timestamp:
#Named
#SessionScoped
public class MyData implements Serializable {
#Inject SessionManager sessionManager;
private Date lastUpdateData;
private void updateData() {
// update data
lastUpdateData = sessionManager.getLastUpdateSelectedPerson();
}
public List<Data> getData() {
if (sessionManager.getLastUpdateSelectedPerson().compareTo(lastUpdateData) > 0)
updateData;
return data;
}
}

Related

Safest way to access a session scope bean in another bean

I am new to jsf and using JSF 2.0 to keep user information in a session scoped bean. I need to access this information across other beans for grunt work. Presently, this is how i am doing:-
private UserBean myuser1 = (UserBean)FacesUtils.getManagedBean("UserBean");
and then access properties as
if (myuser1.getUserType == 1) ...
this works but some time throws Argument Error: parameter key is null exception. I have been using following method too:-
private UserBean myuser2 = new UserBean();
if (myuser2.getUserType == 1) ...
In second method, my understanding is that if UserBean is already created in session, it would be retried. There are lots of question about 'how to access one bean in another' so i am confused. Please tell me one clean method which should always work and not throw null pointer exception abruptly.
The simplest way I know of is using #ManagedProperty, I don't know what you mean by safest though.
Let's say this is your sessionScoped bean :
#ManagedBean
#SessionScopped
public class UserBean {
//bean attributes and methods
}
Then you can access it in any other bean (provided it has the same or a narrower scope) as an attribute like this :
#ManagedBean
#ViewScoped //in this cas you can use SessionScoped, FlowScoped, or RequestScoped too
public class AnotherBean {
#ManagedProperty("#{userBean}")
UserBean userB;
//rest of the bean
//be sure to add getters and setters for the injected bean
}
For more details check this
Hope this helps.
Actually,
parameter key is null exception: it's either you didn't initialize the object witch can be solver with either adding
object = new Object(); // in the constructor of the class.
The second problem may be that the object is " DETACHED " you need to call the object using the method merge (with the entity manager).
A detached object is a known value but the JPA system doesn't know if it is the latest version from the DB or even sometimes the id value is not set for some reason (Not managed with jpa in other words it can be your case).
If em is your entity manager and you have the following function:
public Object latestVersion(Object o){ em.merge; }
In your Bean with:
#EJB
Service service;
if you do em.latestVersion(o); the problem of detached object is solved.
And for the real answer:
To access a object from another view you can simply do the following.
#ManagedBean
#SessionScoped
..... Bean1 {
public static Object o;
.....
}
#ManagedBean
..... Bean 2 {
private Object b=Bean1.o;
.....
}
Good luck
The standard practice of setting dependency of a scoped bean in another scoped bean is to use #Inject annotation like
#Inject UserBean userBean; in the bean you want use the UserBean object.
Your UserBean should be a stateful one.
#Stateful
#LocalBean
public class UserBean
{
private String name;
public String getName() { return name; }
public void setName( String name_ ) { name = name_; }
}
And just inject it into a stateless bean to modify its state:
#Stateless
#LocalBean
public class MyStatelessBean
{
#EJB
private UserBean userBean;
public String getUserName() { userBean.getName(); };
public void setUserName( String name_ ) { userBean.setName( name_); }
}
Or you can access it from (not wider scoped) managed beans as well in the same way:
#ManagedBean
#Dependent
public class MyJSFManagedBean
{
#EJB
private UserBean userBean;
}
You wrote in your comment you does not use EJBs at all. The picture modify like this:
The UserBean should be a SessionScoped CDI bean
#Named
#SessionScoped
pubilc class UserBean
{}
The othe CDI bean should be in a nearer scope:
#Named
#Request // or #ViewScoped or #Dependent
public class OwnerBean
{
#Inject
UserBean userBean;
}
The container automatically takes care to create the beans in the right scope and insert them into the owers (any kind of container managed objects : servlets, filters, action listeners, JSF/CDI beans). You need to insert a wider scoped resource into a thinner scoped one.

How to pass bean property from one view to another view

I'm using JSF 2.1 and Primefaces:
I have a view scoped managed bean with a managed property and a method that set something on other view scoped managed bean and forward to other page referencing that managed bean:
#ManagedBean
#ViewScoped
public class HelloMB {
#ManagedProperty("otherMB")
private OtherMB other;
public String changeOtherMB() {
otherMB.setAnyObject(new Object());
return "otherPage.xhtml";
}
}
#ManagedBean
#ViewScoped
public class OtherMB {
private Object o;
public void setAnyObject(Object o) {
this.o = o;
}
}
So, when otherPage is rendered o is null.
You have idea how could I solve this? How can I retain an Object in a #ViewScoped managed bean and keep it live on other page without using #SessionScoped?
The view scope is destroyed and recreated once you navigate to a different JSF view. You know, the view scope lives as long as you're interacting with the same JSF view. In this particular case you effectively end up with two instances of the #{otherMB} managed bean during one request. One instance which is used by the source view and another instance which is used by the destination view.
As the second view is created within the very same request, you could just pass it as a request attribute.
#ManagedBean
#ViewScoped
public class HelloMB implements Serializable {
public String changeOtherMB() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getRequestMap().put("anyObject", anyObject);
return "otherPage.xhtml";
}
}
#ManagedBean
#ViewScoped
public class OtherMB {
private Object anyObject;
#PostConstruct
public void init() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
this.anyObject = ec.getRequestMap().get("anyObject");
}
}
I however wonder if you're aware about the importance of idempotent vs non-idempotent requests. Perhaps you actually need a "plain vanilla" link without the need to invoke a view scoped bean action method. See the last "See also" link below for an extensive example on that.
See also:
How to choose the right bean scope?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Creating master-detail pages for entities, how to link them and which bean scope to choose

Correct Usage of Stateful Session Bean in EJB

I am trying to construct a test website that display various information about a student with JSF 2.0, EJB 3.1 and JPA 2.0.
After a student login, the student can browse different pages for displaying different kind of information, which is what a usual registration management system does. Say displaying a timetable according to enrollment information in one page, and display the assignments in another page. Information that will be displayed include attributes of the student, mapped entities like registered course, assignment submitted etc.
At first, I tried to create a stateless bean and get the information required for each page, and change some attributes of student
#Entity
public class Student{
#Id
private String sid;
private String address;
#ManyToMany
private List<Assignment> submittedAssignments;
#ManyToMany
private List<Course> courses;
}
#Stateless
#LocalBean
public class studentDao {
#PersistenceContext(unitName="PU")
private EntityManager em;
public Student getStudent(String sid){
return em.find(Student.class, sid);
}
public List<Course> getCoursesByStudent(String sid){
return em.find(Student.class, sid).getCourses();
}
public List<Assignment> getAssignmentsByStudent(String sid){
return em.find(Student.class, sid).getSubmittedAssignments();
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void ChangeAddress(String sid, String newAddress){
em.find(Student.class, sid).setAddress(newAddress);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void SubmitAssignment(String sid, Assignment submittedAssignment){
}
//Some more methods, just list a few for illustration.
}
#ManagedBean
#SessionScoped
public class LoginSession{
private String sid;
//getter
public login(String sid){
//probably will need password validation later, so I have to connect to db and check the password.
this.sid=sid;
}
}
#ManagedBean
#RequestScoped
public class AssignmentBean{
#EJB
private StudentDao studentDao;
#ManagedProperty(value="#{loginSession}")
private LoginSession session;
public List<Assignment> getAssignments(){
return studentDao.getAssignmentsByStudent(session.sid);
}
}
assignment.xhtml:
<h:datatable value="#{assignmentBean.assignments}"></h:datatable>
And then, I find it quite tedious to pass student from managed bean to stateless ejb, retrieve student, again and again, then return only a part of information to ManagedBean for display, therefore I think I can do it like this.
#Stateful
#LocalBean
public class studentDao {
#PersistenceContext(unitName="PU", type=PersistenceContextType.EXTENDED)
private EntityManager em;
private Student currentStudent;
public void login(String sid){
//didn't make a password, just type sid and done.
currentStudent = em.find(Student.class, sid);
}
public Boolean getLoggedIn(){
return currentStudent != null;
}
public Student getCurrentStudent(){
return currentStudent;
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void ChangeAddress(String newAddress){
currentStudent.setAddress(newAddress);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void SubmitAssignment(Assignment submittedAssignment){
}
}
#ManagedBean
#SessionScoped
public class LoginSession{
#EJB
private StudentDao studentDao;
public login(String sid){
studentDao.login(sid);
}
}
assignment.xhtml:
<h:datatable value="#{loginSession.studentDao.currentStudent.submittedAssignments}"></h:datatable>
As the currentStudent lives with the extended persistence context, I can retrieve the information directly. And I do not have to pass SID to EJB again and again for each operation, also applies for finding that student again and again as it is already there managed.
I am not sure if it is an abuse of #Stateful bean as the pages had been a large group of conversations, unlike the common "shopping cart" example where user can choose a lot of orders, make payment, then commit all database changes after validation and user confirm.
Please comment on the above SFSB usage whether it is an abuse of SFSB and why if it is an abuse, or which one of the above two designs is better (or suggest another if both were bad as I am the first time writing a web application starting from scrap but a few books from Apress, those started with Pro whatever, EJB3, JPA, JSF, Java EE 6 with glassfish etc).
Thank you.
If you use stateful session beans, the EJB container manages state on behalf of your client, so there is no need to pass around an ID: It's more comfortable.
In your case it's OK.
Generally speaking, consider these items:
Stateless services are sometimes a little bit easier to test: If you write a test case, you do not have to consider the side-effects on the state.
If your stateful session bean holds cached data, you have to handle cache invalidation.
If your session bean is #Remote: If client and server are not on the same JVM, each method call requires marshalling, and goes over the network.
If you have lots of stateful sessions which consume resources on the server side: You already have two Lists there, and their number and their content will most likely grow over the time. Using a stateless setup, you can design an application where the state is only managed in the browser, for example.
If someone else is using your comfortable stateful API, and you must change it later, you will have endless discussions.
For these reasons I always prefer the stateless setup using data transfer objects. But again, in your case it's OK.

How do I force a managed bean to initialize first?

I have a navigation managed bean for each user.
and I need it to initialize first before any other bean because a value is required from the bean.
May I know how do I perform that?
I have tried eager="true" but it doesn't work.
any quick and easy solution via faceconfig would be greatly appreciated.
Just perform the desired initialization job in bean's #PostConstruct.
#PostConstruct
public void init() {
// Here.
}
It'll be invoked when the bean is injected/referenced from another bean for the first time.
The eager=true works only on application scoped beans.
From what I see you should reference the other bean. Let's assume a have a utility class that can pull a bean from the context.
Basically ->
//Bean A
public void doSomething()
{
String required = Utility.getBeanB().getRequiredValue();
use(required);
}
...
//Bean B
public String getRequiredValue()
{
return "Hi, I'm a required value";
}
I have several large web apps that have a "Session Bean" that stores stuff like user preferences, shared objects etc... and this method works perfectly. By using a reference to the bean you eliminate the need to chain the initialization. That method will always DEPEND on the method in the other bean, thus guaranteeing the order of initialization.
There's a variety of ways to access the bean but I usually go through the EL route ->
Get JSF managed bean by name in any Servlet related class
Best of luck, I try to stay "functionally pure" when I can--and I hope that get's a laugh considering the language!
Here's some cool hax for ya, in case other solutions aren't working for you due to various circumstances...
Let's say I have a class Alpha that I want initialized first:
public class Alpha {
#PostConstruct
public void init() {
}
}
I can put the following method in Alpha:
#ManagedBean(name = "Alpha", eager = true)
public class Alpha {
public static void requireAlpha() {
FacesContext context = FacesContext.getCurrentInstance();
Object alpha = context.getApplication().evaluateExpressionGet(context, "#{Alpha}", Object.class);
System.out.println("Alpha required: " + alpha.toString());
}
#PostConstruct
public void init() {
}
}
Then, in any classes that are initializing too early, simply call:
Alpha.requireAlpha();
// ...
// (Code that requires Alpha to be initialized first.)
And if there's a ChildAlpha class that extends Alpha that you really want to be initialized (rather than the parent), make sure to use "ChildAlpha" instead, in both the name="" and the EL Expression ("#{}").
See here for more infos: Get JSF managed bean by name in any Servlet related class

JSF2.0: ManagedProperty Lifecycle?

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).

Resources