mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-03 03:38:33 +08:00
Refs #722 BackgroundTasks: Web implementation
This commit is contained in:
parent
5994e97315
commit
6efab0773e
@ -7,6 +7,7 @@
|
||||
package com.haulmont.cuba.desktop.gui.utils;
|
||||
|
||||
import com.haulmont.cuba.core.global.TimeProvider;
|
||||
import com.haulmont.cuba.gui.components.Window;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundTask;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundTaskHandler;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundWorker;
|
||||
@ -17,7 +18,6 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -27,13 +27,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
*
|
||||
* @author artamonov
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DesktopBackgroundWorker implements BackgroundWorker {
|
||||
|
||||
private WatchDog watchDogThread;
|
||||
private WatchDog watchDog;
|
||||
|
||||
public DesktopBackgroundWorker() {
|
||||
watchDogThread = new DesktopWatchDog();
|
||||
watchDog = new DesktopWatchDog();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -42,48 +41,58 @@ public class DesktopBackgroundWorker implements BackgroundWorker {
|
||||
|
||||
// create task handler
|
||||
TaskExecutor<T> taskExecutor = new DesktopTaskExecutor<T>(task);
|
||||
return new TaskHandler<T>(taskExecutor, watchDogThread);
|
||||
final TaskHandler<T> taskHandler = new TaskHandler<T>(taskExecutor, watchDog);
|
||||
|
||||
task.getOwnerWindow().addListener(new Window.CloseListener() {
|
||||
@Override
|
||||
public void windowClosed(String actionId) {
|
||||
taskHandler.cancel(true);
|
||||
}
|
||||
});
|
||||
|
||||
return taskHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchDog
|
||||
*/
|
||||
private class DesktopWatchDog extends SwingWorker<Void, TaskHandler> implements BackgroundWorker.WatchDog{
|
||||
private class DesktopWatchDog extends SwingWorker<Void, TaskHandler> implements BackgroundWorker.WatchDog {
|
||||
|
||||
private static final int WATCHDOG_INTERVAL = 100;
|
||||
private static final int WATCHDOG_INTERVAL = 2000;
|
||||
|
||||
private boolean watching = false;
|
||||
private ReentrantLock watchLock;
|
||||
private Set<TaskHandler> watches;
|
||||
private final Set<TaskHandler> watches;
|
||||
|
||||
private DesktopWatchDog() {
|
||||
watchLock = new ReentrantLock();
|
||||
watches = new LinkedHashSet<TaskHandler>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
while (watching) {
|
||||
cleanupTasks();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cleanupTasks() throws InterruptedException {
|
||||
TimeUnit.MILLISECONDS.sleep(WATCHDOG_INTERVAL);
|
||||
|
||||
watchLock.lock();
|
||||
synchronized (watches) {
|
||||
long actualTime = TimeProvider.currentTimestamp().getTime();
|
||||
|
||||
long actualTime = TimeProvider.currentTimestamp().getTime();
|
||||
|
||||
List<TaskHandler> forRemove = new LinkedList<TaskHandler>();
|
||||
for (TaskHandler task : watches) {
|
||||
if (task.isCancelled() || task.isDone()) {
|
||||
forRemove.add(task);
|
||||
} else if (task.checkHangup(actualTime)) {
|
||||
cancelTask(task);
|
||||
forRemove.add(task);
|
||||
List<TaskHandler> forRemove = new LinkedList<TaskHandler>();
|
||||
for (TaskHandler task : watches) {
|
||||
if (task.isCancelled() || task.isDone()) {
|
||||
forRemove.add(task);
|
||||
} else if (task.checkHangup(actualTime)) {
|
||||
cancelTask(task);
|
||||
forRemove.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
watches.removeAll(forRemove);
|
||||
}
|
||||
|
||||
watches.removeAll(forRemove);
|
||||
|
||||
watchLock.unlock();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cancelTask(TaskHandler task) {
|
||||
@ -94,7 +103,7 @@ public class DesktopBackgroundWorker implements BackgroundWorker {
|
||||
protected void process(List<TaskHandler> chunks) {
|
||||
for (TaskHandler task : chunks) {
|
||||
if (task.isHangup())
|
||||
task.cancel(true);
|
||||
task.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,15 +113,12 @@ public class DesktopBackgroundWorker implements BackgroundWorker {
|
||||
}
|
||||
|
||||
public void manageTask(TaskHandler backroundTask) {
|
||||
watchLock.lock();
|
||||
|
||||
watches.add(backroundTask);
|
||||
|
||||
if (!watching) {
|
||||
startWatching();
|
||||
synchronized (watches) {
|
||||
watches.add(backroundTask);
|
||||
}
|
||||
|
||||
watchLock.unlock();
|
||||
if (!watching)
|
||||
startWatching();
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +131,7 @@ public class DesktopBackgroundWorker implements BackgroundWorker {
|
||||
|
||||
private DesktopTaskExecutor(BackgroundTask<T> runnableTask) {
|
||||
this.runnableTask = runnableTask;
|
||||
runnableTask.setProgressHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,11 +147,19 @@ public class DesktopBackgroundWorker implements BackgroundWorker {
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
runnableTask.done();
|
||||
if (!runnableTask.isInterrupted())
|
||||
runnableTask.done();
|
||||
}
|
||||
|
||||
public void execute(long timeout, TimeUnit unit) {
|
||||
execute();
|
||||
@Override
|
||||
public boolean cancelExecution(boolean mayInterruptIfRunning) {
|
||||
runnableTask.setInterrupted(true);
|
||||
|
||||
if (!isDone()) {
|
||||
cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -6,9 +6,9 @@
|
||||
|
||||
package com.haulmont.cuba.desktop.sys;
|
||||
|
||||
import com.haulmont.cuba.core.sys.AppContext;
|
||||
import com.haulmont.cuba.desktop.gui.DesktopComponentsFactory;
|
||||
import com.haulmont.cuba.desktop.gui.components.DesktopExportDisplay;
|
||||
import com.haulmont.cuba.desktop.gui.utils.DesktopBackgroundWorker;
|
||||
import com.haulmont.cuba.gui.AppConfig;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundWorker;
|
||||
import com.haulmont.cuba.gui.export.ExportDisplay;
|
||||
@ -22,7 +22,6 @@ import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
|
||||
public class DesktopAppConfig extends AppConfig {
|
||||
|
||||
private volatile ComponentsFactory componentsFactory;
|
||||
private volatile BackgroundWorker backgroundWorker;
|
||||
|
||||
@Override
|
||||
protected ExportDisplay __createExportDisplay() {
|
||||
@ -31,12 +30,7 @@ public class DesktopAppConfig extends AppConfig {
|
||||
|
||||
@Override
|
||||
protected BackgroundWorker __getBackgroundWorker() {
|
||||
if (backgroundWorker == null) {
|
||||
synchronized (this) {
|
||||
backgroundWorker = new DesktopBackgroundWorker();
|
||||
}
|
||||
}
|
||||
return backgroundWorker;
|
||||
return AppContext.getBean(BackgroundWorker.NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
<bean id="cuba_FileUploading" class="com.haulmont.cuba.gui.upload.FileUploading"/>
|
||||
|
||||
<bean id="cuba_BackgroundWorker" class="com.haulmont.cuba.desktop.gui.utils.DesktopBackgroundWorker"/>
|
||||
|
||||
<!-- Remote stubs -->
|
||||
|
||||
<bean id="cuba_clusterInvocationSupport" class="com.haulmont.cuba.core.sys.remoting.ClusterInvocationSupport"
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.backgroundtasks;
|
||||
|
||||
import com.haulmont.cuba.gui.AppConfig;
|
||||
import com.haulmont.cuba.gui.components.*;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundTask;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundTaskHandler;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundWorker;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* BackgroundWorker Test
|
||||
* <p>$Id$</p>
|
||||
*
|
||||
* @author artamonov
|
||||
*/
|
||||
public class BackgroundTasksWindow extends AbstractWindow {
|
||||
|
||||
protected BackgroundWorker backgroundWorker;
|
||||
protected BackgroundTaskHandler taskHandler;
|
||||
|
||||
protected Button startBtn;
|
||||
protected Button stopBtn;
|
||||
protected Label statusLabel;
|
||||
|
||||
public BackgroundTasksWindow(IFrame frame) {
|
||||
super(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(Map<String, Object> params) {
|
||||
super.init(params);
|
||||
|
||||
backgroundWorker = AppConfig.getBackgroundWorker();
|
||||
|
||||
final BackgroundTask<Integer> progressIndicator = new BackgroundTask<Integer>(this) {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
publish((Integer) i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void canceled() {
|
||||
statusLabel.setValue("Canceled");
|
||||
|
||||
startBtn.setEnabled(true);
|
||||
stopBtn.setEnabled(false);
|
||||
|
||||
taskHandler = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
statusLabel.setValue("Done");
|
||||
|
||||
startBtn.setEnabled(true);
|
||||
stopBtn.setEnabled(false);
|
||||
|
||||
taskHandler = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(List<Integer> changes) {
|
||||
int size = changes.size();
|
||||
if (size >= 0)
|
||||
statusLabel.setValue(String.valueOf(changes.get(size - 1)));
|
||||
}
|
||||
};
|
||||
|
||||
startBtn = getComponent("startTaskBtn");
|
||||
stopBtn = getComponent("stopTaskBtn");
|
||||
statusLabel = getComponent("statusLbl");
|
||||
|
||||
startBtn.setAction(new AbstractAction("Start") {
|
||||
@Override
|
||||
public void actionPerform(Component component) {
|
||||
if (taskHandler == null) {
|
||||
statusLabel.setValue("Started");
|
||||
taskHandler = backgroundWorker.handle(progressIndicator);
|
||||
|
||||
startBtn.setEnabled(false);
|
||||
stopBtn.setEnabled(true);
|
||||
|
||||
taskHandler.execute(5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
stopBtn.setAction(new AbstractAction("Stop") {
|
||||
@Override
|
||||
public void actionPerform(Component component) {
|
||||
if (taskHandler != null) {
|
||||
taskHandler.cancel(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<window
|
||||
xmlns="http://www.haulmont.com/schema/cuba/gui/window.xsd"
|
||||
class="com.haulmont.cuba.gui.backgroundtasks.BackgroundTasksWindow"
|
||||
caption="BackgroundTasks">
|
||||
|
||||
<dsContext>
|
||||
|
||||
</dsContext>
|
||||
|
||||
<layout spacing="true">
|
||||
|
||||
<groupBox expandable="false">
|
||||
<caption label="Test background tasks"/>
|
||||
<hbox spacing="true" margin="true">
|
||||
<label value="Status:"/>
|
||||
<label id="statusLbl" value="Stopped"/>
|
||||
</hbox>
|
||||
<hbox spacing="true">
|
||||
<button id="startTaskBtn" caption="Start"/>
|
||||
<button id="stopTaskBtn" caption="Stop"/>
|
||||
</hbox>
|
||||
</groupBox>
|
||||
|
||||
</layout>
|
||||
</window>
|
@ -12,6 +12,10 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Backround task for execute in {@link BackgroundWorker}
|
||||
* <p>
|
||||
* <b>It is strongly recommended to be able to interrupt working thread, <br/>
|
||||
* don't ignore {@link InterruptedException} or its ancestors</b>
|
||||
* </p>
|
||||
* <p>$Id$</p>
|
||||
*
|
||||
* @param <T> measure unit which shows progress of task
|
||||
@ -22,6 +26,8 @@ public abstract class BackgroundTask<T> {
|
||||
private ProgressHandler<T> progressHandler;
|
||||
private Window ownerWindow;
|
||||
|
||||
private volatile boolean isInterrupted = false;
|
||||
|
||||
protected BackgroundTask(Window ownerWindow) {
|
||||
this.ownerWindow = ownerWindow;
|
||||
}
|
||||
@ -69,4 +75,12 @@ public abstract class BackgroundTask<T> {
|
||||
public Window getOwnerWindow() {
|
||||
return ownerWindow;
|
||||
}
|
||||
|
||||
public boolean isInterrupted() {
|
||||
return isInterrupted;
|
||||
}
|
||||
|
||||
public void setInterrupted(boolean interrupted) {
|
||||
isInterrupted = interrupted;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
* @author artamonov
|
||||
*/
|
||||
public interface BackgroundWorker {
|
||||
String NAME = "cuba_BackgroundWorker";
|
||||
|
||||
/**
|
||||
* Create handler for background task
|
||||
@ -39,7 +40,7 @@ public interface BackgroundWorker {
|
||||
|
||||
void execute();
|
||||
|
||||
boolean cancel(boolean mayInterruptIfRunning);
|
||||
boolean cancelExecution(boolean mayInterruptIfRunning);
|
||||
|
||||
BackgroundTask<T> getTask();
|
||||
|
||||
@ -97,13 +98,20 @@ public interface BackgroundWorker {
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
boolean canceled = taskExecutor.cancel(mayInterruptIfRunning);
|
||||
boolean canceled = taskExecutor.cancelExecution(mayInterruptIfRunning);
|
||||
if (canceled) {
|
||||
taskExecutor.getTask().canceled();
|
||||
}
|
||||
return canceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel without events for tasks
|
||||
*/
|
||||
public void close() {
|
||||
taskExecutor.cancelExecution(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return taskExecutor.isDone();
|
||||
|
@ -84,7 +84,4 @@
|
||||
<screen id="core$EntitySnapshot.view"
|
||||
template="/com/haulmont/cuba/gui/app/core/entitydiff/diff-view.xml"/>
|
||||
|
||||
<screen id="test$BackroundTasks.window"
|
||||
template="/com/haulmont/cuba/gui/backgroundtasks/background-task-window.xml"/>
|
||||
|
||||
</screen-config>
|
||||
|
@ -101,6 +101,8 @@ public abstract class App extends Application
|
||||
|
||||
private AppCookies cookies;
|
||||
|
||||
private BackgroundTaskManager backgroundTaskManager;
|
||||
|
||||
protected boolean testModeRequest = false;
|
||||
|
||||
protected String clientAddress;
|
||||
@ -124,6 +126,7 @@ public abstract class App extends Application
|
||||
};
|
||||
cookies.setCookiesEnabled(true);
|
||||
timers = new AppTimers(this);
|
||||
backgroundTaskManager = new BackgroundTaskManager();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
@ -457,6 +460,22 @@ public abstract class App extends Application
|
||||
}
|
||||
}
|
||||
|
||||
public BackgroundTaskManager getTaskManager() {
|
||||
return backgroundTaskManager;
|
||||
}
|
||||
|
||||
public void addBackgroundTask(Thread task) {
|
||||
backgroundTaskManager.addTask(task);
|
||||
}
|
||||
|
||||
public void removeBackgroundTask(Thread task) {
|
||||
backgroundTaskManager.removeTask(task);
|
||||
}
|
||||
|
||||
public void cleanupBackgroundTasks() {
|
||||
backgroundTaskManager.cleanupTasks();
|
||||
}
|
||||
|
||||
Window getCurrentWindow() {
|
||||
String name = currentWindowName.get();
|
||||
return (name == null ? getMainWindow() : getWindow(name));
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>$Id$</p>
|
||||
*
|
||||
* @author artamonov
|
||||
*/
|
||||
public class BackgroundTaskManager {
|
||||
|
||||
private transient Set<Thread> taskSet;
|
||||
|
||||
public BackgroundTaskManager() {
|
||||
taskSet = Collections.synchronizedSet(new LinkedHashSet<Thread>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add task to task set
|
||||
* @param task Task
|
||||
*/
|
||||
public void addTask(Thread task) {
|
||||
taskSet.add(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop manage of stopped task
|
||||
* @param task Task
|
||||
*/
|
||||
public void removeTask(Thread task) {
|
||||
taskSet.remove(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt all tasks
|
||||
*/
|
||||
public void cleanupTasks() {
|
||||
// Stop threads
|
||||
for (Thread taskThread : taskSet) {
|
||||
if (taskThread.isAlive())
|
||||
taskThread.interrupt();
|
||||
}
|
||||
// Clean task set
|
||||
taskSet.clear();
|
||||
}
|
||||
}
|
@ -6,13 +6,13 @@
|
||||
|
||||
package com.haulmont.cuba.web;
|
||||
|
||||
import com.haulmont.cuba.core.sys.AppContext;
|
||||
import com.haulmont.cuba.gui.AppConfig;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundWorker;
|
||||
import com.haulmont.cuba.gui.export.ExportDisplay;
|
||||
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
|
||||
import com.haulmont.cuba.web.filestorage.WebExportDisplay;
|
||||
import com.haulmont.cuba.web.gui.WebComponentsFactory;
|
||||
import com.haulmont.cuba.web.gui.utils.WebBackgroundWorker;
|
||||
|
||||
/**
|
||||
* <p>$Id$</p>
|
||||
@ -22,7 +22,6 @@ import com.haulmont.cuba.web.gui.utils.WebBackgroundWorker;
|
||||
public class WebAppConfig extends AppConfig {
|
||||
|
||||
private volatile ComponentsFactory componentsFactory;
|
||||
private volatile BackgroundWorker backgroundWorker;
|
||||
|
||||
@Override
|
||||
protected ExportDisplay __createExportDisplay() {
|
||||
@ -31,12 +30,7 @@ public class WebAppConfig extends AppConfig {
|
||||
|
||||
@Override
|
||||
protected BackgroundWorker __getBackgroundWorker() {
|
||||
if (backgroundWorker == null) {
|
||||
synchronized (this) {
|
||||
backgroundWorker = new WebBackgroundWorker();
|
||||
}
|
||||
}
|
||||
return backgroundWorker;
|
||||
return AppContext.getBean(BackgroundWorker.NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.gui.utils;
|
||||
|
||||
import com.haulmont.cuba.web.App;
|
||||
import com.vaadin.Application;
|
||||
import com.vaadin.terminal.gwt.server.WebApplicationContext;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* <p>$Id$</p>
|
||||
*
|
||||
* @author artamonov
|
||||
*/
|
||||
public class BackgroundWorkerListener implements HttpSessionListener {
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
|
||||
|
||||
HttpSession session = httpSessionEvent.getSession();
|
||||
|
||||
WebApplicationContext applicationContext = WebApplicationContext.getApplicationContext(session);
|
||||
|
||||
final Collection<Application> applications = applicationContext.getApplications();
|
||||
|
||||
// Cleanup tasks in applications
|
||||
for (Application app : applications) {
|
||||
// Purge threads
|
||||
((App)app).cleanupBackgroundTasks();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,21 +7,16 @@
|
||||
package com.haulmont.cuba.web.gui.utils;
|
||||
|
||||
import com.haulmont.cuba.core.global.TimeProvider;
|
||||
import com.haulmont.cuba.gui.components.IFrame;
|
||||
import com.haulmont.cuba.gui.components.Timer;
|
||||
import com.haulmont.cuba.gui.components.Window;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundTask;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundTaskHandler;
|
||||
import com.haulmont.cuba.gui.executors.BackgroundWorker;
|
||||
import com.haulmont.cuba.web.App;
|
||||
import com.haulmont.cuba.web.gui.WebTimer;
|
||||
import com.haulmont.cuba.web.gui.WebWindow;
|
||||
import com.vaadin.ui.Component;
|
||||
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -31,42 +26,53 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
*
|
||||
* @author artamonov
|
||||
*/
|
||||
public class WebBackgroundWorker implements BackgroundWorker, HttpSessionListener {
|
||||
public class WebBackgroundWorker implements BackgroundWorker {
|
||||
|
||||
private static final int UI_TIMER_UPDATE_MS = 500;
|
||||
|
||||
private WatchDog watchDogThread;
|
||||
private WatchDog watchDog;
|
||||
|
||||
public WebBackgroundWorker() {
|
||||
watchDogThread = new WebWatchDog();
|
||||
watchDog = new WebWatchDog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> BackgroundTaskHandler handle(final BackgroundTask<T> task) {
|
||||
checkNotNull(task);
|
||||
|
||||
App appInstance = App.getInstance();
|
||||
|
||||
// UI timer
|
||||
WebTimer pingTimer = new WebTimer(UI_TIMER_UPDATE_MS, true);
|
||||
|
||||
// create task executor
|
||||
final WebTaskExecutor<T> taskExecutor = new WebTaskExecutor<T>(appInstance, task, pingTimer);
|
||||
|
||||
// add thread to taskSet
|
||||
appInstance.addBackgroundTask(taskExecutor);
|
||||
|
||||
// create task handler
|
||||
TaskExecutor<T> taskExecutor = new WebTaskExecutor<T>(task, pingTimer);
|
||||
final TaskHandler<T> taskHandler = new TaskHandler<T>(taskExecutor, watchDogThread);
|
||||
final TaskHandler<T> taskHandler = new TaskHandler<T>(taskExecutor, watchDog);
|
||||
|
||||
// add timer to AppWindow for UI ping
|
||||
pingTimer.addTimerListener(new com.haulmont.cuba.gui.components.Timer.TimerListener() {
|
||||
|
||||
private long intentVersion = 0;
|
||||
|
||||
@Override
|
||||
public void onTimer(Timer timer) {
|
||||
// handle intents
|
||||
if (!taskHandler.isCancelled()) {
|
||||
if (intentVersion != taskExecutor.getIntentVersion()) {
|
||||
intentVersion = taskExecutor.getIntentVersion();
|
||||
taskExecutor.handleIntents();
|
||||
}
|
||||
}
|
||||
|
||||
// if completed
|
||||
if (taskHandler.isDone()) {
|
||||
task.done();
|
||||
timer.stopTimer();
|
||||
} else {
|
||||
if (!taskHandler.isCancelled()) {
|
||||
IFrame frame = task.getOwnerWindow().getFrame();
|
||||
Component webWindow = ((WebWindow) frame).getComponent();
|
||||
webWindow.requestRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,34 +81,29 @@ public class WebBackgroundWorker implements BackgroundWorker, HttpSessionListene
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
App.getInstance().addTimer(pingTimer, task.getOwnerWindow());
|
||||
appInstance.addTimer(pingTimer, task.getOwnerWindow());
|
||||
|
||||
task.getOwnerWindow().addListener(new Window.CloseListener() {
|
||||
@Override
|
||||
public void windowClosed(String actionId) {
|
||||
taskHandler.cancel(true);
|
||||
}
|
||||
});
|
||||
|
||||
return taskHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
|
||||
// Purge session tasks and threads
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchDog
|
||||
*/
|
||||
private class WebWatchDog extends Thread implements WatchDog{
|
||||
private class WebWatchDog extends Thread implements WatchDog {
|
||||
|
||||
private static final int WATCHDOG_INTERVAL = 100;
|
||||
private static final int WATCHDOG_INTERVAL = 2000;
|
||||
|
||||
private volatile boolean watching = false;
|
||||
private ReentrantLock watchLock;
|
||||
private Set<TaskHandler> watches;
|
||||
private final Set<TaskHandler> watches;
|
||||
|
||||
private WebWatchDog() {
|
||||
watchLock = new ReentrantLock();
|
||||
watches = new LinkedHashSet<TaskHandler>();
|
||||
}
|
||||
|
||||
@ -113,33 +114,32 @@ public class WebBackgroundWorker implements BackgroundWorker, HttpSessionListene
|
||||
cleanupTasks();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
throw new RuntimeException(ignored);
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupTasks() throws Exception {
|
||||
TimeUnit.MILLISECONDS.sleep(WATCHDOG_INTERVAL);
|
||||
|
||||
watchLock.lock();
|
||||
synchronized (watches) {
|
||||
long actual = TimeProvider.currentTimestamp().getTime();
|
||||
|
||||
long actual = TimeProvider.currentTimestamp().getTime();
|
||||
|
||||
List<TaskHandler> forRemove = new LinkedList<TaskHandler>();
|
||||
for (TaskHandler task : watches) {
|
||||
if (task.isCancelled() || task.isDone()) {
|
||||
forRemove.add(task);
|
||||
} else if (task.checkHangup(actual)) {
|
||||
cancelTask(task);
|
||||
forRemove.add(task);
|
||||
List<TaskHandler> forRemove = new LinkedList<TaskHandler>();
|
||||
for (TaskHandler task : watches) {
|
||||
if (task.isCancelled() || task.isDone()) {
|
||||
forRemove.add(task);
|
||||
} else if (task.checkHangup(actual)) {
|
||||
cancelTask(task);
|
||||
forRemove.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
watches.removeAll(forRemove);
|
||||
}
|
||||
|
||||
watches.removeAll(forRemove);
|
||||
|
||||
watchLock.unlock();
|
||||
}
|
||||
|
||||
private void cancelTask(TaskHandler task) {
|
||||
task.cancel(true);
|
||||
task.close();
|
||||
}
|
||||
|
||||
private void startWatching() {
|
||||
@ -148,15 +148,12 @@ public class WebBackgroundWorker implements BackgroundWorker, HttpSessionListene
|
||||
}
|
||||
|
||||
public void manageTask(TaskHandler backroundTask) {
|
||||
watchLock.lock();
|
||||
|
||||
watches.add(backroundTask);
|
||||
|
||||
if (!watching) {
|
||||
startWatching();
|
||||
synchronized (watches) {
|
||||
watches.add(backroundTask);
|
||||
}
|
||||
|
||||
watchLock.unlock();
|
||||
if (!watching)
|
||||
startWatching();
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,33 +162,60 @@ public class WebBackgroundWorker implements BackgroundWorker, HttpSessionListene
|
||||
*/
|
||||
private class WebTaskExecutor<T> extends Thread implements TaskExecutor<T> {
|
||||
|
||||
private App app;
|
||||
|
||||
private BackgroundTask<T> runnableTask;
|
||||
private WebTimer pingTimer;
|
||||
|
||||
private volatile boolean canceled = false;
|
||||
private volatile boolean done = false;
|
||||
|
||||
private WebTaskExecutor(BackgroundTask<T> runnableTask, WebTimer pingTimer) {
|
||||
private volatile long intentVersion = 0;
|
||||
private final List<T> intents = Collections.synchronizedList(new LinkedList<T>());
|
||||
|
||||
private WebTaskExecutor(App app, BackgroundTask<T> runnableTask, WebTimer pingTimer) {
|
||||
this.runnableTask = runnableTask;
|
||||
this.pingTimer = pingTimer;
|
||||
this.app = app;
|
||||
runnableTask.setProgressHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
runnableTask.setInterrupted(false);
|
||||
runnableTask.run();
|
||||
done = true;
|
||||
// Is done
|
||||
if (!runnableTask.isInterrupted())
|
||||
done = true;
|
||||
// Remove from executions
|
||||
app.removeBackgroundTask(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleProgress(T ... changes) {
|
||||
runnableTask.progress(Arrays.asList(changes));
|
||||
public void handleProgress(T... changes) {
|
||||
synchronized (intents) {
|
||||
intentVersion++;
|
||||
intents.addAll(Arrays.asList(changes));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
public boolean cancelExecution(boolean mayInterruptIfRunning) {
|
||||
boolean canceled = false;
|
||||
|
||||
runnableTask.setInterrupted(true);
|
||||
|
||||
if (super.isAlive() && mayInterruptIfRunning) {
|
||||
// Interrupt
|
||||
interrupt();
|
||||
canceled = isInterrupted();
|
||||
|
||||
// Check
|
||||
canceled = isInterrupted() || isDone();
|
||||
|
||||
// Remove task from execution
|
||||
if (canceled)
|
||||
app.removeBackgroundTask(this);
|
||||
|
||||
this.canceled = canceled;
|
||||
}
|
||||
if ((pingTimer != null) && canceled) {
|
||||
pingTimer.stopTimer();
|
||||
@ -216,5 +240,15 @@ public class WebBackgroundWorker implements BackgroundWorker, HttpSessionListene
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
public long getIntentVersion() {
|
||||
return intentVersion;
|
||||
}
|
||||
|
||||
public void handleIntents() {
|
||||
synchronized (intents) {
|
||||
runnableTask.progress(intents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -35,6 +35,8 @@
|
||||
|
||||
<bean id="cuba_FileUploading" class="com.haulmont.cuba.gui.upload.FileUploading"/>
|
||||
|
||||
<bean id="cuba_BackgroundWorker" class="com.haulmont.cuba.web.gui.utils.WebBackgroundWorker"/>
|
||||
|
||||
<!-- MBeans registration -->
|
||||
|
||||
<bean id="cuba_web_MBeanExporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
|
||||
|
@ -18,6 +18,10 @@
|
||||
<listener-class>com.haulmont.cuba.web.sys.WebAppContextLoader</listener-class>
|
||||
</listener>
|
||||
|
||||
<listener>
|
||||
<listener-class>com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>cuba_servlet</servlet-name>
|
||||
<servlet-class>com.haulmont.cuba.web.sys.CubaApplicationServlet
|
||||
|
Loading…
Reference in New Issue
Block a user