Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Merge pull request #507 from zanata/async-task-manager
Browse files Browse the repository at this point in the history
Hold active tasks in a map, but cache finished tasks briefly
  • Loading branch information
definite committed Jun 24, 2014
2 parents b35bd04 + 928ea7a commit 6e61a9d
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 69 deletions.
Expand Up @@ -70,10 +70,6 @@ public Date getDateFromLong(long value) {
return new Date(value);
}

public void clearAllFinished() {
asyncTaskManagerServiceImpl.clearInactive();
}

public void cancel(AsyncTaskHandle handle) {
handle.cancel();
}
Expand Down
Expand Up @@ -24,6 +24,7 @@

import javax.security.auth.Subject;

import com.google.common.util.concurrent.MoreExecutors;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
Expand Down Expand Up @@ -64,7 +65,8 @@ public class AsynchronousTaskExecutor {
*/
@Asynchronous
public <V, H extends AsyncTaskHandle<V>> void runAsynchronously(
final AsyncTask<V, H> task, final Principal runAsPpal,
final AsyncTask<V, H> task, final Runnable onComplete,
final Principal runAsPpal,
final Subject runAsSubject, final String username) {
AsyncUtils.outject(task.getHandle(), ScopeType.EVENT);

Expand All @@ -75,11 +77,12 @@ public void execute() {
prepareSecurityContext(username);
V returnValue = task.call();
task.getHandle().set(returnValue);
} catch (Exception t) {
} catch (Throwable t) {
task.getHandle().setException(t);
log.error(
"Exception when executing an asynchronous task.", t);
}
onComplete.run();
}

@Override
Expand All @@ -92,7 +95,6 @@ public Subject getSubject() {
return runAsSubject;
}
};

runAsOp.run();
}

Expand Down
5 changes: 3 additions & 2 deletions zanata-war/src/main/java/org/zanata/async/TaskExecutor.java
Expand Up @@ -48,20 +48,21 @@ public class TaskExecutor {
*
* @param task
* The task to execute.
* @param onComplete
* @return The task handle to keep track of the executed task.
* @throws RuntimeException
* If the provided task value is null.
*/
public <V, H extends AsyncTaskHandle<V>> AsyncTaskHandle<V> startTask(
AsyncTask<V, H> task) {
AsyncTask<V, H> task, Runnable onComplete) {
H handle = task.getHandle();
if (handle == null) {
throw new RuntimeException(
"An Asynchronous task should always return a non-null handle");
}

Identity identity = Identity.instance();
asynchronousTaskExecutor.runAsynchronously(task, identity
asynchronousTaskExecutor.runAsynchronously(task, onComplete, identity
.getPrincipal(), identity.getSubject(),
identity.getCredentials().getUsername());
return handle;
Expand Down
Expand Up @@ -83,10 +83,4 @@ <V, H extends AsyncTaskHandle<V>> void startTask(AsyncTask<V, H> task,
*/
Collection<AsyncTaskHandle> getAllHandles();

/**
* Clears all the inactive handles managed by this service. Inactive handles
* all all those handles that reference a task that is either finished, has
* encountered an error, or has been cancelled.
*/
void clearInactive();
}
Expand Up @@ -22,7 +22,9 @@

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
Expand All @@ -36,7 +38,6 @@
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -50,71 +51,115 @@
@Name("asyncTaskManagerServiceImpl")
@Scope(ScopeType.APPLICATION)
@Startup
@Slf4j
public class AsyncTaskManagerServiceImpl implements AsyncTaskManagerService {

// Collection of all managed task Handles. It's self pruned, and it is
// indexed by
// long valued keys
private Cache<Long, AsyncTaskHandle> handlesById = CacheBuilder
.newBuilder().softValues().expireAfterWrite(1, TimeUnit.HOURS)
.build();
// Map of all active task handles
private Map<Serializable, AsyncTaskHandle> handlesByKey = Maps.newConcurrentMap();

private ConcurrentMap<Serializable, AsyncTaskHandle> handlesByKey = Maps
.newConcurrentMap();
// Cache of recently completed tasks
private Cache<Serializable, AsyncTaskHandle> finishedTasks = CacheBuilder
.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES)
.build();

private long lastAssignedKey = 1;

/**
* Starts a task, using a generated task key
* @param task
* The asynchronous task to run.
* @param <V>
* @param <H>
* @return
*/
@Override
public <V, H extends AsyncTaskHandle<V>> String startTask(
AsyncTask<V, H> task) {
TaskExecutor taskExecutor =
(TaskExecutor) Component.getInstance(TaskExecutor.class);
AsyncTaskHandle<V> handle = taskExecutor.startTask(task);
Long taskKey;
taskKey = generateNextAvailableKey();
handlesById.put(taskKey, handle);
Long taskKey = generateNextAvailableKey();
startTask(task, taskKey);
return taskKey.toString();
}

/**
* Starts a task, using the specified task key
* @param task
* The asynchronous task to run.
* @param key
* @param <V>
* @param <H>
*/
@Override
public <V, H extends AsyncTaskHandle<V>> void startTask(
AsyncTask<V, H> task, Serializable key) {
String taskId = startTask(task);
handlesByKey.put(key, getHandle(taskId));
AsyncTask<V, H> task, final Serializable key) {
TaskExecutor taskExecutor =
(TaskExecutor) Component.getInstance(TaskExecutor.class);
AsyncTaskHandle<V> handle = taskExecutor.startTask(task,
new Runnable() {
@Override
public void run() {
taskFinished(key);
}
});
AsyncTaskHandle oldHandle = handlesByKey.put(key, handle);
if (oldHandle != null) {
log.error(
"Key {} has a duplicate: old handle is {}; new handle is {}",
key, oldHandle, handle);
}
}

private void taskFinished(Serializable key) {
AsyncTaskHandle handle = handlesByKey.remove(key);
if (handle != null) {
finishedTasks.put(key, handle);
} else {
log.error("unknown task key: {}", key);
}
}

/**
* Gets the handle for a generated task key
* @param taskId
* The task Id (as returned by
* {@link AsyncTaskManagerService#startTask(org.zanata.async.AsyncTask)}
* )
* @return
*/
@Override
public AsyncTaskHandle getHandle(String taskId) {
try {
Long taskKey = Long.parseLong(taskId);
AsyncTaskHandle handle = handlesById.getIfPresent(taskKey);
return handle;
return getHandleByKey(taskKey);
} catch (NumberFormatException e) {
return null; // Non-number keys are not allowed in this
// implementation
}
}

/**
* Gets the handle for a task which was started with a specified key
* @param key
* The task id as provided to
* {@link AsyncTaskManagerService#startTask(org.zanata.async.AsyncTask, java.io.Serializable)}
* @return
*/
@Override
public AsyncTaskHandle getHandleByKey(Serializable key) {
return handlesByKey.get(key);
}

@Override
public void clearInactive() {
synchronized (handlesById) {
for (Map.Entry<Long, AsyncTaskHandle> entry : handlesById.asMap()
.entrySet()) {
if (entry.getValue().isDone()) {
handlesById.invalidate(entry.getKey());
}
}
// NB: check the active tasks before finished tasks, in
// case the task finishes in between
AsyncTaskHandle handle = handlesByKey.get(key);
if (handle == null) {
handle = finishedTasks.getIfPresent(key);
}
return handle;
}

@Override
public Collection<AsyncTaskHandle> getAllHandles() {
return handlesById.asMap().values();
Collection<AsyncTaskHandle> handles = Lists.newArrayList();
handles.addAll(handlesByKey.values());
handles.addAll(finishedTasks.asMap().values());
return handles;
}

private synchronized long generateNextAvailableKey() {
Expand Down
1 change: 0 additions & 1 deletion zanata-war/src/main/resources/messages.properties
Expand Up @@ -957,7 +957,6 @@ jsf.rolerules.RoleToAssign.tooltip=This is the role that will be automatically a


#------ [home] > Administration > Process Manager ------
jsf.processmanager.ClearAllFinished.label=Clear All Finished
jsf.processmanager.TotalRunning=Total Running
jsf.processmanager.TotalFinished=Total Finished
! type of process that is currently running
Expand Down
2 changes: 0 additions & 2 deletions zanata-war/src/main/resources/messages_en_GB.properties
Expand Up @@ -816,8 +816,6 @@ jsf.rolerules.RoleToAssign=Role to Assign
# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov
jsf.rolerules.RoleToAssign.tooltip=This is the role that will be automatically assigned to the user upon login, only if the rule conditions are met.
# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov
jsf.processmanager.ClearAllFinished.label=Clear All Finished
# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov
jsf.processmanager.TotalRunning=Total Running
# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov
jsf.processmanager.TotalFinished=Total Finished
Expand Down
1 change: 0 additions & 1 deletion zanata-war/src/main/resources/messages_ja.properties
Expand Up @@ -467,7 +467,6 @@ jsf.rolerules.IdentityPattern=ID \u306E\u30D1\u30BF\u30FC\u30F3
jsf.rolerules.IdentityPattern.tooltip=\u3053\u306E\u30EB\u30FC\u30EB\u3092\u30E6\u30FC\u30B6\u30FC ID \u306B\u9069\u7528\u3059\u308B\u304B\u3069\u3046\u304B\u3092\u6307\u5B9A\u3059\u308B\u6B63\u898F\u8868\u73FE\u3067\u3059\u3002 \u30E6\u30FC\u30B6\u30FC ID \u306F\u8A8D\u8A3C\u30E1\u30AB\u30CB\u30BA\u30E0\u306B\u3088\u3063\u3066\u7570\u306A\u308B\u70B9\u306B\u6CE8\u610F\u3057\u3066\u304F\u3060\u3055\u3044\u3002 \u3053\u306E\u5024\u3092\u7A7A\u767D\u306E\u307E\u307E\u306B\u3059\u308B\u3068\u3001 \u30EB\u30FC\u30EB\u304C\u3059\u3079\u3066\u306E\u30E6\u30FC\u30B6\u30FC ID\u306B\u9069\u7528\u3055\u308C\u308B\u3053\u3068\u306B\u306A\u308A\u307E\u3059\u3002
jsf.rolerules.RoleToAssign=\u5272\u308A\u5F53\u3066\u308B\u30ED\u30FC\u30EB
jsf.rolerules.RoleToAssign.tooltip=\u30EB\u30FC\u30EB\u306E\u6761\u4EF6\u3092\u6E80\u305F\u3057\u305F\u5834\u5408\u306B\u306E\u307F\u3001 \u30E6\u30FC\u30B6\u30FC\u306E\u30ED\u30B0\u30A4\u30F3\u6642\u306B\u81EA\u52D5\u7684\u306B\u5272\u308A\u5F53\u3066\u3089\u308C\u308B\u30ED\u30FC\u30EB\u3067\u3059\u3002
jsf.processmanager.ClearAllFinished.label=\u7D42\u4E86\u6E08\u307F\u3092\u3059\u3079\u3066\u30AF\u30EA\u30A2
jsf.processmanager.TotalRunning=\u5B9F\u884C\u4E2D\u30D7\u30ED\u30BB\u30B9\u306E\u5408\u8A08
jsf.processmanager.TotalFinished=\u7D42\u4E86\u6E08\u307F\u30D7\u30ED\u30BB\u30B9\u306E\u5408\u8A08
# translation auto-copied from project oVirt, version rhevm-3.2, document frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/org.ovirt.engine.ui.webadmin.ApplicationConstants, author nnakakit
Expand Down
1 change: 0 additions & 1 deletion zanata-war/src/main/resources/messages_uk.properties
Expand Up @@ -639,7 +639,6 @@ jsf.rolerules.IdentityPattern=\u0417\u0440\u0430\u0437\u043E\u043A \u041F\u0441\
jsf.rolerules.IdentityPattern.tooltip=\u041F\u043E\u0441\u0442\u0456\u0439\u043D\u0438\u0439 \u0412\u0438\u0440\u0430\u0437 \u0434\u043B\u044F \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F, \u0447\u0438 \u043C\u043E\u0436\u0435 \u0446\u0435 \u043F\u0440\u0430\u0432\u043B\u043E \u0431\u0443\u0442\u0438 \u0437\u0430\u0441\u0442\u043E\u0441\u043E\u0432\u0430\u043D\u0435 \u0434\u043E \u0434\u0430\u043D\u043E\u0433\u043E \u041F\u0441\u0435\u0432\u0434\u043E. \u0417\u0432\u0435\u0440\u043D\u0456\u0442\u044C \u0443\u0432\u0430\u0433\u0443\: \u041F\u0441\u0435\u0432\u0434\u043E \u043C\u043E\u0436\u0435 \u0431\u0443\u0442\u0438 \u0440\u0456\u0437\u043D\u0438\u043C, \u0432 \u0437\u0430\u043B\u0435\u0436\u043D\u043E\u0441\u0442\u0456 \u0432\u0456\u0434 \u043C\u0435\u0445\u0430\u043D\u0456\u0437\u043C\u0443 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0456\u0457. \u042F\u043A\u0449\u043E \u0446\u0435 \u043F\u043E\u043B\u0435 \u043F\u043E\u0440\u043E\u0436\u043D\u0454 - \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u0437\u0430\u0441\u0442\u043E\u0441\u043E\u0432\u0443\u0432\u0430\u0442\u0438\u043C\u0435\u0442\u044C\u0441\u044F \u0434\u043E \u0412\u0421\u0406\u0425 \u041F\u0441\u0435\u0432\u0434\u043E.
jsf.rolerules.RoleToAssign=\u041D\u0430\u0434\u0430\u0442\u0438 \u0420\u0456\u0432\u0435\u043D\u044C
jsf.rolerules.RoleToAssign.tooltip=\u0426\u0435\u0439 \u0440\u0456\u0432\u0435\u043D\u044C \u043D\u0430\u0434\u0430\u0432\u0430\u0442\u0438\u043C\u0435\u0442\u044C\u0441\u044F \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0435\u0432\u0456 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u043E \u043F\u0440\u0438 \u0432\u0438\u043A\u043E\u043D\u0430\u043D\u043D\u0456 \u043F\u0440\u0430\u0432\u0438\u043B\u0430.
jsf.processmanager.ClearAllFinished.label=\u0412\u0438\u043B\u0443\u0447\u0438\u0442\u0438 \u0432\u0441\u0456 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0456
jsf.processmanager.TotalRunning=\u0417\u0430\u0433\u0430\u043B\u043E\u043C \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E
jsf.processmanager.TotalFinished=\u0417\u0430\u0433\u0430\u043B\u043E\u043C \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E
# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks
Expand Down
Expand Up @@ -456,7 +456,6 @@ jsf.rolerules.IdentityPattern=\u8EAB\u4EFD\u6A21\u5F0F
jsf.rolerules.IdentityPattern.tooltip=\u7528\u4F86\u5224\u5B9A\u6B64\u898F\u5247\u662F\u5426\u9069\u7528\u65BC\u7279\u5B9A\u4F7F\u7528\u8005 ID \u7684\u5E38\u898F\u8868\u793A\u5F0F\u3002\u8ACB\u6CE8\u610F\uFF0C\u4F7F\u7528\u8005 ID \u6703\u6839\u64DA\u8A8D\u8B49\u6A5F\u5236\u800C\u6709\u6240\u4E0D\u540C\u3002\u82E5\u9019\u500B\u503C\u88AB\u4FDD\u7559\u70BA\u7A7A\u767D\uFF0C\u898F\u5247\u5C07\u6703\u5957\u7528\u81F3\u300C\u5168\u90E8\u300D\u7684\u4F7F\u7528\u8005 ID\u3002
jsf.rolerules.RoleToAssign=\u6B32\u5206\u914D\u7684\u89D2\u8272
jsf.rolerules.RoleToAssign.tooltip=\u6B64\u4E43\u5728\u4F7F\u7528\u8005\u767B\u5165\u6642\uFF0C\u6703\u88AB\u81EA\u52D5\u5206\u914D\u7D66\u4F7F\u7528\u8005\u7684\u89D2\u8272\uFF0C\u524D\u63D0\u662F\u5FC5\u9808\u6EFF\u8DB3\u898F\u5247\u7684\u689D\u4EF6\u3002
jsf.processmanager.ClearAllFinished.label=\u6E05\u9664\u6240\u6709\u5DF2\u5B8C\u6210\u7684\u9805\u76EE
jsf.processmanager.TotalRunning=\u7E3D\u57F7\u884C\u6578\u91CF
jsf.processmanager.TotalFinished=\u7E3D\u5B8C\u6210\u6578\u91CF
# translation auto-copied from project Subscription Manager, version 1.8.X, document keys, author snowlet
Expand Down
4 changes: 0 additions & 4 deletions zanata-war/src/main/webapp/WEB-INF/pages.xml
Expand Up @@ -377,10 +377,6 @@
execute="#{breadcrumbs.addLocation('/admin/home.xhtml', messages['jsf.Administration'])}" />
<action
execute="#{breadcrumbs.addLocation('/admin/processmanager.xhtml', messages['jsf.ProcessManager'])}" />

<navigation from-action="#{processManagerAction.clearAllFinished}">
<redirect view-id="/admin/processmanager.xhtml" />
</navigation>
</page>

<!-- Version Group -->
Expand Down
6 changes: 0 additions & 6 deletions zanata-war/src/main/webapp/admin/processmanager.xhtml
Expand Up @@ -11,12 +11,6 @@

<ui:define name="page_title">#{messages['jsf.ProcessManager']}</ui:define>
<ui:define name="right_content">
<rich:panel>
<f:facet name="header">#{messages['jsf.Actions']}</f:facet>
<s:link styleClass="action_link"
action="#{processManagerAction.clearAllFinished}"
value="#{messages['jsf.processmanager.ClearAllFinished.label']}"/>
</rich:panel>
<rich:panel id="statsPanel">
<f:facet name="header">#{messages['jsf.Statistics']}</f:facet>

Expand Down

0 comments on commit 6e61a9d

Please sign in to comment.