I am using JBoss 4.2.3 with JDK 1.5. I have created a stateless EJB whose purpose is to delete a file after a specified period of time (in milliseconds).
The EJB code is:
import java.io.File;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import org.jboss.annotation.ejb.LocalBinding;
#Stateless
#LocalBinding(jndiBinding = "TimedFileDeletion")
public class TimedFileDeletionBean implements TimedFileDeletionBeanLocal {
#Resource
TimerService timerService;
File fileToDelete;
public void setRequiredInfo(long intervalDuration, File fileToDelete) {
timerService.createTimer(intervalDuration, "Created new timer");
this.fileToDelete = fileToDelete;
}
#Timeout
public void timeout(Timer timer) {
System.out.println("Timeout occurred");
if(fileToDelete.exists()) {
fileToDelete.delete();
}
}
}
The local interface is:
import java.io.File;
public interface TimedFileDeletionBeanLocal {
public void setRequiredInfo(long intervalDuration, File fileToDelete);
}
When I call the bean through the web container (I use the Stripes framework) the timeout method is called after the specified time but it only prints "Timeout occurred", it does not delete the file and it throws an exception. This is the console output:
INFO [STDOUT] Timeout occurred
ERROR [TimerImpl] Error invoking ejbTimeout: javax.ejb.EJBException: java.lang.NullPointerException
Any advice would be appreciated.
In stateless session bean, conversational state is not maintained. Stateless bean instance variables are shared between invocations, so they may overlap.
Therefore even if you set file using setRequiredInfo(), on timeout it gets the fileToDelete null.
Try checking null before doing operation.
Below is some code snippet might help you.
class FileUtility {
// Make singleton class to store list of files to delete
public static List<File> files;
//-- get/set accessing methods
}
//---------------------
public void setRequiredInfo(long intervalDuration, File fileToDelete) {
timerService.createTimer(intervalDuration, fileToDelete.getName()+Math.random());
FileUtility.files.add(fileToDelete);
}
//---------------------
#Timeout
public void timeout(Timer timer) {
System.out.println("Timeout occurred");
for(File fileToDelete : Fileutility.files){
if(fileToDelete.exists()) {
fileToDelete.delete();
}
}
}
One thing that appears that might be a problem is that you're passing into the setRequiredInfo method a reference to a File. That reference is then stored locally, using a debugger I would verify that the reference value is the same when the timer fires. I suspect that it may no longer be the same file referenced or that the File object may be transient.
Also, just a little warning with EJBTimers and JBoss. This version of JBoss spins a thread for every EJB with a timer. So, if you have 500 files to delete with these EJBs, JBoss will spin up 500 threads. This behavior, while undesirable, does comply with the EJB spec (which is ambiguous on implementation). These threads will be recreated if the container restarts and the timers are still waiting to fire.
Related
I was trying to migrate from payara/micro:4.181 to payara/micro:5.2022.3 and I noticed that the initialization method with observer [#initialized(ApplicationScoped.class) ServletContext init] is not invoked inside Jar files.
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext init)
It is invoked correctly when using payara/micro:4.181 though.
To reproduce the described behaviour:
download the attached reproduce_observer_issue.zip from github_link_to_illustration_files
unzip the files into "current_dir"
the unzipped files contains a Dockerfile with following content:
#FROM payara/micro:5.2022.3
FROM payara/micro:4.181
COPY app.war $DEPLOY_DIR
uncomment the line corresponding to the version of payara/micro you want to run the app with.
Run the following docker commands to deploy the app :
docker build -t repissue:v1 .
docker run repissue:v1
If you check the path "current_dir\sources\libs\lib\src\main\java\mylib\Library.java" you can see that it contains two init methods, however when deploying on payara/micro:5.2022.3 init(#observes #initialized(ApplicationScoped.class) ServletContext init) won't be invoked (check logs)
package mylib;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
import javax.servlet.ServletContext;
#ApplicationScoped
public class Library {
public boolean someLibraryMethod() {
return true;
}
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
System.out.println(" ### log-1 mylib.Library.init(java.lang.Object) called ###");
}
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext init) {
System.out.println(" ### log-2 mylib.Library.init(javax.servlet.ServletContext) invoked ###");
}
}
Thanks in advance for any eventual reply/hint
I think there is a misunderstanding about what to expect from #Observes #Initialized(ApplicationScoped.class). The JakartaEE specs (8) say:
An event with this qualifier is fired when a context is initialized, i.e. ready for use.
And "context" here is meant in the sense of CDI context, which ServletContext is definitely not.
Also I found no documentation whatsoever about what the type of the event message will be. Hence this is totally implementation specific and there should be made no assumptions about it (if you want to keep your code portable).
If your Library object received that event twice (once with Objectand once with ServletContext) in Payara 4, then this can be considered a mistake (not to say bug) of that platform.
I'd recommend to either implement a javax.servlet.ServletContainerInitializer or a javax.servlet.ServletContextListener, if you want to be notified about server startup and get hold of ServletContext during that phase. The big advantage of ServletContextListener (over the other) is that you can easily interact with CDI beans (#ApplicationScoped or #Singleton).
Here is an example:
package mylib;
import javax.inject.Inject;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class MyListener implements ServletContextListener {
#Inject
Library library;
#Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println(" ### log-4 mylib.MyListener.contextInitialized() invoked ###");
System.out.println(sce.getServletContext());
System.out.println(library);
System.out.println(library.someLibraryMethod());
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println(" ### log-5 mylib.MyListener.contextDestroyed() invoked ###");
System.out.println(sce.getServletContext());
}
}
(tested with your Docker project)
Hope this helps.
Update
I also found the following Gist which seems to be exactly what you want to achieve: https://gist.github.com/mojavelinux/637959
Note that here #Initialized and #Destroyed are custom qualifiers and not the ones from package javax.enterprise.context.
Additionally here is an updated version of that Gist using the qualifiers from javax.enterprise.context: https://gist.github.com/nineninesevenfour/d9c643ff5a7f98302f89687720d0a138. With that your original code of Library can to stay unchanged.
With the updated Gist however these two methods are called twice (once from Payara and once triggered from ServletContextLifecycleNotifier:
// Library.java
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
System.out.println(" ### log-1 mylib.Library.init(java.lang.Object) called ###");
}
// App.java
public static void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
System.out.println(new App().getGreeting());
}
I'm using a Java EE 7 + GlassFish and need to perform some operation against a number of JPA entities from a stateless bean.
#Stateless
public class JobRunner
{
public void do()
{
for (Entity entity:facade.findAll())
{
///do some work against entity
}
}
}
This JobRunner bean is injected into the servlet and I invoke do() method from the web UI.
The issue is that all entities are being changed within one transaction so if one fails everything is rolled back what is not desirable. Is there a way to start and close a new transaction for each entity (i.e. for each iteration of the loop)?
I can write an external client and make a loop there calling a stateless bean for each entity but it's not something that completely works for me as I prefer to keep an app monolithic. Can I somehow manage transactions form inside a container?
Maybe JMS helps? If I implement a doer as message listener and will be sending a message for each entity, will it start a new transaction for each one?
#Stateless
public class JobRunner
{
public void do()
{
for (Entity entity:facade.findAll())
{
sendMessageToRealDoer(entity);
}
}
}
Create another bean, specifying #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW), at method or bean level:
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
#Stateless
public class JobWork {
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doWork(Entity entity) {
// do what you would do in the loop with the Entity
// this runs in a new transaction
}
}
I wish I could tell you that you only need to annotate a method of the same bean (JobRunner) and simply call it. This is not possible (EDIT)without workarounds - check comment from Steve C(/EDIT) because when calling methods of this object in EJBs and CDI beans the interceptors do not get called. Transactions are implemented with interceptors in both cases.
Some notes:
If the total duration of the operations in the loop is expected to be long, you will get a timeout in the outer transaction, that is implicitly started for the JobRunner stateless EJB. You will want to take measure that no "outer" transaction is started.
Sending the data to a queue will work too; but queues will process them asynchronously, meaning that the execution will return to the servlet calling JobRunner.do() most probably before all items have been processed.
We have the following simple app: one #Singleton bean with #Timeout method and one servlet which starts timer. After first deploy we see that method is called once in 2 seconds - which is expected. Then after hot re-deploy we see that method is called twice within 2 seconds. After a few redeploys method is called multiple times during the same 2 seconds. Restarting the server doesn't help. See the code below:
import javax.ejb.*;
#Remote(TimerRemote.class)
#Singleton
public class TimerBean implements TimerRemote {
#Resource
private SessionContext context;
public void startTimer() {
context.getTimerService().createTimer(2000,2000,null);
}
#Timeout
public void timeoutCallback(javax.ejb.Timer timer) {
System.out.println("timeoutCallback is called: " + timer);
}
}
#Timeout method should be called after given interval time. Currently method is getting called multiple times within a second.
The timer is persistent per default and is not cancelled at all.
Please refer to the official Java EE6 Tutorial: Using the Timer Service.
Prefer using #Schedule and set persistent=false if you don't need the timer to be persistent. Or try a programmatic approach controlling the lifecycle of the timer on your own.
I have a Java EE application which should start a synchronization process with an external system once after its deployment.
How could I implement this requirement?
Below are listed a couple of popular methods for getting lifecycle callbacks in JavaEE apps.
Create a javax.servlet.ServletContextListener implementation
If you have a web component to your .ear file (embedded .war) or your deployment is a .war by itself you can add a ServletContextListener to your web.xml and get a callback when the server starts or is shutting down.
Example:
package com.stackoverflow.question
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
public class MyServletContextListener implements ServletContextListener{
#Override
public void contextInitialized(ServletContextEvent contextEvent) {
/* Do Startup stuff. */
}
#Override
public void contextDestroyed(ServletContextEvent contextEvent) {
/* Do Shutdown stuff. */
}
}
and then add this configuration to your web.xml deployment descriptor.
$WAR_ROOT/WEB-INF/web.xml.
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee">
<listener>
<listener-class>com.stackoverflow.question.MyServletContextListener</listener-class>
</listener>
</web-app>
Create an EJB 3.1 #Startup Bean
This method uses an EJB 3.1 singleton to get a startup and shutdown callback from the server.
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Startup;
import javax.ejb.Singleton;
#Singleton
#Startup
public class LifecycleBean {
#PostConstruct
public void init() {
/* Startup stuff here. */
}
#PreDestroy
public void destroy() {
/* Shutdown stuff here */
}
}
I tested the suggested solution which uses the #Startup and #PostConstruct annotations. It turned out that Glassfish does not complete the deployment of an application until all methods annotated with #PostConstruct have finished. So in my case the deployment would take from several minutes up to an hour.
But I figured out a different way to achive what I want. The best solution seems to be a timer callback method which cancels its timer after its execution.
#Stateless
public class SynchronisationService {
#Schedule(hour = "*", minute = "*", persistent = false)
protected void init(Timer timer)
{
doTheSync();
timer.cancel();
}
}
Using a non-persistent timer allows the timer to be re-created if the application server is restarted.
You can use the #Startup and #PostConstruct annotations to perform tasks on application startup.
Using a ServletContextListener, or a servlet that is initialized at startup, for example. Of course, this becomes much harder if you have multiple deployments of the application in a cluster, and only want this process to be run once.
in the JSF-2 application I'm working on, I need to start a server side Timer when a user does an action.
This timer must be related to the application itself, so it must survive when the user session is closed.
To solve this problem, I thought to use java.util.Timer class instantiating the timer object in an Application scoped bean.
Could it be a good solution? Are there other better ways to achive this? Thanks
No ejb-container
If your container doesnt have ejb capabilities (tomcat, jetty etc..), you can go with quartz scheduler library: http://quartz-scheduler.org/
They also has some nice code samples: http://quartz-scheduler.org/documentation/quartz-2.1.x/examples/Example1
EJB 3.1
If your app-server have a EJB 3.1 (glassfish, Jboss), there is a java ee standard way of creating timers. Mainly look into the #Schedule and #Timeout annotations.
Something like this might cover your usecase (method annotated #Timeout will be invoked when timer runs out)
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
#Stateless
public class TimerBean {
#Resource
protected TimerService timerService;
#Timeout
public void timeoutHandler(Timer timer) {
String name = timer.getInfo().toString();
System.out.println("Timer name=" + name);
}
public void startTimer(long initialExpiration, long interval, String name){
TimerConfig config = new TimerConfig();
config.setInfo(name);
config.setPersistent(false);
timerService.createIntervalTimer(initialExpiration, interval, config);
}
}