• Post Reply Bookmark Topic Watch Topic
  • New Topic

Thread, EJB and asynch managedbeans.

 
Cedric Bosch
Ranch Hand
Posts: 99
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I want to have an infinite loop in my web app that will run in the entirety of my web app life-cycle. I'm gonna process DB data in the loop. The problem is that my bean is blocked by the loop. I tried to make a new thread (I read I shouldn't do that) but then I cannot access my EJB. I also tried@Singleton, @LocalBean, @Startup without luck (even though it worked once but I couldn't reproduce it). I'm not sure how I should go about this.



thanks in advance
 
Tim Holloway
Bartender
Posts: 18415
58
Android Eclipse IDE Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You cannot spawn threads in EJBs.

You also cannot spawn threads in JSF ManagedBeans, Listeners, Validators or any other JSF component. The very concept of an "async Managed Bean" is foreign to how J2EE works. The JSF lifecycle runs under a thread assigned at random from the server's application thread pool and should not attempt to do anything "infinite" or even long-running. JSF methods should execute as quickly as possible, then return.

If you try to violate these constraints, you will have unpredictable results up to and possibly including random crashing of the appserver itself.

What you can do is spawn a master engine thread from a ServletContextListener's start-listening method. That thread can do pretty much whatever it wants, including spawning sub-threads to do work for it as well as sleeping on a synchronized request queue. EJBs and JSF components can then talk to that subsystem using synchronized data methods.
 
Cedric Bosch
Ranch Hand
Posts: 99
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Holloway wrote:You cannot spawn threads in EJBs.

What you can do is spawn a master engine thread from a ServletContextListener's start-listening method. That thread can do pretty much whatever it wants, including spawning sub-threads to do work for it as well as sleeping on a synchronized request queue. EJBs and JSF components can then talk to that subsystem using synchronized data methods.


I'm guessing you are talking about this method : contextInitialized(ServletContextEvent sce). I tried but I doubt I'm doing it correctly. I'm crashing the server again.



I don't see spawning a thread in that method as a possibility because I'll be working on my database and I can't use ejb in threads.
 
Tim Holloway
Bartender
Posts: 18415
58
Android Eclipse IDE Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, you don't want to DO a "forever" loop in the contextInitialized() method. If you do that, then the context will never finish initializing, the thread that's doing the initializing will be hanging and the server will never finish launching the webapp.

What you want the contextInitialized method to do is launch a child thread to to (or manage) the "forever" work. Unlike the main Http processors, the initialization thread IS allowed to launch sub-threads and those threads can do as much work as they like as long as you run asynchronously and don't make the contextInitialized method wait on them. You can even store a reference to them in a member of the TopStreamsBean class so that you can post requests to that thread(s).

You can invoke EJBs from such threads as well. But the EJBs shouldn't be doing "forever" work either, since they have to be shared with non-forever requesters such as JSF action methods. Leave that sort of work to your engine threads. EJB methods, like JSF methods, should process as fast as they can and then exit. If the EJB is associated with a really slow database function, you may need to get especially clever.

I think that - subject to those limitations - you can use local EJB interfaces and not need remote EJB, since it's all in the same webapp/classpath and doesn't need to cross JVM boundaries or anything like that.
 
Cedric Bosch
Ranch Hand
Posts: 99
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I see..

Tim Holloway wrote:

I think that - subject to those limitations - you can use local EJB interfaces and not need remote EJB, since it's all in the same webapp/classpath and doesn't need to cross JVM boundaries or anything like that.


So should I create a bean annotated @Local that implements an interface annotated @Local and then inject it like a normal ejb with @EJB ? as seen here : http://docs.oracle.com/cd/E19798-01/821-1841/gipmz/index.html ?

In two sentences what I would like to do is that I have a database of users which has those 2 columns: 'username' and 'twitchChannel'. I would like to continuously check what user from my website is in the top 10 that will figure on the front page. So the one with most viewers will be top 1, the second most viewer top2, and so on. Since the number of viewers changes a lot I thought I'd have to do a continuous loop. I understand what you say that my ejb should go back to the container so it can be used again. So I'd need an ejb or other that can be monopolized by thread. Is it what the @Local is about ?

Thanks for the answer. I'm a bit oblivious to those ejb things.
 
Tim Holloway
Bartender
Posts: 18415
58
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ah. This begins to look like you've assumed a solution and you're trying to work your way backwards. I think if I can understand the actual final function I can suggest something more elegant.

So it sounds like what you want is a statistical resource that tracks "top users". I'm not certain how "top users" are defined, but here are some possibilities:

1. Persons who have the most overall page requests to this webapp.

2. Persons who have the most effective page operations in this webapp.

3. One of the above, but tracking across multiple webapps.

4. Something like one of the above but with scoring rules not yet determined.

The same basic mechanism works for 1 and 2, something similar works for 3.

One thing you could do is maintain an EJB that itself keeps and updates the high scores. But instead of constantly chattering to the database, just keep those user IDs and score values in memory in the EJB itself. If you're sharing across webapps, it would be a remote EJB, otherwise it could be a local EJB. This mode of operation is easier to implement and less overall overhead.

To maintain a permanent record, you could run a periodic process that collected the current high scores and posted them to the database (you'd probably also want to reload them if the application restarts). One way of doing that would be to keep the score list in a stateful session EJB and have each user/score row in the table backed by an Entity EJB.

You can do the score updating as a method invoked from the webapp(s) by simply selecting the part of the app where scoring is counted and invoking the score-minder EJB's scoring method. Possible locations for that function are in a request listener (advantage, you can look at ALL requests, including non-JSF requests or in an action method (doesn't keep track of requests rejected because of errors), or in a JSF lifecycle listener (midway between the 2 previous possibilities).

Then the engine thread spawned in the servletContextListener could wake up at intervals and make the score-minder post its latest values. You will have to pay attention to thread-safe (synchronization) and database transaction management to do this reliably, but otherwise, it's not very complicated.

You cannot inject EJBs into the servletContextListener. It's not only not a Managed Bean, it's run before JSF bean management has even been initialized. So you'll need service locator (JNDI) code to locate the score-minder EJB.

I'm presuming that you're probably only actively tracking a few users at one time. If you have 1000 users online all at once, you might need something more elaborate, since the memory requirements would be related to the number of users being actively tracked.
 
Cedric Bosch
Ranch Hand
Posts: 99
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Holloway wrote:
I'm presuming that you're probably only actively tracking a few users at one time. If you have 1000 users online all at once, you might need something more elaborate, since the memory requirements would be related to the number of users being actively tracked.

This is something I'd like to deal with. I'm gonna test my whole user base, not the users that are connected as you said. I'm expecting more than 1000 users (offline and online combined) way more hopefully.
The "highscore" is the top 10 people with the most amount of viewers on their twitch channel (twitch is a streaming website where people stream themselves playing video games). I only have their channel in my database at the moment not the viewers amount. What I'm planning to do is to send HTTP requests to twitch to know each user's viewer amount. Thus I'll do something like this



I'm going the hardroad with this because I could simply request from twitch the TOP 10 ppl. But that would give me the users that don't have an account on my website as well... I might consider this if my idea is too hard to implement but that's a shame.
 
Tim Holloway
Bartender
Posts: 18415
58
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If you need a REALLY large set of data really fast, you may want to consider bumping up the architectural complexity. Consider using a server cluster so as to spread the load. And make the statistical functions stateless. Also consider a mechanism such as Memcached that will attempt to keep as much data in RAM as possible without requiring you to do a lot of fiddling around in application logic.
 
Cedric Bosch
Ranch Hand
Posts: 99
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

I did put this on hold for quiet some time but I'm back to it and re-reading through your answers I'm asking myself some questions:

Tim Holloway wrote:You cannot spawn threads in EJBs.
You also cannot spawn threads in JSF ManagedBeans, Listeners, Validators or any other JSF component. The very concept of an "async Managed Bean" is foreign to how J2EE works. The JSF lifecycle runs under a thread assigned at random from the server's application thread pool and should not attempt to do anything "infinite" or even long-running. JSF methods should execute as quickly as possible, then return.


Even if it's an applicationScoped bean that's only running in the background of the web app and making actions on the database, ie not called anywhere by anyone or anything ? This answer on stackoverflow by BalusC seem to say that it would be ok in such case. He also talks about @Asynchronous so I'll look into that.

By the way, just to be sure, in the thread in question that would run an infinite loop. I can call a stateless EJB right ? To make the actions on the database.

You have given me a lot of help thanks.
 
Tim Holloway
Bartender
Posts: 18415
58
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
They may possess vast wisdom I do not, but otherwise they're all totally full of manure.

The problem, as I said, is that the threads that run J2EE/JEE apps are not dedicated to the app or to a resource within the app. They belong to the server. An application-scoped bean doesn't "run", you invoke methods in it. That is, some HttpRequest invokes a servlet/JSP (for example, the FacesServlet), which runs through request-handling code which eventually decides to invoke a method(s) in the backing bean (regardless of its scope). The thread that all that processing comes from the request thread pool, is returned to that thread pool as soon as the request has been processed, and shouldn't be returned with random junk (for example, child threads) hanging off of it. Failure to observe that constraint can destabilize Tomcat itself.

The safe way to spawn long-running/infinite threads is to do so either in a servet's init() method (not generally encouraged these days) or in a ServletContextListener. In either of these 2 places, the thread that the code is being run under is a global thread and therefore doesn't require the cleanliness that pool threads do.

That's been the way it was historically. The latest generation of JEE has had a lot of improvements in getting "engine" threads to be implicitly supported by the standard, although last time I went looking I didn't see the simplicity I'd been led to expect.

In amongst some assertions that sound totally wrong regardless, the article you reference does provide an interesting example. I just think that there are some constraints that they're not considering, since they assert it's "not a hack" for JSF beans to spawn threads.

The EJB 3.1 Asynchronous bean/method does appear to be able to request setting in motion an independent thread to service async method(s) in that bean. It's not quite as straightforward as simply declaring the bean @Asynchronous - I'm pretty sure you actually have to invoke one of those methods to kickstart it. And I'm not sure about using it for an infinite-loop method, but there's potential. The stickier problem is that EJBs don't function very well as backing beans, so you'd probably have to kickstart off a normal backing bean action. In which case, the scope of the backing bean itself is immaterial, since it's the EJB that's bound to the spawned thread.

If you want to experiment along those lines, by all means go ahead, and we'd be grateful to hear what you learned. The one thing you should look out for is the name of the spawned thread. If it has "pool" in it, you're in trouble. If it has something like "EJB-Async-thread" in the threadname, you're probably safe. You might also have problems if you're using Tomcat or jettty, since any EJB support your app has would have to be supplied by a third-party library (they lack native EJB support). And if you're running something like WebSphere, WebLogic or Glassfish, then make sure it's a new enough version to fully support EJB 3.1.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!