How to design communication from a backend server to the frontend using JavaEE (EJB, JSF, PrimeFaces) - jsf

Normally, a frontend such as a web application (e.g. JSF) is used to retrieve data from a system and display it. User interaction is required: a user pulls up a website and e.g. clicks a button which in turn triggers a whole round trip process (view calls controller, controller informs model, model updates and notifies view via controller again).
In my setup I don't have any user interaction because my webapp is a view only. No user can do any CRUD actions (except Read). The "user interaction" comes so to speak from my backend: This system runs the entirety of business logic and receives user interactions through other channels than the webapp.
However, all changes within the backend need to be somehow pushed into the webapp frontend.
I am using JavaEE, more precisely EJB 3.2, JSF 2.2 and PrimeFaces 5.1 (as well as JMS 2.x, WildFly 8.2, OmniFaces 2.0)
My backend communicates changes via JMS. I defined a MessageListener within my webapp that listens to all incoming changes. These changes are pushed into the model (EJBs).
As far as my knowledge goes should a model never know anything about it's view: the model is completely agnostic to the view. In JavaEE terms this means that my EJBs (model) should not inject any ManagedBeans (controller).
Now here is my question: How should the model inform the controller/view about changes, so that they get reflected within the view?
Am I forced to use something like CDI Events?
Or in case of PrimeFaces, should I use PrimeFaces Push (WebSocket implementation using Atmosphere) that kinda simulates a user interaction? In other words, should my JMS MessageListener directly push the changes to all clients (browsers) via WebSocket and then use AJAX to simulate an user interaction (which in turn does nothing else than what a "normal" user would trigger in the webapp)?
Or how about the old fashioned way according to Observer Pattern: Every managed bean that needs to be notified I add as listener to the EJB with help of #PostConstruct and also make sure that the listener gets removed with #PreDestroy?

We found a solution doing it in a "hybrid" mode. Here is what we came up with:
Observer Pattern
We used the classic Observer Pattern: A managed bean adds itself as listener to an EJB, which in turn is updated by the JMS message listener. Therefore, we made use of the #PostConstruct to add a listener as well as #PreDestroy to remove listener respectively whenever the managed bean gets created/destroyed.
In other words, whenever the model gets updated, it informs its listeners, which are the managed beans (controller). But this doesn't yet update the view. Here comes the hybrid mode into play:
PrimeFaces Push
Once the model (EJB) is updated it has to push a notification to it's web clients via PrimeFaces Push. This way all clients know they have to go grab the changes:
#ManagedBean(name = "notifier")
#ApplicationScoped
public class MarketDataPushNotifier implements Serializable {
private final Logger log = LoggerFactory.getLogger(MarketDataPushNotifier.class);
public final static String CHANNEL = "/notify";
private final EventBus eventBus = EventBusFactory.getDefault().eventBus();
public MarketDataPushNotifier() {
// bean instantiation only
}
public void notifyListeners() {
log.info("Sending UI update notification...");
eventBus.publish(CHANNEL, true);
}
}
Of course one can design the notify channel more fine grained. In the view (facelet) do:
<p:remoteCommand name="updateTable" actionListener="#{bean.onUpdate}" update="table" process="#this" />
<!--table snippet ommitted-->
<p:socket onMessage="handleMessage" channel="/notify" autoConnect="true"/>
<script type="text/javascript">
function handleMessage(update) {
if (update) {
updateTable();
}
}
</script>
Hope that helps...

Related

Does load balancing in the server side of a web app generate multiple instances of a singleton AppScoped bean of JSF?

Suppose I deploy my web app on AWS or GAE and my app in JSF has a Singleton ApplicationScoped Bean with methods "void setList( List)" "List getList()".
I call this methods from a SessionScoped bean when a user makes modifications to the list.
I want to ensure that all users get the changes in their own session by pushing a notification message to them so that they can get the list again.
If the load balancer of AWS or GAE splits the app in several instances, how do they manage this singleton ApplicationScoped bean? Are there many instances of the singleton? how are they synchronized? is there any risk that one instance has different information?
I suppose all instances of the app in each server participating in the load balancing needs to be updated somehow but this would kill the purpose of load balancing since the work would be replicated everywhere. It might be possible that the singleton work is not load balanced but I don't know. The documentation is very extense and hard to get familiar with.
#ManagedBean( name = "theModelBean",
eager = true )
#Singleton
#ApplicationScoped
public class ModelBean {
ArrayList<Data> theList;
public List<Data> getList(){
return theList;
}
public void setList( List<Data> aList ) {
this.theList = aList;
}
}
#ManagedBean( name = "theController" )
#SessionScoped
public class Controller {
#ManagedProperty(value = "#{theModelBean}")
private ModelBean theModelBean;
public foo(){
ArrayList<Data> list = new ArrayList<>;
list.add( new Data() );
theModelBean.setList( list );
}
}
I wish load balancing does not interfere with my logic and that it handles everything in a transparent way for me. Otherwise I might have to make theModelBean write the list to the database everytime it changes and get it from there everytime it is requested from a session.
I'll ignore the "load balancing" / "load balanced" terms in your question and assume that you actually meant "clustering" / "clustered". As in: the same WAR file is being deployed to multiple servers which are all behind a single proxy (who does the actual load balancing, but load balancing itself is not the cause of the observed problem).
Yes, each server of the cluster will get its own instance of any "application scoped" bean. This not only includes the JSF #javax.faces.bean.ApplicationScoped, but also the CDI #javax.enterprice.context.ApplicationScoped and #javax.inject.Singleton and the EJB #javax.ejb.Singleton.
The normal approach is indeed to keep track of shared data in a single common data source which is used by all servers of the cluster. Usually a SQL-based RDBMS is being used for that. Usually you fire a SQL query to obtain the most recent data from the DB on every request/view. If you're using JPA for that, you usually use 2nd level cache to cache the data so that the amount of DB hits will be reduced. This can be configured cluster-wide.
If the data is immutable (i.e. read-only after creation), then the alternative approach to saving in a DB is to rely on session persistence. Have a #SessionScoped bean which reads from the #ApplicationScoped one during writeObject() and writes-if-absent to the #ApplicationScoped during readObject(). One real world example is implemented in the code behind the JSF <f:websocket> and OmniFaces <o:socket>.

How to run a long running process in JSF without facing "Connection Failed" error in browser [duplicate]

I want to execute a stored proc by clicking on a button developed in JSF and Java. The proc takes roughly around 30 minutes in execution.
When the user clicks on this button, s/he should get a default message like -
Data is being processed. Kindly login after 60 minutes to check the data.
Now when we click on the button, the UI Page hangs for the time when the proc is getting executed.
Is there a workaround to show this default message and run the proc in the backend?
Just trigger an #Asynchronous EJB method. It'll fire and forget a separate thread and the bean action method will immediately return.
#Named
public class Bean {
#EJB
private Service service;
public void submit() {
service.asyncDoSomething();
// Add message here.
}
}
#Stateless
public class Service {
#Asynchronous
public void asyncDoSomething() {
// ...
}
}
See also:
Is it safe to start a new thread in a JSF managed bean?
How can server push asynchronous changes to a HTML page created by JSF?
You can call a method on click of button which can use Future or FutureTask, available in the java.util.concurrent package. Former is an interface, the latter is an implementation of the Future interface.
By using "Future" in your code, your asynchronous task will be executed immediately with the promise of the result being made available to the calling thread in the future.
Have a look at this link: Is it safe to start a new thread in a JSF managed bean?
Also have a look at this link: https://codereview.stackexchange.com/questions/39123/efficient-way-of-having-synchronous-and-asynchronous-behavior-in-an-application

Where should I create my ConnectionPool in a JSF application?

I am new to JSF/Facelets and I am creating an application that does the usual CRUD operations over a (No SQL) database. The database has an API that allows the creation of a pool of connections, and then from this object my operations can take and release connections. I suppose this pool has to be created only once for the whole application when it is deployed, be shared (as static?) and closed once the application is destroyed. Is my approach correct? What is the best practice to do this? I have no idea of where I should place my code and how I should call it.
With my old SQL database I used to configure a "testOnBorrow" and a "validationQuery" in the context.xml Resource so I didn't have to create an explicit pool programmatically.
I found two great tutorials (here and here) but I can't figure out from them where to put the code that creates the pool.
Note: I know this might be only a Servlet problem, but I am tagging it as JSF since I don't know if there is a way to do this in JSF (like an application scoped bean). Thanks.
EDIT
Looking at the fact that I cannot find a JAR with a DataSource for the database to be loaded via context.xml, perhaps my question should be more specific: where can I run code once, when a JSF application is deployed and where can I run code when a JSF application is destroyed?
You can implement a weblistner( i.e ServletContextListener). and can use contextInitialized , contextDestroyed method of that to create and destroy your connection pool
Sample :
#WebListener
public class ContextWebListner implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
// initialize connection pool.
}
#Override
public void contextDestroyed(ServletContextEvent event) {
// Destroy connection pool.
}
}

Is there any in JSF2 a callback for activation / deactivation of a session bean? [duplicate]

Is it possible to do something like this: When a user session starts I read a certain integral attribute from the database. As the user performs certain activities in this session, I update that variable(stored in session) & when the session ends, then I finally store that value to the DB.
My question is how do I identify using the JSF framework if the user session has ended & I should then store the value back to DB?
Apart from the HttpSessionListener, you can use a session scoped managed bean for this. You use #PostConstruct (or just the bean's constructor) and #PreDestroy annotations to hook on session creation and destroy
#ManagedBean
#SessionScoped
public class SessionManager {
#PostConstruct
public void sessionInitialized() {
// ...
}
#PreDestroy
public void sessionDestroyed() {
// ...
}
}
The only requirement is that this bean is referenced in a JSF page or as #ManagedProperty of any request scoped bean. Otherwise it won't get created. But in your case this should be no problem as you're apparently already using a session scoped managed bean, just adding a #PreDestroy method ought to be sufficient.
My question is how do I identify using
the JSF framework if the user session
has ended & I should then store the
value back to DB?
The JSF framework does not have a separate concept of a session; it uses the underlying session management features of the Servlet specification.
You would have to create a HttpSessionListener that provides hooks for you to capture the session creation and destruction events, where you can read the value and store it back into the DB.
HttpSessionListener, or if you need Dependency Injection for that save, you might use #PostConstruct & #PreDestroy. Remember that the session is destroyed when you call invalidate() or after session timeout, not when the user closes the browser. Why do you use Session Scope anyway, Conversation Scope might fit you better.

StackOverflow error when initializing JSF SessionScoped bean in HttpSessionListener

Continuing on my previous question, I'm trying to initialize a session-scoped JSF bean when the application's session first starts, so the bean will be available to a user, regardless of which page they access on my web application first. My custom listener:
public class MyHttpSessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent se) {
if (FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
.get("mySessionBean") == null) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
.put("mySessionBean", new MySessionBean());
}
}
}
However, this is giving me a stack overflow error. It appears that the put() method in the SessionMap class tries to create a new HttpSession, thus causing an infinite loop to occur with my listener. How can I initialize a JSF session-scoped bean when my application's session first starts, without running into this issue?
I'm using JSF 2 with Spring 3, running on WebSphere 7.
Thanks!
The session isn't been fully finished creating at that point. Only when the listener method leaves, the session is put into the context and available by request.getSession() as JSF's getSessionMap() is using under the covers.
Instead, you should be grabbing the session from the event argument and use its setAttribute() method. JSF lookups and stores session scoped managed beans just there and won't create a new one if already present.
public void sessionCreated(HttpSessionEvent event) {
event.getSession().setAttribute("mySessionBean", new MySessionBean());
}
Note that I removed the superfluous nullcheck as it's at that point impossible that the session bean is already there.
Unrelated to the concrete problem, you should actually never rely on the FacesContext being present in an implementation which isn't managed by JSF. It is quite possible that the session can be created during a non-JSF request.

Resources