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:
User A logs in. Stores value in his Map.
User B logs in. Stores value in his Map.
Process runs, updates all values in map.
User B and A will check their value constantly throughout the session.
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:
Should I actually use an ApplicationScoped bean for this problem?
I do not have EJB.
I know I haven't added the synchronicity yet,
but is this safe? Can this actually work?
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.
Related
I'm writing REST API of coupons system,
and I'm trying to create a thread that works all the time that the server is running.
The thread needs to remove the token+client session if the client doesn't use the server (through the controllers class) passes 10 seconds.
The class of the thread:
public class ClientSessionCleaner implements Runnable {
private boolean run = true;
private Map<String, ClientSession> tokensMap;
public ClientSessionCleaner() {
/*Empty*/
}
#Autowired
public ClientSessionCleaner(#Qualifier("tokens") Map<String, ClientSession> tokensMap) {
this.tokensMap = tokensMap;
}
#Override
public void run() {
HashMap<String, ClientSession> copy = new HashMap<>(tokensMap);
do {
CleanMap(copy);
}while (run);
}
private void CleanMap(HashMap<String, ClientSession> copy) {
copy.forEach((k, v) -> {
if (System.currentTimeMillis() - v.getLastAccessMillis() == 10 * 1_000){
copy.remove(k);
}
});
}
I'm starting the thread in the main class, it is ok?
public static void main(String[] args) {
SpringApplication.run(CouponSystemApplication.class, args);
ClientSessionCleaner cleaner = new ClientSessionCleaner();
Thread thread =new Thread(cleaner);
thread.start();
}
When I'm starting the server I'm getting this:
Exception in thread "Thread-178" java.lang.NullPointerException
at java.base/java.util.HashMap.putMapEntries(HashMap.java:496)
at java.base/java.util.HashMap.<init>(HashMap.java:485)
at com.Avinadav.couponsystem.rest.login.ClientSessionCleaner.run(ClientSessionCleaner.java:25)
at java.base/java.lang.Thread.run(Thread.java:834)
The tokens map:
#Configuration
public class RestConfiguration {
#Bean(name = "tokens")
public Map<String, ClientSession> tokensMap() {
return new HashMap<>();
}
}
I don't know if the thread code is ok (?) and what I should do to make the thread work.
I'm new with threads,
thx for all the help!
If I understand you correctly, it seems like you're trying to implement some kind of a cleanup service for outdated ClientSessions. Is that right?
If so, your Runnable can actually be a #Component in which a #Scheduled annotation will define a periodic procedure in which the cleaning will take place.
For more info about Scheduling, check out the The #Scheduled Annotation in Spring
Your use-case may fit the functionality of a popular caching library like Caffeine or Google Guava, because it has support for maps with time-based eviction and it seems to be me that's what you're trying to accomplish.
LoadingCache<String, ClientSession> tokensMap = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS)
.build();
For more complex logic use LoadingCache#expireAfter. Using a library like this will prevent you from having to deal with complex concurrency issues.
I have a Java, Spring application where I schedule some report jobs. The component looks like this:
#Component
public class RegisterReportSchedules implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ThreadPoolTaskScheduler ts;
private List<String> reportSchedules; //contains list of report schedules
#Autowired
private SomeTask sometask;
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
reportSchedules.forEach((String schedule) -> {
ReportSchedule reportSchedule = new ReportSchedule(schedule,
propertiesUtil.getProperty(schedule + "." + Constants.CRON));
ts.schedule(new ReportTask(reportSchedule),
new CronTrigger(reportSchedule.getCronExpression()));
});
}
class ReportTask implements Runnable {
private ReportSchedule schedule;
public ReportTask(ReportSchedule schedule) {
this.schedule = schedule;
}
#Override
public void run() {
sometask.process(schedule);
}
}
}
Say I have 5 reportSchedules to process. After all 5 ReportTasks have been completed, I need to write into a db table one entry to say all report task completed.
But how can I track this information about each report schedule is completed in my application?
Do I need to write to database table for each schedule completed or is there a better alternative within Spring that will trigger a notification event of some sort which I can then use to write the ALL COMPLETED event to the table? Appreciate if some answers with examples are given.
Since you don't need to track the reportSchedules, I'd be tempted to do something like:
Move to use a Queue so that the Strings are removed when you poll.
Track the number of tasks that you submit. (*)
Add a custom ApplicationEvent type of ReportScheduleProcessedEvent (or similar) and publish one of these (to Spring's ApplicationEventPublisher) at the end of the ReportTask's run method.
Add a new ApplicationListener for this type, which waits until it has received as many events as you tracked in (*); and then publishes something to the database.
I'm afraid I haven't provided any code here, since you might or might not need to care about thread-safety at a bunch of points above, and handling this appropriately might not be trivial.
Edit; per comment asking for a sample. I'll assume you're using at least spring 4.3.
Edit Edit: per more comments.
abstract class ReportScheduleEvent extends ApplicationEvent { ... }
public class IncomingReportCompletionEvent
extends ReportScheduleEvent {
private final int eventsToExpect;
// ...
}
public class ReportCompletionEvent extends ReportSchedulingEvent {
// ...
}
public class YourListener
implements ApplicationListener<ReportSchedulingEvent> {
private final DatabaseWriter dbWriter;
private volatile int expectedEvents = 0;
public void onApplicationEvent(final ReportSchedulingEvent event) {
if (event instanceof IncomingReportCompletionEvent) {
this.expectedEvents =
((IncomingReportCompletionEvent) event)
.getExpectedEventCount();
} else {
this.expectedEvents--;
if (this.expectedEvents == 0) {
this.dbWriter.doYourThing();
}
}
}
}
In my application I have the following potentially long running classes:
ScheduleLocalNotificationsOperation
UnScheduleLocalNotificationsOperation
SyncEventsToDeviceCalendarOperation
UnSyncEventsToDeviceCalendarOperation
SaveProductsToLocalStorageImpl
and so on.
Most of these are structured this way:
public interface Operation {
void execute();
}
public class ScheduleLocalNotificationsOperation implements Operation {
private MyApplication application;
private List<LocalNotificationDescriptor> localNotifications;
public ScheduleLocalNotificationsOperation (MyApplication application, List<LocalNotificationDescriptor> localNotifications) {
this.application = application;
this.localNotifications = localNotifications;
}
#Override
public void execute() {
// Do some stuff
}
}
Ideally, I would like to design my Operation classes to be concurrency agnostic, and to be able to impose some multithreading policy on them from the outside, like so:
public class MainActivity extends Activity {
public static ExecutorService executorService = Executors.newCachedThreadPool();
#Override
public void onCreate() {
executorService.submit(new Runnable {
#Override
public void run() {
new ScheduleLocalNotificationsOperation(application, createLocalNotificationsList()).execute();
}
});
}
}
But this way I should implement some concurrency policy in every client, every time I call those classes.
On the other hand, I dont want to interrupt the pure logic in those operations by adding concurrency to them, so my question is basically, from design stand point what is the better way to do this, if there is a clearly defined one?
Can I create an OperationsRunner class that will be called by any client and will execute any Operation using polymorphism and make that class be the only one that deals with concurrency?
Let's say the class MyCoolProcess has the logic of my app which is needed to be called in it's own thread. We'll create a thread, call it and continue with the application.
This class is a EJB; annotated with #Stateless
Now we have the MyController class; which is going to call a new thread.
Code:
public class MyController {
#EJB
MyCoolProcess p;
public Response foo() {
Thread t = new Thread() {
public void run() {
p.run();
}
};
t.start();
// continues ...
}
}
#Stateless
public class MyCoolProcess {
public void run() {
// heavy task
}
}
That is working fine; the point is... before that solution I've tried with the Runnable interface. Which was I wanted at first time. The approach would be:
public class MyController {
#EJB
MyCoolProcess p;
public Response foo() {
Thread t = new Thread(p);
t.start();
// continues ...
}
}
#Stateless
public class MyCoolProcess implements Runnable {
#Override
public void run() {
// heavy task
}
}
That doesn't work. Actually, the server cannot start. Crashes trying to inject the dependencies. I'm not be able to implement the interface Runnable if I'm a EJB isn't it? WHY
And... is there any way to do the Runnable way instead the anonymous class?
From the EJB spec:
The enterprise bean must not attempt to manage threads. The enterprise bean must not attempt to start, stop, suspend, or resume a thread, or to change a thread’s priority or name. The enterprise bean must not attempt to manage thread groups.
See Adam's Blog.
I try to get an async process running.
Based on this example: http://tomee.apache.org/examples-trunk/async-methods/README.html
But the method addWorkflow(Workflow workflow) will only return when the code in run(Workflow workflow) is fully completed.
Then when it returns and result.get(); is called I'll get the exception:
Caused by: java.lang.IllegalStateException: Object does not represent an acutal Future
Any suggestion what I'm missing?
#Singleton
public class WorkflowProcessor {
#EJB
private WorkflowManager workflowManager;
private final static Logger log = Logger.getLogger(WorkflowProcessor.class.getName());
public void runWorkflows(Collection<Workflow> workflows) throws Exception{
final long start = System.nanoTime();
final long numberOfWorkflows = workflows.size();
Collection<Future<Workflow>> asyncWorkflows = new ArrayList<>();
for(Workflow workflow : workflows){
Future<Workflow> w = addWorkflow(workflow);
asyncWorkflows.add(w);
}
log.log(Level.INFO, "workflow jobs added {0}", new Object[]{numberOfWorkflows});
for(Future<Workflow> result : asyncWorkflows){
result.get();
}
final long total = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
log.log(Level.INFO, "WorkflowProcessor->runWorkflows {0} workflows completed in:{1}", new Object[]{numberOfWorkflows, total});
}
#Asynchronous
#Lock(LockType.READ)
#AccessTimeout(-1)
private Future<Workflow> addWorkflow(Workflow workflow){
run(workflow);
return new AsyncResult<Workflow>(workflow);
}
private void run(Workflow workflow){
this.workflowManager.runWorkflow(workflow);
}
So the normal way would be to have the #Asynchronous method in another bean from the caller method.
#Stateless
public class ComputationProcessor {
#Asynchronous
public Future<Data> performComputation {
return new AsyncResult<Data>(null);
}
}
#Stateless
public class ComputationService {
#Inject
private ComputationProcessor mProcessor;
public void ...() {
Future<Data> result = mProcessor.performComputation();
...
}
}
As you discovered, it won't work if the #Asynchronous method is in the same bean than the caller.
The issue is that Java can't decorate the implicit this pointer.
In other words, the #Asynchronous annotation won't be processed and you're doing an ordinary method call.
You can inject your so singleton with a reference to itself (call this e.g. "self"), then call self.addWorkflow.
You might also want to consider running your async code in a stateless bean. You are using a read lock for addWorkflow, but runWorkflow still has a write lock. I think you have a dead lock now: you're holding the lock until work is done, but no work can be done until the write lock is released.