I have one application running in the following environment.
GlassFish Server 4.0
JSF 2.2.8-02
PrimeFaces 5.1 final
PrimeFaces Extension 2.1.0
OmniFaces 1.8.1
EclipseLink 2.5.2 having JPA 2.1
MySQL 5.6.11
JDK-7u11
There are several public pages which are lazily loaded from the database. A few CSS menus are displayed on the header of the template page like displaying category/subcategory-wise featured, top seller, new arrival etc products.
The CSS menus are populated dynamically from the database based on various categories of products in the database.
These menus are populated on every page load which is completely unnecessary. Some of these menus require complex/expensive JPA criteria queries.
Currently the JSF managed beans that populate these menus are view scoped. They all should be application scoped, be loaded only once on application start up and be updated only when something in the corresponding database tables (category/subcategory/product etc) is updated/changed.
I made some attempts to understand WebSokets (never tried before, completely new to WebSokets) like this and this. They worked fine on GlassFish 4.0 but they don't involve databases. I'm still not able to understand properly how WebSokets work. Especially when database is involved.
In this scenario, how to notify the associated clients and update the above-mentioned CSS menus with the latest values from the database, when something is updated/deleted/added to the corresponding database tables?
A simple example/s would be great.
Preface
In this answer, I'll assume the following:
You're not interested in using <p:push> (I'll leave the exact reason in the middle, you're at least interested in using the new Java EE 7 / JSR356 WebSocket API).
You want an application scoped push (i.e. all users gets the same push message at once; thus you're not interested in a session nor view scoped push).
You want to invoke push directly from (MySQL) DB side (thus you're not interested in invoking push from JPA side using an entity listener). Edit: I'll cover both steps anyway. Step 3a describes DB trigger and step 3b describes JPA trigger. Use them either-or, not both!
1. Create a WebSocket endpoint
First create a #ServerEndpoint class which basically collects all websocket sessions into an application wide set. Note that this can in this particular example only be static as every websocket session basically gets its own #ServerEndpoint instance (they are unlike servlets thus stateless).
#ServerEndpoint("/push")
public class Push {
private static final Set<Session> SESSIONS = ConcurrentHashMap.newKeySet();
#OnOpen
public void onOpen(Session session) {
SESSIONS.add(session);
}
#OnClose
public void onClose(Session session) {
SESSIONS.remove(session);
}
public static void sendAll(String text) {
synchronized (SESSIONS) {
for (Session session : SESSIONS) {
if (session.isOpen()) {
session.getAsyncRemote().sendText(text);
}
}
}
}
}
The example above has an additional method sendAll() which sends the given message to all open websocket sessions (i.e. application scoped push). Note that this message can also quite good be a JSON string.
If you intend to explicitly store them in application scope (or (HTTP) session scope), then you can use the ServletAwareConfig example in this answer for that. You know, ServletContext attributes map to ExternalContext#getApplicationMap() in JSF (and HttpSession attributes map to ExternalContext#getSessionMap()).
2. Open the WebSocket in client side and listen on it
Use this piece of JavaScript to open a websocket and listen on it:
if (window.WebSocket) {
var ws = new WebSocket("ws://example.com/contextname/push");
ws.onmessage = function(event) {
var text = event.data;
console.log(text);
};
}
else {
// Bad luck. Browser doesn't support it. Consider falling back to long polling.
// See http://caniuse.com/websockets for an overview of supported browsers.
// There exist jQuery WebSocket plugins with transparent fallback.
}
As of now it merely logs the pushed text. We'd like to use this text as an instruction to update the menu component. For that, we'd need an additional <p:remoteCommand>.
<h:form>
<p:remoteCommand name="updateMenu" update=":menu" />
</h:form>
Imagine that you're sending a JS function name as text by Push.sendAll("updateMenu"), then you could interpret and trigger it as follows:
ws.onmessage = function(event) {
var functionName = event.data;
if (window[functionName]) {
window[functionName]();
}
};
Again, when using a JSON string as message (which you could parse by $.parseJSON(event.data)), more dynamics is possible.
3a. Either trigger WebSocket push from DB side
Now we need to trigger the command Push.sendAll("updateMenu") from the DB side. One of simplest ways it letting the DB to fire a HTTP request on a web service. A plain vanilla servlet is more than sufficient to act like a web service:
#WebServlet("/push-update-menu")
public class PushUpdateMenu extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Push.sendAll("updateMenu");
}
}
You've of course the opportunity to parameterize the push message based on request parameters or path info, if necessary. Don't forget to perform security checks if the caller is allowed to invoke this servlet, otherwise anyone else in the world other then the DB itself would be able to invoke it. You could check the caller's IP address, for example, which is handy if both DB server and web server run at the same machine.
In order to let the DB fire a HTTP request on that servlet, you need to create a reusable stored procedure which basically invokes the operating system specific command to execute a HTTP GET request, e.g. curl. MySQL doesn't natively support executing an OS specific command, so you'd need to install a user defined function (UDF) for that first. At mysqludf.org you can find a bunch of which SYS is of our interest. It contains the sys_exec() function which we need. Once installed it, create the following stored procedure in MySQL:
DELIMITER //
CREATE PROCEDURE menu_push()
BEGIN
SET #result = sys_exec('curl http://example.com/contextname/push-update-menu');
END //
DELIMITER ;
Now you can create insert/update/delete triggers which will invoke it (assuming table name is named menu):
CREATE TRIGGER after_menu_insert
AFTER INSERT ON menu
FOR EACH ROW CALL menu_push();
CREATE TRIGGER after_menu_update
AFTER UPDATE ON menu
FOR EACH ROW CALL menu_push();
CREATE TRIGGER after_menu_delete
AFTER DELETE ON menu
FOR EACH ROW CALL menu_push();
3b. Or trigger WebSocket push from JPA side
If your requirement/situation allows to listen on JPA entity change events only, and thus external changes to the DB does not need to be covered, then you can instead of DB triggers as described in step 3a also just use a JPA entity change listener. You can register it via #EntityListeners annotation on the #Entity class:
#Entity
#EntityListeners(MenuChangeListener.class)
public class Menu {
// ...
}
If you happen to use a single web profile project wherein everything (EJB/JPA/JSF) is thrown together in the same project, then you can just directly invoke Push.sendAll("updateMenu") in there.
public class MenuChangeListener {
#PostPersist
#PostUpdate
#PostRemove
public void onChange(Menu menu) {
Push.sendAll("updateMenu");
}
}
However, in "enterprise" projects, service layer code (EJB/JPA/etc) is usually separated in EJB project while web layer code (JSF/Servlets/WebSocket/etc) is kept in Web project. The EJB project should have no single dependency on web project. In that case, you'd better fire a CDI Event instead which the Web project could #Observes.
public class MenuChangeListener {
// Outcommented because it's broken in current GF/WF versions.
// #Inject
// private Event<MenuChangeEvent> event;
#Inject
private BeanManager beanManager;
#PostPersist
#PostUpdate
#PostRemove
public void onChange(Menu menu) {
// Outcommented because it's broken in current GF/WF versions.
// event.fire(new MenuChangeEvent(menu));
beanManager.fireEvent(new MenuChangeEvent(menu));
}
}
(note the outcomments; injecting a CDI Event is broken in both GlassFish and WildFly in current versions (4.1 / 8.2); the workaround fires the event via BeanManager instead; if this still doesn't work, the CDI 1.1 alternative is CDI.current().getBeanManager().fireEvent(new MenuChangeEvent(menu)))
public class MenuChangeEvent {
private Menu menu;
public MenuChangeEvent(Menu menu) {
this.menu = menu;
}
public Menu getMenu() {
return menu;
}
}
And then in the web project:
#ApplicationScoped
public class Application {
public void onMenuChange(#Observes MenuChangeEvent event) {
Push.sendAll("updateMenu");
}
}
Update: at 1 april 2016 (half a year after above answer), OmniFaces introduced with version 2.3 the <o:socket> which should make this all less circuitous. The upcoming JSF 2.3 <f:websocket> is largely based on <o:socket>. See also How can server push asynchronous changes to a HTML page created by JSF?
Since you are using Primefaces and Java EE 7 it should be easy to implement:
use Primefaces Push ( example here http://www.primefaces.org/showcase/push/notify.xhtml )
Create a view which listen to a Websocket endpoint
Create a database listener which produces a CDI event on database change
The payload of the event could either be the delta of the latest data or just and update information
Propagate the CDI event via Websocket to all clients
Clients updating the data
Hope this helps
If you need some more details just ask
Regards
PrimeFaces has poll features to update the component automatically. In the following example, <h:outputText> will be auto updated every 3 seconds by <p:poll>.
How to notify the associated clients and update the above-mentioned CSS menus with the latest values from the database?
Create a listener method like process() to select your menu data. <p:poll> will be auto-update your menu component.
<h:form>
<h:outputText id="count"
value="#{AutoCountBean.count}"/> <!-- Replace your menu component-->
<p:poll interval="3" listener="#{AutoCountBean.process}" update="count" />
</h:form>
#ManagedBean
#ViewScoped
public class AutoCountBean implements Serializable {
private int count;
public int getCount() {
return count;
}
public void process() {
number++; //Replace your select data from db.
}
}
Related
I process messages from a queue. I use data from the incoming message to determine which class to use to process the message; for example origin and type. I would use the combination of origin and type to look up a FQCN and use reflection to instantiate an object to process the message. At the moment these processing objects are all simple POJOs that implement a common interface. Hence I am using a strategy pattern.
The problem I am having is that all my external resources (mostly databases accessed via JPA) are injected (#Inject) and when I create the processing object as described above all these injected objects are null. The only way I know to populate these injected resources is to make each implementation of the interface a managed bean by adding #stateless. This alone does not solve the problem because the injected members are only populated if the class implementing the interface is itself injected (i.e. container managed) as opposed to being created by me.
Here is a made up example (sensitive details changed)
public interface MessageProcessor
{
public void processMessage(String xml);
}
#Stateless
public VisaCreateClient implements MessageProcessor
{
#Inject private DAL db;
…
}
public MasterCardCreateClient implements MessageProcessor…
In the database there is an entry "visa.createclient" = "fqcn.VisaCreateClient", so if the message origin is "Visa" and the type is "Create Client" I can look up the appropriate processing class. If I use reflection to create VisaCreateClient the db variable is always null. Even if I add the #Stateless and use reflection the db variable remains null. It's only when I inject VisaCreateClient will the db variable get populated. Like so:
#Stateless
public QueueReader
{
#Inject VisaCreateClient visaCreateClient;
#Inject MasterCardCreateClient masterCardCreateClient;
#Inject … many more times
private Map<String, MessageProcessor> processors...
private void init()
{
processors.put("visa.createclient", visaCreateClient);
processors.put("mastercard.createclient", masterCardCreateClient);
… many more times
}
}
Now I have dozens of message processors and if I have to inject each implementation then register it in the map I'll end up with dozens of injections. Also, should I add more processors I have to modify the QueueReader class to add the new injections and restart the server; with my old code I merely had to add an entry into the database and deploy the new processor on the class path - didn't even have to restart the server!
I have thought of two ways to resolve this:
Add an init(DAL db, OtherResource or, ...) method to the interface that gets called right after the message processor is created with reflection and pass the required resource. The resource itself was injected into the QueueReader.
Add an argument to the processMessage(String xml, Context context) where Context is just a map of resources that were injected into the QueueReader.
But does this approach mean that I will be using the same instance of the DAL object for every message processor? I believe it would and as long as there is no state involved I believe it is OK - any and all transactions will be started outside of the DAL class.
So my question is will my approach work? What are the risks of doing it that way? Is there a better way to use a strategy pattern to dynamically select an implementation where the implementation needs access to container managed resources?
Thanks for your time.
In a similar problem statement I used an extension to the processor interface to decide which type of data object it can handle. Then you can inject all variants of the handler via instance and simply use a loop:
public interface MessageProcessor
{
public boolean canHandle(String xml);
public void processMessage(String xml);
}
And in your queueReader:
#Inject
private Instance<MessageProcessor> allProcessors;
public void handleMessage(String xml) {
MessageProcessor processor = StreamSupport.stream(allProcessors.spliterator(), false)
.filter(proc -> proc.canHandle(xml))
.findFirst()
.orElseThrow(...);
processor.processMessage(xml);
}
This does not work on a running server, but to add a new processor simply implement and deploy.
Within an Vaadin application I am planning to implement an asynchronous result-overview for a method.
The result overview contains a table for possible results. These results should generate while a backend-method is running asynchronous in a thread. Communication between the backend and frontend of the application is planned with using CDI-Events (information for the result will be in the CDI-Event).
I already achieved to fire CDI-Events, put them into the result-table and display the table after the method is finished. But when I execute the method within a thread (so the view is displayed and events get inserted instead of waiting to see the complete table), my CDI-Events won't fire (or get received).
Is there any way to work this out? I read about receiving CDI-Events asynchronous (blog entry), but I did not find anything about firing events within a thread...
WildFly 10.0.1.Final, Java 8, Java-EE 7 and Vaadin 7.6.6.
Thread, which should fire CDI-Events:
public class Executer implements Runnable{
#Override
public void run(){
// Here will be the backend-method invocation for firing CDI-Events
// CDI-Dummy-Event - Does not fire properly. receiveStatusEvent() does not invoke
BeanManager beanManager = CDI.current().getBeanManager();
beanManager.fireEvent(new ResultEvent("Result event example"));
}
}
Bean which receives CDI-Events
public class EventReceiver implements LoggingProvider{
public EventReceiver(){
}
public void receiveStatusEvent(#Observes ResultEvent event) {
this.info("Event received: " + event.toString());
}
}
Starting the thread with help from ManagedExecutorService
public void executeAsynchBackendMethod(){
// CDI-Dummy-Event works - receiveStatusEvent() invokes correctly
BeanManager beanManager = CDI.current().getBeanManager();
beanManager.fireEvent(new ResultEvent("Result event example"));
/* The following alternative starts a thread, but the events, which are fired in the run() method, do not take any action in the receiveStatusEvent() method */
// Getting managedExecuterService
this.managedExecuterService = (ManagedExecutorService) new InitialContext().lookup("java:comp/DefaultManagedExecutorService");
// Getting Instance of executer-Runnable (for injecting the backend-service afterwards)
Instance<Executer> executerInstance = CDI.current().select(Executer.class);
Executer executer = executerInstance.get();
// Start thread
this.managedExecuterService.submit(executer);
}
In CDI 1.2 and below, events are strictly synchronous. In CDI 2.0 there is already an implemented version of asynchronous events (in Weld) but I suppose you are stuck with 1.2.
That means (as the blog post you read suggests), you can make use of the infamous EJBs.
As for why CDI does not work in this case - I would say this is all thread-bound. In other words in that given thread where you fire the event you have no observer to be triggered.
Coming from a Guice background, I know that it is possible to seed an object value from a scope using.
scope.seed(Key.get(SomeObject.class), someObject);
I suppose one could do this by registering a Bean that gets a value from an AbstractBoundContext, but examples just seeding one value from a Custom Scope seem hard to find. How do I create a custom scope that seeds a value that can be injected elsewhere?
Edit:
I am currently using the following workaround, that can be injected in an interceptor to set the Configuration when entering the scope, and can then be injected through its thread local provider. I am still looking for options that feel less hacky / are more integrated with the scope/scope context system in Weld though.
#Singleton
public class ConfigurationProducer {
private final InheritableThreadLocal<Configuration> threadLocalConfiguration =
new InheritableThreadLocal<>();
#Produces
#ActiveDataSet
public ConfigurationConfiguration() {
return threadLocalConfiguration.get()
}
public void setConfiguration(Configuration configuration) {
threadLocalConfiguration.set(configuration);
}
}
The answer is to register a custom bean with the AfterBeanDiscovery event, like so:
event.addBean()
.createWith(ctx -> commandContext.getCurrentCommandExecution())
.addType(CommandExecution.class)
.addQualifier(Default.Literal.INSTANCE)
.scope(CommandScoped.class)
.beanClass(CommandExtension.class);
There is a quite sophisticated example available at https://github.com/weld/command-context-example
A request scoped bean collects data, from all many other request beans & business logic. This bean is used through the EL expressions in the page but before this request scoped bean may be used in the page, it needs to build a directory using the collected data (This is done after all collection is over but before the bean properties may be used in page).
How can I execute the building of the directory in this bean after all collection but before it is used through the EL expressions in the page without using <f:event>? I need to build it only once.
#ManagedBean(name="namesDirectory")
#RequestScoped
public class NamesDirectory {
public void addForPersonNameRetrieval(Integer id) { // this is used to collect the data in bean
peopleNamesMap.put(id,null);
.......
}
public void buildDirectory(){ // used, when all collection is over, to build the diirectory
.......
}
public String getPersonName(Integer id) { // used in the JSF page through EL expressions
name = peopleNamesMap.get(id);
}
}
Here buildDirectory() needs to be executed at the end of all collection but before using getPersonName() in the JSF page
You have got several options. You could rebuild the directory after every insert or before every retrieval, however this may cause unnecessary rebuilds. You could rebuild the directory only when needed and called:
Add a flag requiresRebuild and default it to true.
Set it to true in addForPersonaNameRetrieval.
Set it to false in buildDirectory.
Call buildDirectory in getPersonName if a rebuild is necessary.
I have an application that has a class named: UploadItem. The application creates uploading tasks based on information it has, for example, an upload needs to be created to upload a file to sitex.com with this the application creates a new UploadItem and adds that to an ObservableCollection, the collection is bound to a listview.
Now comes the part that I cannot solve.. I decided to change the structure so that people can create their own plugins that can upload a file, the problem lies with the fact that the UploadItem class has properties such as:
string _PercentagedDone;
public string PercentageDone
{
get { return _PercentagedDone; }
set { _PercentagedDone = value + "%"; NotifyPropertyChanged("PercentageDone"); }
}
But the plugin controls on how a file is uploaded, so how would the plugin edit the PercentageDone property that is located in the UploadItem class? If there is no way to do such a thing, then is there another way to achieve the same, i.e. showing the progress on the main GUI?
You'll want to define an interface for the plugins. Something like:
public interface IUploadPlugin
{
Task<bool> Upload(IEnumerable<Stream> files);
int Progress { get; }
}
The plugins then need to implement this interface and export themselves:
[Export(typeof(IUploadPlugin))]
public class MyUploader : IUploadPlugin, INotifyPropertyChanged
{
// ...
}
Notice that this plugin implements INotifyPropertyChanged. This is an easy way to handle updating the progress. Fire PropertyChanged on the Progress property and then databind your ProgressBar control in the main view to this property. Make sure that you fire PropertyChanged on the UI thread.
Another option would be to fire a custom event when the property changes. You could handle this event in the main view logic and update the progress.
Notice that I'm using Task for the return. This allows the caller to wait until the upload task finishes. You could use a callback instead, but with the CTP of the next version of .NET, using Task<> will allow you to use the await keyword for your async programming. Check it out here and here.