0

Hopefully I can make some sense, I've never done this particular task before.

I have an application where I want to create a bean on startup that has a scheduled task that runs every 30 minutes and updates a Map that is used by all sessions in the application. My initial thought was to create an ApplicationScoped bean for this task.

So the idea is this:

  1. User A logs in. Stores value in his Map.
  2. User B logs in. Stores value in his Map.
  3. Process runs, updates all values in map.
  4. User B and A will check their value constantly throughout the session.
  5. Logout, remove value from map.

So right now it looks like this:

@ManagedBean(eager=true, name="monitor")
@ApplicationScoped
public class MyMonitor implements Serializable {

    private static final long serialVersionUID = -1L;
    private ScheduledExecutorService scheduler; 
    private HashMap<Integer, String> myDict;

    @PostConstruct
    public void init() {
        myDict = new HashMap<Integer, String>();
        myDict.put(1, "a");
        myDict.put(2, "b");
        myDict.put(3, "c");

        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 30, TimeUnit.SECONDS);
    }

    @PreDestroy
    public void destroy() {
        scheduler.shutdownNow();
    }

    public class SomeDailyJob implements Runnable {

        @Override
        public void run() {
            System.out.println("hello world");
        }

    }

    public HashMap<Integer, String> getMyDict() {
        return myDict;
    }
    public void setMyDict(HashMap<Integer, String> myDict) {
        this.myDict = myDict;
    }
}

In another class, I need to somehow retrieve the value from myDict based on key (this class is in the DAO layer, it is not a managed bean). I tried to instantiate this bean in that class:

public class MyDAO {
    @ManagedProperty(value="#{myMonitor}")
    private MyMonitor monitor;
}

And got:

WARNING: The web application [app] is still processing a request that has yet to finish

My questions are this:

  1. Should I actually use an ApplicationScoped bean for this problem? I do not have EJB.
  2. I know I haven't added the synchronicity yet, but is this safe? Can this actually work?
  • 1
    So you found this answer by BalusC but decided to not have a weblistener manage the scheduling? https://stackoverflow.com/questions/4691132/how-to-run-a-background-task-in-a-servlet-based-web-application – Selaron Oct 21 '19 at 20:01

1 Answers1

-2

You can use a java.util.Timer for this. Define a class

    import java.util.TimerTask;

    public class Monitor extends TimerTask {

        @Override
        public void run() {
          // do something
        }
    }

then your class may be refactored to something like (I removed other code to keep just the idea)

@ManagedBean(eager=true, name="monitor")
@ApplicationScoped
public class MyMonitor implements Serializable {

    //runs as daemon thread
    private final Timer timer = new Timer(true);
    private Monitor monitor = new Monitor();

    @PostConstruct
    public void init() {       
       // period are in milliseconds
       timer.scheduleAtFixedRate(monitor, 0, 30*1000);
    }

    @PreDestroy
    public void destroy() {
       timer.cancel();
    }
}

This will help you also to move most of the update logic in the Monitor.run() separating it from the scheduler logic. I hope it helps.

user2688838
  • 681
  • 5
  • 8
  • 5
    According to https://stackoverflow.com/questions/7499534/spawning-threads-in-a-jsf-managed-bean-for-scheduled-tasks-using-a-timer you should *never* use java.util.Timer in Java EE – Tomek Oct 22 '19 at 11:13
  • OP is not using java-ee otherwise an EJB would have been available – Kukeltje Apr 16 '20 at 13:57