JPA EclipseLink entities not refreshing - jsf

I have a problem with entities not being refreshed when values in the database are changed from outside the JPA session. For instance, I have a user entity:
#Entity
#Cacheable(false)
public class UserBean implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToMany(mappedBy = "receiver")
#JoinTable(name = "NOTIFICATIONS_RECEIVED")
private List<NotificationBean> notificationsReceived;
...
}
And notifications entity:
#Entity
#Cacheable(false)
public class NotificationBean implements Serializable{
#Id
#GeneratedValue
private Long id;
#ManyToOne
private UserBean receiver;
...
}
I use this inside a JSF application and have a SessionScoped bean, which loads the user after login and stores it:
#Named("sessionManager")
#SessionScoped
public class SessionManagerBean implements Serializable {
#PersistenceUnit(unitName = "PU")
private EntityManagerFactory emf;
private UserBean user;
public UserBean getUser() throws Exception {
if (user == null) {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
String username = request.getRemoteUser();
if (username != null) {
EntityManager em = null;
try {
utx.begin();
em = emf.createEntityManager();
Query query = em.createQuery("SELECT u from UserBean u WHERE u.username = ?1");
query.setParameter(1, username);
user = (UserBean) query.getSingleResult();
}
catch (Exception e) {
try {
utx.rollback();
} catch (Exception e) {
} finally {
utx.commit();
em.close();
}
}
return user;
}
}
}
public void refreshUser() {
EnitytManager em = emf.createEntityManager();
// similar code as above to retrieve the user from the database
em.refresh(user);
}
}
The page which displays the notifications calls refreshUser() when it loads:
<f:metadata>
<f:event type="preRenderView" listener="#{sessionManager.refreshUser()}" />
</f:metadata>
The user data is not refreshed though and notifications which are displayed on the page are not updated when I refresh the page.
However, if I change refreshUser() to:
public void refreshUser() {
EntityManager em = emf.createEntityManager();
List<NotificationBean> notifications = em.createNativeQuery("SELECT * FROM NOTIFICATIONBEAN WHERE RECEIVER_ID = " +
user.getId() + ";").getResultList();
user.setMatchChallengesReceived(notifications);
}
the notifications are updated.
I have more variable than notifications that I need to refresh from the database and it would be a lot of code to do the same for each one. I thought em.refresh(user) should reload all variables that have changed from the database for me. I thought it is a caching issue, so I added #Cacheable(false) to UserBean and NotificationBean, but it has no effect.
What am I doing wrong?

If the problem is with notifications, then itis because refreshing user is not set to cascade the refresh. Set the CascadeType.REFRESH on the notificationsReceived mapping.

Related

Invalidating old httpsession drops current httpsession's context

My app handles logins with a #ViewScoped LoginBean, which is injected with a #SessionScoped SessionBean that stores user information and the current HttpSession. This app allows a user N separate sessions. After reaching that limit the user can only create another by killing off the oldest. This is done in the same LoginBean by asking the unmanaged UserSessionManager for the oldest SessionBean, and then invalidating its HttpSession.
Thus, logging in with session "A", we invalidate session "B". This all goes according to plan. But then, sometime during the remaining JSF phases, we also lose the SessionBean for session "A". Tracing down into the CDI code it appears that the session context for session "A" is being destroyed so when the redisplay finishes we have all new session beans.
We are using MyFaces 2.3.6, OpenWebBeans 2.0.16, OpenJDK 11
Is this a bug in OWB, or expected bahavior?
I'm also wondering if I have a fundamental misconception. If I save a SessionBean in my UserSessionManager and the retrieve it during a different session, should it retain its original state or does it get re-evaluated in the new SessionScoped context? I've been finding debugging difficult because my objects seem to actually be proxies, and the UI and debugger show different values at times.
Update 4/27/20:
The #SessionScoped SessionBean is being destroyed by org.apache.webbeans.web.context.WebContextsService#destroyRequestContext() where it destroys the "PropagatedSessionContext". This PropagatedSessionContext is being set by WebContextsService#destroySessionContext(), which is designating the local session to be destroyed despite being given a different specific session. This is where I'm wondering if it's a bug in OWB.
Here's a simplified example of the code:
(In this test code I've made the SessionManager an #ApplicationScoped bean. In the original code it isn't, but the behavior is the same.)
#Named("loginbean")
#ViewScoped
public class LoginBean implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
#Inject private ExternalContext externalContext;
#Inject private SessionBean session;
#Inject private SessionManager sessionMgr;
public String killOldestDoLogin() {
List<SessionInfo> sessions = sessionMgr.getSessions();
SessionInfo oldest = sessions.get(0);
sessionMgr.killSession(oldest.getSessionId());
return doLogin();
}
public String doLogin() {
username = username.trim();
if (username != null && username.length() > 0) {
// After a successful login, avoid session fixation attacks by
// rotating the session ID. This isn't strictly necessary as Faces has
// its own session ID that a third party wouldn't have access to
if (externalContext != null) {
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
if (request != null && request.isRequestedSessionIdValid()) {
newSessionId = request.changeSessionId();
}
}
HttpSession http = (HttpSession)externalContext.getSession(false);
session.setUsername(username);
session.setHttpSession(http);
sessionMgr.addSession(http, session);
}
return "startPage.jsf");
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
.
#Named("sessionbean")
#SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private HttpSession httpSession;
public void reset() {
username = null;
httpSession = null;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public HttpSession getHttpSession() {
return httpSession;
}
public void setHttpSession(HttpSession session) {
this.httpSession = session;
}
public String getSessionId() {
return httpSession == null ? "null" : this.httpSession.getId();
}
}
.
#Named("sessionmanager")
#ApplicationScoped
public class SessionManager {
private HashMap<String,HttpSession> sessionMap = new HashMap<>();
private HashMap<String,SessionBean> beanMap = new HashMap<>();
public void addSession(HttpSession http, SessionBean bean) {
beanMap.put(http.getId(), bean);
sessionMap.put(http.getId(), http);
}
public boolean killSession(String sessionId) {
HttpSession session = sessionMap.get(sessionId);
sessionMap.remove(sessionId);
beanMap.remove(sessionId);
if (session != null) {
session.invalidate();
}
return session != null;
}
public List<SessionInfo> getSessions() {
List<SessionInfo> result = new ArrayList<>();
for (String sessionId : sessionMap.keySet()) {
SessionBean bean = beanMap.get(sessionId);
HttpSession http = sessionMap.get(sessionId);
SessionInfo info = new SessionInfo();
info.setUsername(bean.getUsername());
info.setSessionId(sessionId);
info.setHttpSession(http));
result.add(info);
}
return result;
}
}
.
public class SessionInfo {
private String username;
private String sessionId;
private HttpSession httpSession;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public HttpSession getHttpSession() {
return httpSession;
}
public void setHttpSession(HttpSession httpSession) {
this.httpSession = httpSession;
}
}

Problems migrate from Seam3 to DeltaSpike

I'm just testing to migrate from Seam3 to DeltaSpike, everything is ok if there is only one EntityManager in a bean, but there will be a error if add other EntityManager(other datasource):
JBAS010152: APPLICATION ERROR: transaction still active in request with status 0
the error project:
https://github.com/yuanqixun/hellodeltaspike
run this project environment:
wildfly 8.2.0.Final
H2 datasource
MySql datasource
The EntityManagerProducer code:
#ApplicationScoped
public class EntityManagerProducer {
#PersistenceUnit(unitName = "hellodeltaspike")
EntityManagerFactory emf;
#PersistenceUnit(unitName = "hellodeltaspike2")
EntityManagerFactory mysqlemf;
#Produces
#ConversationScoped
EntityManager createEntityManager(){
return this.emf.createEntityManager();
}
#Produces
#MySqlEm
#ConversationScoped
EntityManager createMysqlEntityManager(){
return this.mysqlemf.createEntityManager();
}
}
The Action code:
#ConversationScoped
#Named
public class PersonAction implements Serializable{
#Inject
EntityManager em;
#Inject
#MySqlEm
EntityManager mysqlEm;
Person person;
List<Person> personList;
#PostConstruct
void afterCreate(){
person = new Person();
personList = queryPersonList();
}
private List<Person> queryPersonList() {
String jql = "select o from Person o ";
List<Person> result = em.createQuery(jql,Person.class).getResultList();
if(result == null)
return new ArrayList<Person>();
return result;
}
#Transactional
public void btnDoSave(ActionEvent event){
try {
if(StringUtils.isEmpty(person.getUuid())){
em.persist(person);
}else{
em.merge(person);
}
em.flush();
String msg = "Saved:"+person.getName();
FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(FacesMessage.SEVERITY_INFO,msg,null));
person = new Person();
personList = queryPersonList();
} catch (Exception e) {
e.printStackTrace();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), null));
}
}
...getter and setter
}
and there will be error:
ERROR [org.jboss.as.txn] (default task-6) JBAS010152: APPLICATION ERROR: transaction still active in request with status 0
Modify the method's annotation,add the special qualifier of the right EntityManager, so the problem will be solved. But also has another problem, how to support multiple entityManager's transaction in one method?
#Transactional(qualifier = {H2Em.class})

JSF display username when the user login

How can I display the username from the userindex page once the user successfully login. Should I be pass it to the constructor and use it? or is there any better solution for this?
Create a session-scoped bean that stores either the user's ID (so you can lookup the user per request) or the actual user object itself.
#Named // or #ManagedBean
#SessionScoped
public class SessionGlobals {
private Integer userId;
public boolean isLoggedIn() {
return userId != null;
}
public Integer getUserId() {
return userId;
}
public void login(int userId) {
this.userId = userId;
}
public void logout() {
this.userId = null;
}
Inject this bean wherever it is required. When you login and logout, call the appropriate methods above.
For example:
#Named // or #ManagedBean
#RequestScoped
public class RequestGlobals {
public User getUser() {
return sessionGlobals.isLoggedIn()
? userDao.findById(sessionGlobals.getUserId())
: null;
}
#Inject
private UserDao userDao;
#Inject
private SessionGlobals sessionGlobals;
}
and in your page or template:
<h:outputText value="Welcome, #{requestGlobals.user.firstName}"
rendered="#{sessionGlobals.loggedIn}"/>

How to get number of connected users and their role using j_security_check?

I get the username of the connected user (using j_security_check) this way, through a managed bean:
......
username = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal().getName();
And then display it in a jsf page this way : #{userBean.username}
But I figured no way to get the number of connected users and get their role.
In other words, I want to display besides the username, the user role and the number of connected users.
How can I achieve this!?
Thanks in advance for your help!
EDIT:
I can now get the Role of the connected user, using a namedquery in a managed bean :
public Users getUserRole(){
try {
Users auser = (Users)
em.createNamedQuery("Users.findByUsername").
setParameter("username", getRemoteUser()).getSingleResult();
return auser;
} catch (NoResultException nre) {
JsfUtil.addErrorMessage(nre, "getUserRole Error");
return null;
}
}
and in the xhtml page:
<h:outputLabel for="rolefacet" value="Role: "/>
<h:outputFormat id="rolefacet" value="#{UserBean.userRole.ugroup}" />
while ugroup is the role name in the Users entity class.
EDIT: One solution that still does not work for me is to add a HttpSessionListener to my web.xml:
package beans;
/**
*
* #author med81
*/
import java.io.Serializable;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.ArrayList;
import javax.faces.context.FacesContext;
public class SessionCounter implements Serializable, HttpSessionListener {
private List sessions = new ArrayList();
Object s = FacesContext.getCurrentInstance().getExternalContext().getSession(false);
public Object getS() {
return s;
}
public void setS(Object s) {
this.s = s;
}
public SessionCounter() {
}
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.getSession();
sessions.add(session.getId());
session.setAttribute("counter", this);
}
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
sessions.remove(session.getId());
session.setAttribute("counter", this);
}
/**
*
* #return size of the session list
*/
public int getActiveSessionNumber() {
return sessions.size();
}
}
Here's a basic kickoff example how you could do it when you're on Servlet 3.0 and thus are able to utilize programmatic login by the new HttpServletRequest#login() API.
The login form: login.xhtml
<h:form>
<h:inputText value="#{user.username}" />
<h:inputSecret value="#{user.password}" />
<h:commandButton value="Login" action="#{user.login}" />
<h:messages />
</h:form>
The user manager bean: com.example.UserManager
#ManagedBean(name="user")
#SessionScoped
public class UserManager implements Serializable {
private String username;
private String password;
private User current;
#EJB
private UserService userService;
#ManagedProperty("#{loginManager.logins}")
private Set<User> logins;
public String login() {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
try {
request.login(username, password);
current = userService.find(username, password);
} catch (ServletException e) {
// Unknown login. Will be handled later in current==null check.
}
if (current == null) {
context.addMessage(null, new FacesMessage("Unknown login"));
return null;
} else {
logins.add(current)
return "home?faces-redirect=true";
}
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "login?faces-redirect=true";
}
// ...
}
The logout (and session invalidate) listener: com.example.LogoutListener
#WebListener
public class LogoutListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent event) {
// NOOP.
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
UserManager userManager = (UserManager) event.getSession().getAttribute("user");
if (userManager != null && userManager.getCurrent() != null) {
userManager.getLogins().remove(userManager.getCurrent());
}
}
}
(Do not do this in logout() method! It's the session invalidation which triggers this, the session invalidation will take place when logout() is called OR when session has expired)
In any logged-in view you can obtain the current user and the login count as follows:
<p>Welcome, #{user.current.name}!</p>
<p>Total logged in users: #{user.logins.size()}</p>
get the number of connected users
I'll assume that you mean to get the number of logged-in users.
Basically, you need to have an applicationwide Set<User> with all logged-in users and add the User to it when it logs in and remove the User when it logs out or when its session is destroyed. Here's an example which uses an application scoped managed bean
#ManagedBean(eager=true)
#ApplicationScoped
public class LoginManager implements Serializable {
private Set<User> users = new HashSet<User>();
public Set<User> getUsers() {
return users;
}
}
If you were using Java EE 6 it would have been easy to replace j_security_check by a managed bean method which utilizes the new Servlet 3.0 HttpServletRequest#login() and simultaneously adds the User to the Set<User> of the injected LoginManager bean.
But on Java EE 5 there is no trivial way to hook on it. You would need to check every request for the logged-in user. Best to achieve this is to put the User object in the session whenever there's an UserPrincipal. You can do this using a filter which does roughly the following job in doFilter() method.
UserPrincipal principal = request.getUserPrincipal();
User user = (User) session.getAttribute("user");
if (principal != null && user == null) {
user = userService.findByName(principal.getName());
session.setAttribute("user", user);
LoginManager loginManager = (LoginManager) servletContext.getAttribute("loginManager");
loginManager.getUsers().add(user);
}
Finally, to remove the user from the logins, best is to hook on HttpSessionListener#sessionDestroyed(), assuming that you're invalidating the session on logout. This will also be called when the session expires.
public void sessionDestroyed(HttpSessionEvent event) {
User user = (User) event.getSession().getAttribute("user");
if (user != null) {
LoginManager loginManager = (LoginManager) event.getSession().getServletContext().getAttribute("loginManager");
loginManager.getUsers().remove(user);
}
}

JSF - Get the SessionScoped Bean instance

I have this configuration on my web application. 2 beans :
1° Bean - It checks the login;
#ManagedBean(name="login")
#SessionScoped
public class Login {
private String nickname;
private String password;
private boolean isLogged;
public String getNickname() { return nickname; }
public void setNickname(String newValue) { nickname=newValue; }
public String getPassword() { return password; }
public void setPassword(String newValue) { password=newValue; }
public void checkLogin() {
... i check on db the nickname and the password ...
if(USER EXIST) {
isLogged=true;
} else {
isLogged=false;
}
return true;
}
}
2° Bean - Manage User parameter :
#ManagedBean(name="user")
#SessionScoped
public class User {
private String name;
private String surname;
private String mail;
public User() {
String[] record=null;
Database mydb=Configuration.getDatabase();
mydb.connetti();
ArrayList<String[]> db_result=null;
db_result=mydb.selectQuery("SELECT name, surname, mail, domicilio FROM users WHERE nickname='???????'");
int i = 0;
while (i<db_result.size() ) {
record=(String[]) db_result.get(i);
i++;
}
}
... getter and setter methods...
}
As you can see, I would like to know how get the nickname setted previously on my login bean, so i can do the query on my DB.
In fact i need to get the instance of the current-session bean login : how can I get it? I should use somethings like session.getBean("login") :)
Hope this question is clear :)
Use #ManagedProperty to inject it and use #PostConstruct to access it after bean's construction (because in a normal constructor it would be still null).
#ManagedBean
#SessionScoped
public class User {
#ManagedProperty(value="#{login}")
private Login login;
#PostConstruct
public void init() {
// Put original constructor code here.
}
// Add/generate getters/setters and other boilerplate.
}
That said, this is not the correct approach. You'd like to do it the other way round. Inject User in Login by #ManagedProperty(value="#{user}") and do the job during submit action method.
You'd also like to put the password in WHERE clause as well. There's absolutely no need to haul the entire users table into Java's memory and determine it one by one. Just let the DB do the job and check if it returns zero or one row.
Also try using the following code:
ExternalContext tmpEC;
Map sMap;
tmpEC = FacesContext.getCurrentInstance().getExternalContext();
sMap = tmpEC.getSessionMap();
login loginBean = (login) sMap.get("login");

Resources