From 73e7e2c6768e11c84ccb2053da4376c4b411655c Mon Sep 17 00:00:00 2001 From: "Carlos A. Munoz" Date: Fri, 17 Oct 2014 14:30:01 +1000 Subject: [PATCH] Async task framework refactoring Pull Request: https://github.com/zanata/zanata-server/pull/606 This is the new async framework which lays the ground for the CDI version. Squashed commit of the following: commit 9fcd562b329130432949a6fd7d72252ec6acf147 Author: Carlos A. Munoz Date: Thu Oct 16 16:29:29 2014 +1000 Fix transaction missing error when pushing. commit de8c56e789862af9afa2dc11f782087fd6067ca8 Author: Carlos A. Munoz Date: Tue Oct 14 16:54:07 2014 +1000 Fix code styling issues. Also add some tests. commit 57bab714e64449b47d37d4017d6ea34a5e26bb3d Author: Carlos A. Munoz Date: Tue Oct 14 09:36:16 2014 +1000 Revert arquillian.xml changes. commit 664fecfe721ffec657df8de45a13385bd0db913c Author: Carlos A. Munoz Date: Mon Oct 13 16:47:54 2014 +1000 Replace async package with async2. Removed the old package and renamed async2 to the old name. commit ae1da24cb3f332af3d99cbd71cb6cda815e0afde Author: Carlos A. Munoz Date: Mon Oct 13 16:04:19 2014 +1000 Move source and translated doc push to new async framework. commit 891ae93b40ef47f9f2d9ac5b94ceb7c29719f29b Author: Carlos A. Munoz Date: Mon Oct 13 13:56:25 2014 +1000 Migrate Zip File building to the new async framework. commit 2256c1abfb8b585d7062420c29376c96b62f0840 Author: Carlos A. Munoz Date: Thu Oct 9 16:06:50 2014 +1000 Migrate Copy Version to the new async framework. commit 6772a89a0032faf9ca0c1f61c5a0b1eec38ad7f6 Author: Carlos A. Munoz Date: Thu Oct 9 13:41:46 2014 +1000 Migrate Copy trans to the new async framework. commit f7123063ab1d00c694cc21c629c5034e896ecc25 Author: Carlos A. Munoz Date: Tue Oct 7 16:17:30 2014 +1000 Migrate Reindexing to the new Async service. commit 112bcc1b5a0910c48e21dcea2cb5d5f1f5933ae4 Author: Carlos A. Munoz Date: Tue Oct 7 16:17:09 2014 +1000 New Asynchronous framework. Behaves similarly to what a CDI implementation would. --- .../org/zanata/action/CopyTransAction.java | 10 +- .../org/zanata/action/CopyTransManager.java | 48 +++-- .../org/zanata/action/CopyVersionManager.java | 37 ++-- .../zanata/action/ProcessManagerAction.java | 12 +- .../org/zanata/action/ProjectHomeAction.java | 9 +- .../action/ProjectIterationZipFileAction.java | 35 ++-- .../java/org/zanata/action/ReindexAction.java | 7 +- .../action/TranslationMemoryAction.java | 41 ++-- .../org/zanata/action/VersionHomeAction.java | 10 +- .../src/main/java/org/zanata/async/Async.java | 36 ++++ .../zanata/async/AsyncMethodInterceptor.java | 125 ++++++++++++ .../main/java/org/zanata/async/AsyncTask.java | 25 +-- .../org/zanata/async/AsyncTaskHandle.java | 133 +++++++++---- .../zanata/async/AsyncTaskHandleManager.java | 101 ++++++++++ ...askExecutor.java => AsyncTaskManager.java} | 113 +++++++---- .../org/zanata/async/AsyncTaskResult.java | 54 +++++ .../java/org/zanata/async/AsyncUtils.java | 88 --------- .../zanata/async/ContainsAsyncMethods.java | 37 ++++ .../java/org/zanata/async/TaskExecutor.java | 68 ------- .../org/zanata/async/TimedAsyncHandle.java | 114 ----------- .../CopyTransTaskHandle.java} | 33 ++-- .../async/handle/CopyVersionTaskHandle.java | 57 ++++++ .../org/zanata/async/tasks/CopyTransTask.java | 97 --------- .../zanata/async/tasks/CopyVersionTask.java | 100 ---------- .../async/tasks/DocumentCopyTransTask.java | 76 ------- .../async/tasks/IterationCopyTransTask.java | 88 --------- .../AsynchronousProcessResourceService.java | 101 ++++------ .../service/CopyTransResourceService.java | 4 +- .../TranslationMemoryResourceService.java | 21 ++ .../search/AbstractIndexingStrategy.java | 2 +- .../service/AsyncTaskManagerService.java | 86 -------- .../org/zanata/service/CopyTransService.java | 41 +++- .../zanata/service/CopyVersionService.java | 24 ++- .../org/zanata/service/DocumentService.java | 21 ++ .../org/zanata/service/IndexingService.java | 39 ++++ .../zanata/service/SearchIndexManager.java | 167 ++-------------- .../service/TranslationArchiveService.java | 76 +++++++ .../zanata/service/TranslationService.java | 22 ++- .../impl/AsyncTaskManagerServiceImpl.java | 169 ---------------- .../service/impl/CopyTransServiceImpl.java | 102 ++++++++-- .../service/impl/CopyVersionServiceImpl.java | 49 ++++- .../service/impl/DocumentServiceImpl.java | 19 +- .../service/impl/IndexingServiceImpl.java | 175 ++++++++++++++++ .../impl/TranslationArchiveServiceImpl.java} | 187 ++++++++++-------- .../service/impl/TranslationServiceImpl.java | 36 +++- .../server/rpc/DownloadAllFilesHandler.java | 48 +++-- .../GetDownloadAllFilesProgressHandler.java | 8 +- .../layout/version/languages-tab.xhtml | 2 +- .../org/zanata/async/AsyncTaskHandleTest.java | 112 +++++++++++ .../org/zanata/async/AsyncTaskITCase.java | 172 ++++++++-------- .../rest/service/CopyTransRestTest.java | 2 - .../CopyTransServiceImplPerformanceTest.java | 4 +- .../impl/CopyTransServiceImplTest.java | 6 +- .../impl/CopyTransServiceUnitTest.java | 7 +- .../impl/CopyVersionServiceImplTest.java | 11 +- .../service/impl/TranslationFinderTest.java | 2 +- .../TranslationMemoryServiceImplTest.java | 2 +- 57 files changed, 1699 insertions(+), 1572 deletions(-) create mode 100644 zanata-war/src/main/java/org/zanata/async/Async.java create mode 100644 zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java create mode 100644 zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java rename zanata-war/src/main/java/org/zanata/async/{AsynchronousTaskExecutor.java => AsyncTaskManager.java} (55%) create mode 100644 zanata-war/src/main/java/org/zanata/async/AsyncTaskResult.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/AsyncUtils.java create mode 100644 zanata-war/src/main/java/org/zanata/async/ContainsAsyncMethods.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/TaskExecutor.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/TimedAsyncHandle.java rename zanata-war/src/main/java/org/zanata/async/{SimpleAsyncTask.java => handle/CopyTransTaskHandle.java} (67%) create mode 100644 zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/tasks/CopyTransTask.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/tasks/CopyVersionTask.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/tasks/DocumentCopyTransTask.java delete mode 100644 zanata-war/src/main/java/org/zanata/async/tasks/IterationCopyTransTask.java delete mode 100644 zanata-war/src/main/java/org/zanata/service/AsyncTaskManagerService.java create mode 100644 zanata-war/src/main/java/org/zanata/service/IndexingService.java create mode 100644 zanata-war/src/main/java/org/zanata/service/TranslationArchiveService.java delete mode 100644 zanata-war/src/main/java/org/zanata/service/impl/AsyncTaskManagerServiceImpl.java create mode 100644 zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java rename zanata-war/src/main/java/org/zanata/{async/tasks/ZipFileBuildTask.java => service/impl/TranslationArchiveServiceImpl.java} (56%) create mode 100644 zanata-war/src/test/java/org/zanata/async/AsyncTaskHandleTest.java diff --git a/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java b/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java index 4219f4e4d3..2b97959264 100644 --- a/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java +++ b/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java @@ -30,7 +30,7 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.security.Restrict; -import org.zanata.async.tasks.CopyTransTask; +import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.dao.ProjectIterationDAO; import org.zanata.i18n.Messages; import org.zanata.model.HCopyTransOptions; @@ -98,7 +98,7 @@ public void updateCopyTrans(String action, String value) { @Override public String getCompletedPercentage() { - CopyTransTask.CopyTransTaskHandle handle = + CopyTransTaskHandle handle = copyTransManager .getCopyTransProcessHandle(getProjectIteration()); if (handle != null) { @@ -150,7 +150,7 @@ public void cancel() { } public String getCurrentProgress() { - CopyTransTask.CopyTransTaskHandle handle = + CopyTransTaskHandle handle = copyTransManager .getCopyTransProcessHandle(getProjectIteration()); if (handle != null) { @@ -160,7 +160,7 @@ public String getCurrentProgress() { } public String getMaxProgress() { - CopyTransTask.CopyTransTaskHandle handle = + CopyTransTaskHandle handle = copyTransManager .getCopyTransProcessHandle(getProjectIteration()); if (handle != null) { @@ -170,7 +170,7 @@ public String getMaxProgress() { } public String getCopyTransEstimatedTimeLeft() { - CopyTransTask.CopyTransTaskHandle handle = + CopyTransTaskHandle handle = copyTransManager .getCopyTransProcessHandle(getProjectIteration()); if (handle != null) { diff --git a/zanata-war/src/main/java/org/zanata/action/CopyTransManager.java b/zanata-war/src/main/java/org/zanata/action/CopyTransManager.java index 6fa9eebe4d..344d73963f 100644 --- a/zanata-war/src/main/java/org/zanata/action/CopyTransManager.java +++ b/zanata-war/src/main/java/org/zanata/action/CopyTransManager.java @@ -24,26 +24,26 @@ import javax.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import lombok.extern.slf4j.Slf4j; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.security.Identity; -import org.zanata.async.tasks.CopyTransTask; -import org.zanata.async.tasks.DocumentCopyTransTask; -import org.zanata.async.tasks.IterationCopyTransTask; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskHandleManager; +import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.model.HCopyTransOptions; import org.zanata.model.HDocument; import org.zanata.model.HProjectIteration; -import org.zanata.service.AsyncTaskManagerService; - -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import static org.zanata.async.tasks.CopyTransTask.CopyTransTaskHandle; +import org.zanata.service.CopyTransService; /** * Manager Bean that keeps track of manual copy trans being run in the system, @@ -55,12 +55,16 @@ @AutoCreate @Name("copyTransManager") @Scope(ScopeType.STATELESS) +@Slf4j // TODO This class should be merged with the copy trans service (?) public class CopyTransManager implements Serializable { private static final long serialVersionUID = 1L; @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private AsyncTaskHandleManager asyncTaskHandleManager; + + @In + private CopyTransService copyTransServiceImpl; @In private Identity identity; @@ -77,9 +81,7 @@ public boolean isCopyTransRunning(@Nonnull Object target) { "Copy Trans can only run for HProjectIteration and HDocument"); } - CopyTransTaskHandle handle = - (CopyTransTaskHandle) asyncTaskManagerServiceImpl - .getHandleByKey(key); + AsyncTaskHandle handle = asyncTaskHandleManager.getHandleByKey(key); return handle != null && !handle.isDone(); } @@ -106,8 +108,10 @@ public void startCopyTrans(HDocument document, HCopyTransOptions options) { } CopyTransProcessKey key = CopyTransProcessKey.getKey(document); - CopyTransTask task = new DocumentCopyTransTask(document, options); - asyncTaskManagerServiceImpl.startTask(task, key); + CopyTransTaskHandle handle = new CopyTransTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(handle, key); + copyTransServiceImpl.startCopyTransForDocument(document, options, + handle); } /** @@ -123,8 +127,10 @@ public void startCopyTrans(HProjectIteration iteration, } CopyTransProcessKey key = CopyTransProcessKey.getKey(iteration); - CopyTransTask task = new IterationCopyTransTask(iteration, options); - asyncTaskManagerServiceImpl.startTask(task, key); + CopyTransTaskHandle handle = new CopyTransTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(handle, key); + copyTransServiceImpl.startCopyTransForIteration(iteration, options, + handle); } public CopyTransTaskHandle getCopyTransProcessHandle(@Nonnull Object target) { @@ -138,7 +144,7 @@ public CopyTransTaskHandle getCopyTransProcessHandle(@Nonnull Object target) { throw new IllegalArgumentException( "Copy Trans can only run for HProjectIteration and HDocument"); } - return (CopyTransTaskHandle) asyncTaskManagerServiceImpl + return (CopyTransTaskHandle) asyncTaskHandleManager .getHandleByKey(key); } @@ -147,7 +153,7 @@ public void cancelCopyTrans(HProjectIteration iteration) { CopyTransProcessKey key = CopyTransProcessKey.getKey(iteration); CopyTransTaskHandle handle = this.getCopyTransProcessHandle(iteration); - handle.forceCancel(); + handle.cancel(true); handle.setCancelledTime(System.currentTimeMillis()); handle.setCancelledBy(identity.getCredentials().getUsername()); } diff --git a/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java b/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java index 5afe0fea2b..6769251552 100644 --- a/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java +++ b/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java @@ -1,25 +1,25 @@ package org.zanata.action; -import static org.zanata.async.tasks.CopyVersionTask.CopyVersionTaskHandle; - import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.security.Identity; -import org.zanata.async.tasks.CopyVersionTask; -import org.zanata.service.AsyncTaskManagerService; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; +import org.zanata.async.AsyncTaskHandleManager; +import org.zanata.async.handle.CopyVersionTaskHandle; +import org.zanata.service.CopyVersionService; /** * @author Alex Eng aeng@redhat.com + * @author Carlos Munoz aeng@redhat.com */ @AutoCreate @Name("copyVersionManager") @@ -27,23 +27,28 @@ @Slf4j public class CopyVersionManager implements Serializable { @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private AsyncTaskHandleManager asyncTaskHandleManager; + + @In + private CopyVersionService copyVersionServiceImpl; @In private Identity identity; public void startCopyVersion(String projectSlug, String versionSlug, String newVersionSlug) { - asyncTaskManagerServiceImpl.startTask(new CopyVersionTask( - projectSlug, versionSlug, newVersionSlug), - CopyVersionKey.getKey(projectSlug, newVersionSlug)); + CopyVersionKey key = CopyVersionKey.getKey(projectSlug, newVersionSlug); + CopyVersionTaskHandle handle = new CopyVersionTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(handle, key); + copyVersionServiceImpl.startCopyVersion(projectSlug, versionSlug, + newVersionSlug, handle); } public void cancelCopyVersion(String projectSlug, String versionSlug) { if (isCopyVersionRunning(projectSlug, versionSlug)) { CopyVersionTaskHandle handle = getCopyVersionProcessHandle(projectSlug, versionSlug); - handle.forceCancel(); + handle.cancel(true); handle.setCancelledTime(System.currentTimeMillis()); handle.setCancelledBy(identity.getCredentials().getUsername()); @@ -51,9 +56,9 @@ public void cancelCopyVersion(String projectSlug, String versionSlug) { } } - public CopyVersionTask.CopyVersionTaskHandle getCopyVersionProcessHandle( + public CopyVersionTaskHandle getCopyVersionProcessHandle( String projectSlug, String versionSlug) { - return (CopyVersionTask.CopyVersionTaskHandle) asyncTaskManagerServiceImpl + return (CopyVersionTaskHandle) asyncTaskHandleManager .getHandleByKey(CopyVersionKey.getKey(projectSlug, versionSlug)); } diff --git a/zanata-war/src/main/java/org/zanata/action/ProcessManagerAction.java b/zanata-war/src/main/java/org/zanata/action/ProcessManagerAction.java index b73a55d889..f7cecea92f 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProcessManagerAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ProcessManagerAction.java @@ -30,7 +30,7 @@ import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.security.Restrict; import org.zanata.async.AsyncTaskHandle; -import org.zanata.service.AsyncTaskManagerService; +import org.zanata.async.AsyncTaskHandleManager; /** * @author Carlos Munoz getRunningProcesses() { ArrayList allHandles = new ArrayList(); - allHandles.addAll(asyncTaskManagerServiceImpl.getAllHandles()); + allHandles.addAll(asyncTaskHandleManager.getAllHandles()); return allHandles; } public int getRunningCount() { int running = 0; - for (AsyncTaskHandle h : asyncTaskManagerServiceImpl.getAllHandles()) { + for (AsyncTaskHandle h : asyncTaskHandleManager.getAllHandles()) { if (!h.isDone()) { running++; } @@ -62,7 +62,7 @@ public int getRunningCount() { } public int getStoppedCount() { - return asyncTaskManagerServiceImpl.getAllHandles().size() + return asyncTaskHandleManager.getAllHandles().size() - getRunningCount(); } @@ -71,7 +71,7 @@ public Date getDateFromLong(long value) { } public void cancel(AsyncTaskHandle handle) { - handle.cancel(); + handle.cancel(true); } } diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectHomeAction.java b/zanata-war/src/main/java/org/zanata/action/ProjectHomeAction.java index d920bd7f5d..945246494a 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectHomeAction.java @@ -38,7 +38,7 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.security.management.JpaIdentityStore; -import org.zanata.async.tasks.CopyVersionTask; +import org.zanata.async.handle.CopyVersionTaskHandle; import org.zanata.common.EntityStatus; import org.zanata.dao.LocaleMemberDAO; import org.zanata.dao.ProjectDAO; @@ -47,7 +47,6 @@ import org.zanata.model.Activity; import org.zanata.model.HAccount; import org.zanata.model.HLocale; -import org.zanata.model.HProject; import org.zanata.model.HProjectIteration; import org.zanata.seam.scope.ConversationScopeMessages; import org.zanata.security.ZanataIdentity; @@ -162,7 +161,7 @@ public boolean isVersionCopying(String projectSlug, String versionSlug) { public String getCopiedDocumentCount(String projectSlug, String versionSlug) { - CopyVersionTask.CopyVersionTaskHandle handler = + CopyVersionTaskHandle handler = copyVersionManager.getCopyVersionProcessHandle(projectSlug, versionSlug); @@ -181,7 +180,7 @@ public void cancelCopyVersion(String projectSlug, String versionSlug) { public String getCopyVersionCompletePercent(String projectSlug, String versionSlug) { - CopyVersionTask.CopyVersionTaskHandle handler = + CopyVersionTaskHandle handler = copyVersionManager.getCopyVersionProcessHandle(projectSlug, versionSlug); @@ -202,7 +201,7 @@ public String getCopyVersionCompletePercent(String projectSlug, public String getCopyVersionTotalDocuments(String projectSlug, String versionSlug) { - CopyVersionTask.CopyVersionTaskHandle handler = + CopyVersionTaskHandle handler = copyVersionManager.getCopyVersionProcessHandle(projectSlug, versionSlug); diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java b/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java index 2bab36af86..217b83353a 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java @@ -3,20 +3,19 @@ import java.io.Serializable; import lombok.Getter; -import lombok.Setter; + import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Begin; import org.jboss.seam.annotations.End; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; -import org.jboss.seam.annotations.security.Restrict; import org.jboss.seam.security.Identity; import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.tasks.ZipFileBuildTask; +import org.zanata.async.AsyncTaskHandleManager; import org.zanata.dao.ProjectIterationDAO; import org.zanata.security.ZanataIdentity; -import org.zanata.service.AsyncTaskManagerService; +import org.zanata.service.TranslationArchiveService; @Name("projectIterationZipFileAction") @Scope(ScopeType.CONVERSATION) @@ -25,7 +24,7 @@ public class ProjectIterationZipFileAction implements Serializable { private static final long serialVersionUID = 1L; @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private AsyncTaskHandleManager asyncTaskHandleManager; @Getter private AsyncTaskHandle zipFilePrepHandle; @@ -36,6 +35,9 @@ public class ProjectIterationZipFileAction implements Serializable { @In private ProjectIterationDAO projectIterationDAO; + @In + private TranslationArchiveService translationArchiveServiceImpl; + @Begin(join = true) public void prepareIterationZipFile(boolean isPoProject, String projectSlug, String versionSlug, String localeId) { @@ -45,21 +47,24 @@ public void prepareIterationZipFile(boolean isPoProject, if (zipFilePrepHandle != null && !zipFilePrepHandle.isDone()) { // Cancel any other processes for this conversation - zipFilePrepHandle.forceCancel(); + zipFilePrepHandle.cancel(true); } - // Start a zip file task - ZipFileBuildTask task = - new ZipFileBuildTask(projectSlug, versionSlug, localeId, - Identity.instance().getCredentials().getUsername(), - isPoProject); - - String taskId = asyncTaskManagerServiceImpl.startTask(task); - zipFilePrepHandle = asyncTaskManagerServiceImpl.getHandle(taskId); + // Start the zip file build + zipFilePrepHandle = new AsyncTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(zipFilePrepHandle); + try { + translationArchiveServiceImpl.startBuildingTranslationFileArchive( + projectSlug, versionSlug, localeId, Identity.instance() + .getCredentials().getUsername(), zipFilePrepHandle); + } + catch (Exception e) { + throw new RuntimeException(e); + } } @End public void cancelFileDownload() { - zipFilePrepHandle.cancel(); + zipFilePrepHandle.cancel(true); } } diff --git a/zanata-war/src/main/java/org/zanata/action/ReindexAction.java b/zanata-war/src/main/java/org/zanata/action/ReindexAction.java index e551fe200b..06f8eac859 100644 --- a/zanata-war/src/main/java/org/zanata/action/ReindexAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ReindexAction.java @@ -17,7 +17,6 @@ import org.joda.time.format.PeriodFormatter; import org.joda.time.format.PeriodFormatterBuilder; import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.TimedAsyncHandle; import org.zanata.service.SearchIndexManager; import com.google.common.base.Optional; @@ -109,7 +108,7 @@ public boolean isError() { return false; } else if (taskHandle.isDone()) { try { - taskHandle.get(); + taskHandle.getResult(); } catch (InterruptedException e) { return true; } catch (ExecutionException e) { @@ -145,7 +144,7 @@ public void reindexDatabase() { } public void cancel() { - searchIndexManager.getProcessHandle().cancel(); + searchIndexManager.getProcessHandle().cancel(true); } public boolean isCanceled() { @@ -173,7 +172,7 @@ private String formatTimePeriod(long durationInMillis) { } public String getElapsedTime() { - TimedAsyncHandle processHandle = searchIndexManager.getProcessHandle(); + AsyncTaskHandle processHandle = searchIndexManager.getProcessHandle(); if (processHandle == null) { log.error("processHandle is null when looking up elapsed time"); return ""; diff --git a/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java b/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java index dff1a0c11c..3f6e679c40 100644 --- a/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java +++ b/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java @@ -25,6 +25,7 @@ import java.io.Serializable; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import javax.faces.event.ValueChangeEvent; @@ -41,14 +42,12 @@ import org.jboss.seam.faces.FacesMessages; import org.jboss.seam.framework.EntityHome; import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.SimpleAsyncTask; +import org.zanata.async.AsyncTaskHandleManager; import org.zanata.dao.TransMemoryDAO; import org.zanata.exception.EntityMissingException; import org.zanata.model.tm.TransMemory; import org.zanata.rest.service.TranslationMemoryResourceService; -import org.zanata.service.AsyncTaskManagerService; import org.zanata.service.SlugEntityService; -import org.zanata.util.ServiceLocator; /** * Controller class for the Translation Memory UI. @@ -70,7 +69,7 @@ public class TranslationMemoryAction extends EntityHome { private SlugEntityService slugEntityServiceImpl; @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private AsyncTaskHandleManager asyncTaskHandleManager; private List transMemoryList; @@ -79,7 +78,7 @@ public class TranslationMemoryAction extends EntityHome { */ @In(scope = ScopeType.PAGE, required = false) @Out(scope = ScopeType.PAGE, required = false) - private AsyncTaskHandle myTaskHandle; + private Future lastTaskResult; /** * Stores the last process error, but only for the duration of the event. @@ -109,25 +108,16 @@ public boolean validateSlug(String slug, String componentId) { public void clearTransMemory(final String transMemorySlug) { String name = "TranslationMemoryAction.clearTransMemory: "+transMemorySlug; - asyncTaskManagerServiceImpl.startTask(new SimpleAsyncTask(name) { - @Override - public Void call() throws Exception { - TranslationMemoryResourceService tmResource = - ServiceLocator.instance().getInstance( - "translationMemoryResource", - TranslationMemoryResourceService.class); - String msg = - tmResource.deleteTranslationUnitsUnguarded( - transMemorySlug).toString(); - return null; - } - }, new ClearTransMemoryProcessKey(transMemorySlug)); - + AsyncTaskHandle handle = new AsyncTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(handle, + new ClearTransMemoryProcessKey(transMemorySlug)); + translationMemoryResource + .deleteTranslationUnitsUnguardedAsync(transMemorySlug, handle); transMemoryList = null; // Force refresh next time list is requested } private boolean isProcessing() { - return myTaskHandle != null; + return lastTaskResult != null; } public boolean isProcessErrorPollEnabled() { @@ -146,9 +136,9 @@ public boolean isProcessErrorPollEnabled() { public String getProcessError() { if (myProcessError != null) return myProcessError; - if (myTaskHandle != null && myTaskHandle.isDone()) { + if (lastTaskResult != null && lastTaskResult.isDone()) { try { - myTaskHandle.get(); + lastTaskResult.get(); } catch (InterruptedException e) { // no error, just interrupted } catch (ExecutionException e) { @@ -156,7 +146,7 @@ public String getProcessError() { this.myProcessError = e.getCause() != null ? e.getCause().getMessage() : ""; } - myTaskHandle = null; + lastTaskResult = null; return myProcessError; } return ""; @@ -175,9 +165,8 @@ public void deleteTransMemory(String transMemorySlug) { public boolean isTransMemoryBeingCleared(String transMemorySlug) { AsyncTaskHandle handle = - asyncTaskManagerServiceImpl - .getHandleByKey(new ClearTransMemoryProcessKey( - transMemorySlug)); + asyncTaskHandleManager.getHandleByKey( + new ClearTransMemoryProcessKey(transMemorySlug)); return handle != null && !handle.isDone(); } diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java index 94522c191a..124751f146 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java @@ -43,7 +43,7 @@ import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.security.Restrict; import org.jboss.seam.util.Hex; -import org.zanata.async.tasks.CopyVersionTask; +import org.zanata.async.handle.CopyVersionTaskHandle; import org.zanata.common.DocumentType; import org.zanata.common.EntityStatus; import org.zanata.common.LocaleId; @@ -325,7 +325,7 @@ public boolean isInProgress() { @Override public String getCompletedPercentage() { - CopyVersionTask.CopyVersionTaskHandle handle = getHandle(); + CopyVersionTaskHandle handle = getHandle(); if (handle != null) { double completedPercent = (double) handle.getCurrentProgress() / (double) handle @@ -337,7 +337,7 @@ public String getCompletedPercentage() { } public int getProcessedDocuments() { - CopyVersionTask.CopyVersionTaskHandle handle = getHandle(); + CopyVersionTaskHandle handle = getHandle(); if (handle != null) { return handle.getDocumentCopied(); } @@ -345,7 +345,7 @@ public int getProcessedDocuments() { } public int getTotalDocuments() { - CopyVersionTask.CopyVersionTaskHandle handle = getHandle(); + CopyVersionTaskHandle handle = getHandle(); if (handle != null) { return handle.getTotalDoc(); } @@ -357,7 +357,7 @@ private CopyVersionManager getCopyVersionManager() { CopyVersionManager.class); } - private CopyVersionTask.CopyVersionTaskHandle getHandle() { + private CopyVersionTaskHandle getHandle() { CopyVersionManager copyVersionManager = ServiceLocator .instance().getInstance(CopyVersionManager.class); diff --git a/zanata-war/src/main/java/org/zanata/async/Async.java b/zanata-war/src/main/java/org/zanata/async/Async.java new file mode 100644 index 0000000000..fe1def2279 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/Async.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +@Target({ ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Async { +} diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java b/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java new file mode 100644 index 0000000000..3999262dcd --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java @@ -0,0 +1,125 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; +import org.jboss.seam.annotations.intercept.Interceptor; +import org.jboss.seam.intercept.InvocationContext; +import org.jboss.seam.intercept.JavaBeanInterceptor; +import org.jboss.seam.intercept.OptimizedInterceptor; +import org.zanata.util.ServiceLocator; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Future; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +@Interceptor(stateless = true, around = JavaBeanInterceptor.class) +public class AsyncMethodInterceptor implements OptimizedInterceptor { + + static final ThreadLocal interceptorRan = + new ThreadLocal(); + + @Override + public Object aroundInvoke(final InvocationContext ctx) throws Exception { + if( ctx.getMethod().getAnnotation(Async.class) == null ) { + return ctx.proceed(); + } + + final AsyncTaskManager taskManager = + ServiceLocator.instance().getInstance( + AsyncTaskManager.class); + + final AsyncTaskHandleManager taskHandleManager = + ServiceLocator.instance().getInstance( + AsyncTaskHandleManager.class); + + Class methodReturnType = ctx.getMethod().getReturnType(); + if (methodReturnType != void.class + && !Future.class.isAssignableFrom(methodReturnType)) { + throw new RuntimeException("Async method " + + ctx.getMethod().getName() + + " must return java.lang.Future or nothing at all"); + } + + if (interceptorRan.get() == null) { + // If there is a Task handle parameter (only the first one will be + // registered) + final Optional handle = + Optional.fromNullable(findHandleIfPresent(ctx + .getParameters())); + + AsyncTask asyncTask = new AsyncTask() { + @Override + public Object call() throws Throwable { + interceptorRan.set(true); + try { + if( handle.isPresent() ) { + handle.get().startTiming(); + } + Object target = + ServiceLocator.instance().getInstance( + ctx.getMethod() + .getDeclaringClass()); + return ctx.getMethod().invoke(target, + ctx.getParameters()); + } catch (InvocationTargetException itex) { + // exception thrown from the invoked method + throw itex.getCause(); + } + finally { + interceptorRan.remove(); + if (handle.isPresent()) { + handle.get().finishTiming(); + taskHandleManager.taskFinished(handle.get()); + } + } + } + }; + + ListenableFuture futureResult = + taskManager.startTask(asyncTask); + if( handle.isPresent() ) { + handle.get().setFutureResult(futureResult); + } + return futureResult; + // Async methods should return ListenableFuture + } else { + return ctx.proceed(); + } + } + + @Override + public boolean isInterceptorEnabled() { + return true; + } + + private AsyncTaskHandle findHandleIfPresent(Object[] params) { + for (Object param : params) { + if (param instanceof AsyncTaskHandle) { + return (AsyncTaskHandle) param; + } + } + return null; + } +} diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTask.java b/zanata-war/src/main/java/org/zanata/async/AsyncTask.java index a319201a41..6cc106975e 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsyncTask.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTask.java @@ -20,27 +20,30 @@ */ package org.zanata.async; -import javax.annotation.Nonnull; -import java.util.concurrent.Callable; +import java.util.concurrent.Future; + +import lombok.Getter; +import lombok.Setter; /** - * Public common interface for all asynchronous tasks in the system. + * Public common class for all asynchronous tasks in the system. * * @param * The type of value returned by this task once finished. - * @param - * The type of task handler provided by the task to keep callers - * informed of progress and/or other task related information. * * @author Carlos Munoz camunoz@redhat.com */ -public interface AsyncTask> extends Callable { +public abstract class AsyncTask { + + @Getter @Setter + private Future future; /** - * @return The handle used to keep task information. Tasks must always - * return the same instance of a handle. + * Computes a result, or throws a Throwable if unable to do so. + * + * @return computed result + * @throws Throwable if unable to compute a result */ - @Nonnull H getHandle(); - + abstract V call() throws Throwable; } diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java index ee9dc401bd..3c6954fbf8 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java @@ -20,13 +20,17 @@ */ package org.zanata.async; -import javax.annotation.Nullable; - -import com.google.common.util.concurrent.AbstractFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; +import com.google.common.base.Optional; + /** * Asynchronous handle to provide communication between an asynchronous task and * interested clients. @@ -34,10 +38,10 @@ * @author Carlos Munoz camunoz@redhat.com */ -public class AsyncTaskHandle extends AbstractFuture { +public class AsyncTaskHandle { - @Getter - private final String taskName; + @Setter(AccessLevel.PACKAGE) + private Future futureResult; @Getter @Setter @@ -51,51 +55,108 @@ public class AsyncTaskHandle extends AbstractFuture { @Setter public int currentProgress = 0; - public AsyncTaskHandle(String taskName) { - this.taskName = taskName; + @Getter + private long startTime = -1; + + @Getter + private long finishTime = -1; + + public int increaseProgress(int increaseBy) { + currentProgress += increaseBy; + return currentProgress; } - @Override - public String toString() { - return getClass().getSimpleName()+"(taskName="+taskName+")"; + void startTiming() { + startTime = System.currentTimeMillis(); } - @Override - protected boolean setException(Throwable throwable) { - return super.setException(throwable); + void finishTiming() { + finishTime = System.currentTimeMillis(); } - @Override - protected boolean set(@Nullable V value) { - return super.set(value); + public boolean cancel(boolean mayInterruptIfRunning) { + return futureResult.cancel(mayInterruptIfRunning); } - public int increaseProgress(int increaseBy) { - currentProgress += increaseBy; - return currentProgress; + public V getResult() throws InterruptedException, ExecutionException { + return futureResult.get(); + } + + public V getResult(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return futureResult.get(timeout, unit); + } + + public boolean isDone() { + return futureResult != null && futureResult.isDone(); + } + + public boolean isCancelled() { + return futureResult != null && futureResult.isCancelled(); + } + + public boolean isStarted() { + return startTime >= 0; + } + + /** + * @return An optional container with the estimated time remaining for the + * process to finish, or an empty container if the time cannot be + * estimated. + */ + public Optional getEstimatedTimeRemaining() { + if (this.startTime > 0 && currentProgress > 0) { + long currentTime = System.currentTimeMillis(); + long timeElapsed = currentTime - this.startTime; + int remainingUnits = this.maxProgress - this.currentProgress; + return Optional.of(timeElapsed * remainingUnits + / this.currentProgress); + } else { + return Optional.absent(); + } + } + + /** + * @return The time that the task has been executing for, or the total + * execution time if the task has finished (in milliseconds). + */ + public long getExecutingTime() { + if (startTime > 0) { + if (finishTime > startTime) { + return finishTime - startTime; + } else { + return System.currentTimeMillis() - startTime; + } + } else { + return 0; + } } /** - * Cancels the task without trying to forcefully interrupt the task. This is - * equivalent to calling cancel(false) - * - * @see AsyncTaskHandle#cancel(boolean) - * @return false if the task could not be cancelled, typically because it - * has already completed normally; true otherwise + * @return The estimated elapsed time (in milliseconds) from the start of + * the process. */ - public boolean cancel() { - return cancel(false); + public long getTimeSinceStart() { + if (this.startTime > 0) { + long currentTime = System.currentTimeMillis(); + long timeElapsed = currentTime - this.startTime; + return timeElapsed; + } else { + return 0; + } } /** - * Cancels the task, forcefully cancelling the task if needed. This is - * equivalent to calling cancel(true) - * - * @see AsyncTaskHandle#cancel(boolean) - * @return false if the task could not be cancelled, typically because it - * has already completed normally; true otherwise + * @return The estimated elapsed time (in milliseconds) from the finish of + * the process. */ - public boolean forceCancel() { - return cancel(true); + public long getTimeSinceFinish() { + if (finishTime > 0) { + long currentTime = System.currentTimeMillis(); + long timeElapsed = currentTime - finishTime; + return timeElapsed; + } else { + return 0; + } } } diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java new file mode 100644 index 0000000000..60197a67ae --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java @@ -0,0 +1,101 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import com.google.common.collect.Lists; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Maps; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +@Name("asyncTaskHandleManager") +@Scope(ScopeType.APPLICATION) +@AutoCreate +public class AsyncTaskHandleManager { + + private Map handlesByKey = Maps + .newConcurrentMap(); + + // Cache of recently completed tasks + private Cache finishedTasks = CacheBuilder + .newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) + .build(); + + public synchronized void registerTaskHandle(AsyncTaskHandle handle, + Serializable key) { + if (handlesByKey.containsKey(key)) { + throw new RuntimeException("Task handle with key " + key + + " already exists"); + } + handlesByKey.put(key, handle); + } + + /** + * Registers a task handle. + * @param handle The handle to register. + * @return An auto generated key to retreive the handle later + */ + public synchronized Serializable registerTaskHandle(AsyncTaskHandle handle) { + Serializable autoGenKey = UUID.randomUUID().toString(); + registerTaskHandle(handle, autoGenKey); + return autoGenKey; + } + + void taskFinished( AsyncTaskHandle taskHandle ) { + synchronized (handlesByKey) { + // TODO This operation is O(n). Maybe we can do better? + for (Map.Entry entry : handlesByKey + .entrySet()) { + if( entry.getValue().equals( taskHandle ) ) { + handlesByKey.remove( entry.getKey() ); + finishedTasks.put(entry.getKey(), entry.getValue()); + } + } + } + } + + public AsyncTaskHandle getHandleByKey(Serializable key) { + if( handlesByKey.containsKey(key) ) { + return handlesByKey.get(key); + } + return finishedTasks.getIfPresent(key); + } + + public Collection getAllHandles() { + Collection handles = Lists.newArrayList(); + handles.addAll(handlesByKey.values()); + handles.addAll(finishedTasks.asMap().values()); + return handles; + } +} diff --git a/zanata-war/src/main/java/org/zanata/async/AsynchronousTaskExecutor.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java similarity index 55% rename from zanata-war/src/main/java/org/zanata/async/AsynchronousTaskExecutor.java rename to zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java index 6a072a4236..5e23c59335 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsynchronousTaskExecutor.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the * @author tags. See the copyright.txt file in the distribution for a full * listing of individual contributors. * @@ -21,68 +21,83 @@ package org.zanata.async; import java.security.Principal; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import javax.annotation.Nonnull; import javax.security.auth.Subject; -import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; + import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Create; +import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; -import org.jboss.seam.annotations.async.Asynchronous; +import org.jboss.seam.contexts.Lifecycle; import org.jboss.seam.security.RunAsOperation; - -import lombok.extern.slf4j.Slf4j; import org.zanata.action.AuthenticationEvents; import org.zanata.dao.AccountDAO; import org.zanata.model.HAccount; +import org.zanata.security.ZanataIdentity; import org.zanata.security.ZanataJpaIdentityStore; import org.zanata.util.ServiceLocator; +import com.google.common.util.concurrent.ListenableFuture; + /** - * This class executes a Runnable Process asynchronously. Do not use this class - * directly. Use {@link org.zanata.async.TaskExecutor} instead as this is just a - * wrapper to make sure Seam can run the task in the background. - * {@link TaskExecutor} is able to do this as well as return an instance of the - * task handle to keep track of the task's progress. * @author Carlos Munoz camunoz@redhat.com */ -@Name("asynchronousTaskExecutor") -@Scope(ScopeType.STATELESS) +@Name("asyncTaskManager") +@Scope(ScopeType.APPLICATION) @AutoCreate @Slf4j -public class AsynchronousTaskExecutor { +public class AsyncTaskManager { + + private ExecutorService scheduler; + + @Create + public void init() { + scheduler = Executors.newFixedThreadPool(3); + } + + @Destroy + public void cleanup() { + scheduler.shutdown(); + } + + public ListenableFuture startTask( + final @Nonnull AsyncTask> task) { + HAccount taskOwner = ServiceLocator.instance() + .getInstance(ZanataJpaIdentityStore.AUTHENTICATED_USER, + HAccount.class); + ZanataIdentity ownerIdentity = ZanataIdentity.instance(); + + // Extract security context from current thread + final String taskOwnerUsername = + taskOwner != null ? taskOwner.getUsername() : null; + final Principal runAsPpal = ownerIdentity.getPrincipal(); + final Subject runAsSubject = ownerIdentity.getSubject(); + + // final result + final AsyncTaskResult taskFuture = new AsyncTaskResult(); + + final RunnableOperation runnableOp = new RunnableOperation() { - /** - * Runs the provided task asynchronously with the given security - * constraints. - * - * @param task Task to run asynchronously. - * @param runAsPpal Security Principal to tun the task. - * @param runAsSubject Security Subject to run the task. - * @param username The username to run the task. - */ - @Asynchronous - public > void runAsynchronously( - final AsyncTask task, final Runnable onComplete, - final Principal runAsPpal, - final Subject runAsSubject, final String username) { - AsyncUtils.outject(task.getHandle(), ScopeType.EVENT); - - RunAsOperation runAsOp = new RunAsOperation() { @Override public void execute() { try { - prepareSecurityContext(username); - V returnValue = task.call(); - task.getHandle().set(returnValue); + prepareSecurityContext(taskOwnerUsername); + V returnValue = getReturnValue(task.call()); + taskFuture.set(returnValue); } catch (Throwable t) { - task.getHandle().setException(t); + taskFuture.setException(t); log.error( - "Exception when executing an asynchronous task.", t); + "Exception when executing an asynchronous task.", t); } - onComplete.run(); } @Override @@ -95,7 +110,17 @@ public Subject getSubject() { return runAsSubject; } }; - runAsOp.run(); + scheduler.execute(runnableOp); + return taskFuture; + } + + private static V getReturnValue(Future asyncTaskFuture) + throws Exception { + // If the async method returns void + if (asyncTaskFuture == null) { + return null; + } + return asyncTaskFuture.get(); } /** @@ -108,7 +133,7 @@ private static void prepareSecurityContext(String username) { * a way to simulate a login for asyn tasks, or at least to inherit the * caller's context */ - if( username != null ) { + if (username != null) { // Only if it's an authenticated task should it try and do this // injection AccountDAO accountDAO = @@ -123,4 +148,16 @@ private static void prepareSecurityContext(String username) { idStore.setAuthenticateUser(authenticatedAccount); } } + + public abstract class RunnableOperation extends RunAsOperation implements + Runnable { + + @Override + public void run() { + Lifecycle.beginCall(); // Start contexts + super.run(); + Lifecycle.endCall(); // End contexts + } + } + } diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTaskResult.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskResult.java new file mode 100644 index 0000000000..a6b246d153 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskResult.java @@ -0,0 +1,54 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async; + +import javax.annotation.Nullable; + +import com.google.common.util.concurrent.AbstractFuture; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +public class AsyncTaskResult extends AbstractFuture { + + AsyncTaskResult() { + } + + public static AsyncTaskResult taskResult(T value) { + AsyncTaskResult result = new AsyncTaskResult(); + result.set(value); + return result; + } + + public static AsyncTaskResult taskResult() { + return taskResult(null); + } + + @Override + public boolean set(@Nullable V value) { + return super.set(value); + } + + @Override + public boolean setException(Throwable throwable) { + return super.setException(throwable); + } +} diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncUtils.java b/zanata-war/src/main/java/org/zanata/async/AsyncUtils.java deleted file mode 100644 index 254a0e0448..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/AsyncUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.async; - -import org.jboss.seam.ScopeType; - -import com.google.common.base.Optional; - -import lombok.extern.slf4j.Slf4j; - -/** - * @author Carlos Munoz camunoz@redhat.com - */ -@Slf4j -public class AsyncUtils { - private static final String ASYNC_HANDLE_NAME = "__ASYNC_HANDLE__"; - - /** - * Outjects an asynchronous task handle. Use - * {@link AsyncUtils#getAsyncHandle(org.jboss.seam.ScopeType, Class)} or - * {@link AsyncUtils#getEventAsyncHandle(Class)} to retrieve the outjected - * handle. - * - * @param handle - * The handle to outject. - * @param scopeType - * The scope to outject the handle to. - */ - public static final void outject(AsyncTaskHandle handle, - ScopeType scopeType) { - if (scopeType.isContextActive()) { - scopeType.getContext().set(ASYNC_HANDLE_NAME, handle); - } else { - log.warn("Could not outject Async handle to scope " - + scopeType.toString() + " as the context is not active"); - } - } - - /** - * Fetches an asynchronous task handle fro mthe event context. - * - * @param type - * The expected handle type. - * @return null if no handle is found. - */ - public static final Optional - getEventAsyncHandle(Class type) { - return getAsyncHandle(ScopeType.EVENT, type); - } - - /** - * Fetches an asynchronous task handle from a Seam context. - * - * @param scopeType - * The seam scope to look for the handle. - * @param type - * The expected handle type. - * @return null if no handle is found. - */ - public static final Optional getAsyncHandle( - ScopeType scopeType, Class type) { - if (scopeType.isContextActive()) { - return Optional. fromNullable((H) scopeType.getContext().get( - ASYNC_HANDLE_NAME)); - } - return Optional.absent(); - } - -} diff --git a/zanata-war/src/main/java/org/zanata/async/ContainsAsyncMethods.java b/zanata-war/src/main/java/org/zanata/async/ContainsAsyncMethods.java new file mode 100644 index 0000000000..8bfa912e9d --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/ContainsAsyncMethods.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@org.jboss.seam.annotations.intercept.Interceptors(AsyncMethodInterceptor.class) +public @interface ContainsAsyncMethods { +} diff --git a/zanata-war/src/main/java/org/zanata/async/TaskExecutor.java b/zanata-war/src/main/java/org/zanata/async/TaskExecutor.java deleted file mode 100644 index 5fc22b3f3b..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/TaskExecutor.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.async; - -import org.jboss.seam.ScopeType; -import org.jboss.seam.annotations.AutoCreate; -import org.jboss.seam.annotations.In; -import org.jboss.seam.annotations.Name; -import org.jboss.seam.annotations.Scope; -import org.jboss.seam.security.Identity; - -import javax.annotation.Nonnull; - -/** - * This component executes {@link org.zanata.async.AsyncTask} instances. It is - * generally more advisable to use the - * {@link org.zanata.service.AsyncTaskManagerService} component when running - * asynchronous tasks. - * - * @author Carlos Munoz camunoz@redhat.com - */ -@Name("taskExecutor") -@Scope(ScopeType.STATELESS) -@AutoCreate -public class TaskExecutor { - @In - private AsynchronousTaskExecutor asynchronousTaskExecutor; - - /** - * Executes an asynchronous task in the background. - * - * @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 > AsyncTaskHandle startTask( - @Nonnull AsyncTask task, Runnable onComplete) { - H handle = task.getHandle(); - Identity identity = Identity.instance(); - asynchronousTaskExecutor.runAsynchronously(task, onComplete, identity - .getPrincipal(), identity.getSubject(), - identity.getCredentials().getUsername()); - return handle; - } - -} diff --git a/zanata-war/src/main/java/org/zanata/async/TimedAsyncHandle.java b/zanata-war/src/main/java/org/zanata/async/TimedAsyncHandle.java deleted file mode 100644 index 0278f119b6..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/TimedAsyncHandle.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.async; - -import com.google.common.base.Optional; - -import lombok.Getter; - -/** - * An Asynchronous handle that has facility methods to time the duration of the - * task. - * - * @author Carlos Munoz camunoz@redhat.com - */ -public class TimedAsyncHandle extends AsyncTaskHandle { - - @Getter - private long startTime = -1; - - @Getter - private long finishTime = -1; - - public TimedAsyncHandle(String taskName) { - super(taskName); - } - - public void startTiming() { - startTime = System.currentTimeMillis(); - } - - public void finishTiming() { - finishTime = System.currentTimeMillis(); - } - - /** - * @return An optional container with the estimated time remaining for the - * process to finish, or an empty container if the time cannot be - * estimated. - */ - public Optional getEstimatedTimeRemaining() { - if (this.startTime > 0 && currentProgress > 0) { - long currentTime = System.currentTimeMillis(); - long timeElapsed = currentTime - this.startTime; - int remainingUnits = this.maxProgress - this.currentProgress; - return Optional.of(timeElapsed * remainingUnits - / this.currentProgress); - } else { - return Optional.absent(); - } - } - - /** - * @return The time that the task has been executing for, or the total - * execution time if the task has finished (in milliseconds). - */ - public long getExecutingTime() { - if (startTime > 0) { - if (finishTime > startTime) { - return finishTime - startTime; - } else { - return System.currentTimeMillis() - startTime; - } - } else { - return 0; - } - } - - /** - * @return The estimated elapsed time (in milliseconds) from the start of - * the process. - */ - public long getTimeSinceStart() { - if (this.startTime > 0) { - long currentTime = System.currentTimeMillis(); - long timeElapsed = currentTime - this.startTime; - return timeElapsed; - } else { - return 0; - } - } - - /** - * @return The estimated elapsed time (in milliseconds) from the finish of - * the process. - */ - public long getTimeSinceFinish() { - if (finishTime > 0) { - long currentTime = System.currentTimeMillis(); - long timeElapsed = currentTime - finishTime; - return timeElapsed; - } else { - return 0; - } - } -} diff --git a/zanata-war/src/main/java/org/zanata/async/SimpleAsyncTask.java b/zanata-war/src/main/java/org/zanata/async/handle/CopyTransTaskHandle.java similarity index 67% rename from zanata-war/src/main/java/org/zanata/async/SimpleAsyncTask.java rename to zanata-war/src/main/java/org/zanata/async/handle/CopyTransTaskHandle.java index e7bb8d8b3a..3caa57738e 100644 --- a/zanata-war/src/main/java/org/zanata/async/SimpleAsyncTask.java +++ b/zanata-war/src/main/java/org/zanata/async/handle/CopyTransTaskHandle.java @@ -1,5 +1,5 @@ /* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the * @author tags. See the copyright.txt file in the distribution for a full * listing of individual contributors. * @@ -18,26 +18,33 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF * site: http://www.fsf.org. */ -package org.zanata.async; +package org.zanata.async.handle; import lombok.Getter; - -import javax.annotation.Nonnull; +import lombok.Setter; +import org.zanata.async.AsyncTaskHandle; /** - * Simple default partial implementation of an async task that produces a bare - * bones {@link AsyncTaskHandle} that tracks progress. - * * @author Carlos Munoz camunoz@redhat.com */ -public abstract class SimpleAsyncTask implements - AsyncTask> { +public class CopyTransTaskHandle extends AsyncTaskHandle { + @Getter + @Setter + private String cancelledBy; + + @Getter + @Setter + private long cancelledTime; + + @Getter + @Setter + private String triggeredBy; + @Getter - @Nonnull - private final AsyncTaskHandle handle; + private boolean prepared; - public SimpleAsyncTask(String taskName) { - handle = new AsyncTaskHandle(taskName); + public void setPrepared() { + this.prepared = true; } } diff --git a/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java b/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java new file mode 100644 index 0000000000..5b1b00cb13 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async.handle; + +import lombok.Getter; +import lombok.Setter; +import org.zanata.async.AsyncTaskHandle; + +/** + * Asynchronous task handle for the copy version process. + * + * @author Carlos Munoz camunoz@redhat.com + */ +public class CopyVersionTaskHandle extends AsyncTaskHandle { + @Getter @Setter + private int documentCopied; + @Getter @Setter + private int totalDoc; + @Getter @Setter + private String cancelledBy; + @Getter @Setter + private long cancelledTime; + @Getter @Setter + private String triggeredBy; + @Getter + private boolean prepared; + + public void setPrepared() { + this.prepared = true; + } + + /** + * Increments the processed document count by 1 + */ + public void incrementDocumentProcessed() { + documentCopied++; + } +} diff --git a/zanata-war/src/main/java/org/zanata/async/tasks/CopyTransTask.java b/zanata-war/src/main/java/org/zanata/async/tasks/CopyTransTask.java deleted file mode 100644 index 10b6578c66..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/tasks/CopyTransTask.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.async.tasks; - -import lombok.Getter; -import lombok.Setter; -import org.zanata.async.AsyncTask; -import org.zanata.async.TimedAsyncHandle; -import org.zanata.model.HCopyTransOptions; -import org.zanata.security.ZanataIdentity; - -import javax.annotation.Nonnull; -import java.io.IOException; - -/** - * Asynchronous Task that runs copy trans. Subclasses should allow for running - * copy trans against different targets. - * - * @author Carlos Munoz camunoz@redhat.com - */ -public abstract class CopyTransTask implements - AsyncTask { - - protected HCopyTransOptions copyTransOptions; - - private final CopyTransTaskHandle handle; - - public CopyTransTask(HCopyTransOptions copyTransOptions, String taskName) { - this.copyTransOptions = copyTransOptions; - this.handle = new CopyTransTaskHandle(taskName); - } - - @Nonnull - @Override - public CopyTransTaskHandle getHandle() { - return handle; - } - - /** - * @return The maximum progress for the copy trans task - * (ie the number of textflows times the number of locales). - */ - protected abstract int getMaxProgress(); - - protected abstract void callCopyTrans(); - - @Override - public Void call() throws Exception { - getHandle().startTiming(); - getHandle() - .setTriggeredBy(ZanataIdentity.instance().getAccountUsername()); - getHandle().setMaxProgress(getMaxProgress()); - - callCopyTrans(); - - getHandle().finishTiming(); - return null; - } - - public static class CopyTransTaskHandle extends TimedAsyncHandle { - @Getter - @Setter - private String cancelledBy; - - @Getter - @Setter - private long cancelledTime; - - @Getter - @Setter - private String triggeredBy; - - public CopyTransTaskHandle(String taskName) { - super(taskName); - } - - } -} diff --git a/zanata-war/src/main/java/org/zanata/async/tasks/CopyVersionTask.java b/zanata-war/src/main/java/org/zanata/async/tasks/CopyVersionTask.java deleted file mode 100644 index b7fae529ae..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/tasks/CopyVersionTask.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.zanata.async.tasks; - -import javax.annotation.Nonnull; - -import org.zanata.async.AsyncTask; -import org.zanata.async.TimedAsyncHandle; -import org.zanata.security.ZanataIdentity; -import org.zanata.service.CopyVersionService; -import org.zanata.service.impl.CopyVersionServiceImpl; -import org.zanata.util.ServiceLocator; - -import lombok.Getter; -import lombok.Setter; - -/** - * @author Alex Eng aeng@redhat.com - */ - -public class CopyVersionTask implements - AsyncTask { - - private final CopyVersionTaskHandle handle; - - private final String projectSlug; - private final String versionSlug; - private final String newVersionSlug; - - public CopyVersionTask(String projectSlug, String versionSlug, - String newVersionSlug) { - handle = - new CopyVersionTaskHandle("VersionCopyTask:" + versionSlug - + ":" + newVersionSlug); - this.projectSlug = projectSlug; - this.versionSlug = versionSlug; - this.newVersionSlug = newVersionSlug; - } - - /** - * @return The maximum progress for the copy version task. (total document - * to be copied) - */ - protected int getMaxProgress() { - CopyVersionService copyVersionServiceImpl = - ServiceLocator.instance().getInstance( - CopyVersionServiceImpl.class); - - return copyVersionServiceImpl.getTotalDocCount( - projectSlug, versionSlug); - } - - protected void runCopyVersion() { - CopyVersionService copyVersionServiceImpl = - ServiceLocator.instance().getInstance( - CopyVersionServiceImpl.class); - copyVersionServiceImpl.copyVersion(projectSlug, versionSlug, - newVersionSlug); - } - - @Override - public Void call() throws Exception { - getHandle().startTiming(); - getHandle() - .setTriggeredBy(ZanataIdentity.instance().getAccountUsername()); - getHandle().setMaxProgress(getMaxProgress()); - getHandle().setTotalDoc(getMaxProgress()); - - runCopyVersion(); - - getHandle().finishTiming(); - return null; - } - - @Nonnull - @Override - public CopyVersionTaskHandle getHandle() { - return handle; - } - - @Getter - @Setter - public static class CopyVersionTaskHandle extends TimedAsyncHandle { - private int documentCopied; - private int totalDoc; - private String cancelledBy; - private long cancelledTime; - private String triggeredBy; - - public CopyVersionTaskHandle(String taskName) { - super(taskName); - } - - /** - * Increments the processed task by 1 - */ - public void incrementDocumentProcessed() { - documentCopied++; - } - - } -} diff --git a/zanata-war/src/main/java/org/zanata/async/tasks/DocumentCopyTransTask.java b/zanata-war/src/main/java/org/zanata/async/tasks/DocumentCopyTransTask.java deleted file mode 100644 index e2d70736b0..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/tasks/DocumentCopyTransTask.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.async.tasks; - -import java.util.List; - -import org.zanata.dao.TextFlowDAO; -import org.zanata.model.HCopyTransOptions; -import org.zanata.model.HDocument; -import org.zanata.model.HLocale; -import org.zanata.service.CopyTransService; -import org.zanata.service.LocaleService; -import org.zanata.service.impl.CopyTransServiceImpl; -import org.zanata.service.impl.LocaleServiceImpl; -import org.zanata.util.ServiceLocator; - -/** - * Copy Trans task that runs copy trans on a single document and all languages - * available to its iteration. - * - * @author Carlos Munoz camunoz@redhat.com - */ -public class DocumentCopyTransTask extends CopyTransTask { - private HDocument document; - - public DocumentCopyTransTask(HDocument document, - HCopyTransOptions copyTransOptions) { - super(copyTransOptions, "DocumentCopyTransTask: " + document.getDocId()); - this.document = document; - } - - @Override - protected int getMaxProgress() { - LocaleService localeService = - ServiceLocator.instance().getInstance(LocaleServiceImpl.class); - - List localeList = - localeService.getSupportedLanguageByProjectIteration(document - .getProjectIteration().getProject().getSlug(), document - .getProjectIteration().getSlug()); - int localeCount = localeList.size(); - - int textFlowCount = ServiceLocator.instance().getInstance( - TextFlowDAO.class) - .countActiveTextFlowsInDocument( - document.getId()); - return localeCount * textFlowCount; - } - - @Override - protected void callCopyTrans() { - CopyTransService copyTransServiceImpl = - ServiceLocator.instance().getInstance( - CopyTransServiceImpl.class); - copyTransServiceImpl.copyTransForDocument(document, copyTransOptions); - } -} diff --git a/zanata-war/src/main/java/org/zanata/async/tasks/IterationCopyTransTask.java b/zanata-war/src/main/java/org/zanata/async/tasks/IterationCopyTransTask.java deleted file mode 100644 index 5812985576..0000000000 --- a/zanata-war/src/main/java/org/zanata/async/tasks/IterationCopyTransTask.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.async.tasks; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; -import org.jboss.seam.Component; -import org.zanata.dao.TextFlowDAO; -import org.zanata.model.HCopyTransOptions; -import org.zanata.model.HLocale; -import org.zanata.model.HProjectIteration; -import org.zanata.service.CopyTransService; -import org.zanata.service.LocaleService; -import org.zanata.service.impl.CopyTransServiceImpl; -import org.zanata.service.impl.LocaleServiceImpl; -import org.zanata.util.ServiceLocator; - -/** - * Copy Trans task that runs copy trans for a whole project iteration and all - * languages. - * - * @author Carlos Munoz camunoz@redhat.com - */ -@Slf4j -public class IterationCopyTransTask extends CopyTransTask { - private HProjectIteration projectIteration; - private long maxProgress = -1; - - public IterationCopyTransTask(HProjectIteration projectIteration, - HCopyTransOptions options) { - super(options, "IterationCopyTransTask: " + projectIteration.getSlug()); - this.projectIteration = projectIteration; - } - - @Override - protected int getMaxProgress() { - if (maxProgress < 0) { - ServiceLocator locator = ServiceLocator.instance(); - log.debug("counting locales"); - LocaleService localeService = - locator.getInstance(LocaleServiceImpl.class); - List localeList = - localeService.getSupportedLanguageByProjectIteration( - projectIteration.getProject().getSlug(), - projectIteration.getSlug()); - int localeCount = localeList.size(); - log.debug("counting locales finished"); - log.debug("counting textflows"); - TextFlowDAO textFlowDAO = - locator.getInstance(TextFlowDAO.class); - long textFlowCount = textFlowDAO.countActiveTextFlowsInProjectIteration( - projectIteration.getId()); - log.debug("counting textflows finished"); - maxProgress = localeCount * textFlowCount; - } - // TODO use long for progress everywhere - return (int) maxProgress; - } - - @Override - protected void callCopyTrans() { - CopyTransService copyTransServiceImpl = - ServiceLocator.instance().getInstance( - CopyTransServiceImpl.class); - copyTransServiceImpl.copyTransForIteration(projectIteration, - copyTransOptions); - } -} diff --git a/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java b/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java index 7191b44f0e..86e4af89ab 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java @@ -20,6 +20,7 @@ */ package org.zanata.rest.service; +import java.io.Serializable; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -29,7 +30,7 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Transactional; import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.SimpleAsyncTask; +import org.zanata.async.AsyncTaskHandleManager; import org.zanata.common.EntityStatus; import org.zanata.common.LocaleId; import org.zanata.common.MergeType; @@ -44,17 +45,13 @@ import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.TranslationsResource; import org.zanata.security.ZanataIdentity; -import org.zanata.service.AsyncTaskManagerService; import org.zanata.service.DocumentService; import org.zanata.service.LocaleService; import org.zanata.service.TranslationService; -import org.zanata.service.impl.DocumentServiceImpl; -import org.zanata.service.impl.TranslationServiceImpl; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; -import org.zanata.util.ServiceLocator; import javax.ws.rs.Path; @@ -74,10 +71,16 @@ public class AsynchronousProcessResourceService implements AsynchronousProcessResource { @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private LocaleService localeServiceImpl; @In - private LocaleService localeServiceImpl; + private DocumentService documentServiceImpl; + + @In + private TranslationService translationServiceImpl; + + @In + private AsyncTaskHandleManager asyncTaskHandleManager; @In private DocumentDAO documentDAO; @@ -119,23 +122,15 @@ public ProcessStatus startSourceDocCreation(final String idNoSlash, } String name = "SourceDocCreation: "+projectSlug+"-"+iterationSlug+"-"+idNoSlash; - SimpleAsyncTask task = new SimpleAsyncTask(name) { - @Override - public Void call() throws Exception { - DocumentService documentServiceImpl = - ServiceLocator.instance().getInstance( - DocumentServiceImpl.class); - documentServiceImpl.saveDocument(projectSlug, iterationSlug, - resource, extensions, copytrans, true); - // TODO This should update with real progress - getHandle().setCurrentProgress(getHandle().getMaxProgress()); - return null; - } - }; - - String taskId = asyncTaskManagerServiceImpl.startTask(task); - return getProcessStatus(taskId); // TODO Change to return 202 Accepted, - // with a url to get the progress + AsyncTaskHandle handle = new AsyncTaskHandle(); + Serializable taskId = asyncTaskHandleManager.registerTaskHandle(handle); + documentServiceImpl.saveDocumentAsync(projectSlug, iterationSlug, + resource, extensions, copytrans, true, handle); + + return getProcessStatus(taskId.toString()); // TODO Change to return 202 + // Accepted, + // with a url to get the + // progress } @Override @@ -150,23 +145,15 @@ public ProcessStatus startSourceDocCreationOrUpdate(final String idNoSlash, resourceUtils.validateExtensions(extensions); // gettext, comment String name = "SourceDocCreationOrUpdate: "+projectSlug+"-"+iterationSlug+"-"+idNoSlash; - SimpleAsyncTask task = new SimpleAsyncTask(name) { - @Override - public Void call() throws Exception { - DocumentService documentServiceImpl = - ServiceLocator.instance().getInstance( - DocumentServiceImpl.class); - documentServiceImpl.saveDocument(projectSlug, iterationSlug, - resource, extensions, copytrans, true); - // TODO This should update with real progress - return null; - } - }; - - String taskId = asyncTaskManagerServiceImpl.startTask(task); - return this.getProcessStatus(taskId); // TODO Change to return 202 - // Accepted, with a url to get the - // progress + AsyncTaskHandle handle = new AsyncTaskHandle(); + Serializable taskId = asyncTaskHandleManager.registerTaskHandle(handle); + documentServiceImpl.saveDocumentAsync(projectSlug, iterationSlug, + resource, extensions, copytrans, true, handle); + + return getProcessStatus(taskId.toString()); // TODO Change to return 202 + // Accepted, + // with a url to get the + // progress } @Override @@ -196,33 +183,19 @@ public ProcessStatus startTranslatedDocCreationOrUpdate( final MergeType finalMergeType = mergeType; String name = "TranslatedDocCreationOrUpdate: "+projectSlug+"-"+iterationSlug+"-"+idNoSlash+"-"+locale; - SimpleAsyncTask> task = - new SimpleAsyncTask>(name) { - @Override - public List call() throws Exception { - TranslationService translationServiceImpl = - ServiceLocator.instance().getInstance( - TranslationServiceImpl.class); - - // Translate - List messages = - translationServiceImpl.translateAllInDoc( - projectSlug, iterationSlug, id, locale, - translatedDoc, extensions, - finalMergeType, true); - - return messages; - } - }; - - String taskId = asyncTaskManagerServiceImpl.startTask(task); - return this.getProcessStatus(taskId); + AsyncTaskHandle handle = new AsyncTaskHandle(); + Serializable taskId = asyncTaskHandleManager.registerTaskHandle(handle); + translationServiceImpl.translateAllInDocAsync(projectSlug, + iterationSlug, id, locale, translatedDoc, extensions, + finalMergeType, true, handle); + + return this.getProcessStatus(taskId.toString()); } @Override public ProcessStatus getProcessStatus(String processId) { AsyncTaskHandle handle = - asyncTaskManagerServiceImpl.getHandle(processId); + asyncTaskHandleManager.getHandleByKey(processId); if (handle == null) { throw new NotFoundException("A process was not found for id " @@ -244,7 +217,7 @@ public ProcessStatus getProcessStatus(String processId) { if (handle.isDone()) { Object result = null; try { - result = handle.get(); + result = handle.getResult(); } catch (InterruptedException e) { // The process was forcefully cancelled status.setStatusCode(ProcessStatusCode.Failed); diff --git a/zanata-war/src/main/java/org/zanata/rest/service/CopyTransResourceService.java b/zanata-war/src/main/java/org/zanata/rest/service/CopyTransResourceService.java index 0b2471968f..6f70390edd 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/CopyTransResourceService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/CopyTransResourceService.java @@ -25,7 +25,7 @@ import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.zanata.action.CopyTransManager; -import org.zanata.async.tasks.CopyTransTask; +import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.dao.DocumentDAO; import org.zanata.model.HDocument; import org.zanata.rest.NoSuchEntityException; @@ -81,7 +81,7 @@ public CopyTransStatus getCopyTransStatus(String projectSlug, identity.checkPermission("copy-trans", document.getProjectIteration()); - CopyTransTask.CopyTransTaskHandle processHandle = + CopyTransTaskHandle processHandle = copyTransManager.getCopyTransProcessHandle(document); if (processHandle == null) { diff --git a/zanata-war/src/main/java/org/zanata/rest/service/TranslationMemoryResourceService.java b/zanata-war/src/main/java/org/zanata/rest/service/TranslationMemoryResourceService.java index 83c86ba0f6..f3352a25c1 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/TranslationMemoryResourceService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/TranslationMemoryResourceService.java @@ -21,6 +21,7 @@ package org.zanata.rest.service; import java.io.InputStream; +import java.util.concurrent.Future; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -36,6 +37,10 @@ import org.jboss.seam.annotations.TransactionPropagationType; import org.jboss.seam.annotations.Transactional; import org.jboss.seam.annotations.security.Restrict; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; import org.zanata.common.LocaleId; import org.zanata.dao.TextFlowStreamingDAO; import org.zanata.dao.TransMemoryDAO; @@ -60,6 +65,7 @@ @Path(TranslationMemoryResource.SERVICE_PATH) @Transactional(TransactionPropagationType.SUPPORTS) @Slf4j +@ContainsAsyncMethods @ParametersAreNonnullByDefault // TODO options to export obsolete docs and textflows to TMX? public class TranslationMemoryResourceService implements @@ -206,6 +212,21 @@ public Object deleteTranslationUnitsUnguarded(String slug) { } } + /** + * Deletes without checking security permissions and asynchronously. + * + * @param slug + * @return + */ + @Async + public Future deleteTranslationUnitsUnguardedAsync(String slug, + AsyncTaskHandle handle) { + // TODO the handle is not being used for progress tracking in the + // current implementation + return AsyncTaskResult + .taskResult(deleteTranslationUnitsUnguarded(slug)); + } + private Lock lockTM(String slug) { Lock tmLock = new Lock("tm", slug); String owner = lockManagerServiceImpl.attainLockOrReturnOwner(tmLock); diff --git a/zanata-war/src/main/java/org/zanata/search/AbstractIndexingStrategy.java b/zanata-war/src/main/java/org/zanata/search/AbstractIndexingStrategy.java index edad444c38..19cab620dc 100644 --- a/zanata-war/src/main/java/org/zanata/search/AbstractIndexingStrategy.java +++ b/zanata-war/src/main/java/org/zanata/search/AbstractIndexingStrategy.java @@ -2,9 +2,9 @@ import org.hibernate.ScrollableResults; import org.hibernate.search.FullTextSession; -import org.zanata.async.AsyncTaskHandle; import lombok.extern.slf4j.Slf4j; +import org.zanata.async.AsyncTaskHandle; /** * Base indexing strategy. diff --git a/zanata-war/src/main/java/org/zanata/service/AsyncTaskManagerService.java b/zanata-war/src/main/java/org/zanata/service/AsyncTaskManagerService.java deleted file mode 100644 index 511a3249ba..0000000000 --- a/zanata-war/src/main/java/org/zanata/service/AsyncTaskManagerService.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.service; - -import java.io.Serializable; -import java.util.Collection; - -import org.zanata.async.AsyncTask; -import org.zanata.async.AsyncTaskHandle; - -/** - * The Task manager Service offers a central location where tasks running - * asynchronously can be started, and queried. - * - * @author Carlos Munoz camunoz@redhat.com - */ -public interface AsyncTaskManagerService { - /** - * Starts a task asynchronously. - * - * @param task - * The asynchronous task to run. - * @return The key by which the handle for the task may be retrieved. - */ - > String startTask(AsyncTask task); - - /** - * Starts a task asynchronously while providing a key to store the task's - * handle. The handle may be retrieved later using the provided key. - * - * @param task - * The asynchronous task to run. - * @param key - * The key that may be used to retrieve the task handle later. - */ - > void startTask(AsyncTask task, - Serializable key); - - /** - * Retrieves a task handle. - * - * @param taskId - * The task Id (as returned by - * {@link AsyncTaskManagerService#startTask(org.zanata.async.AsyncTask)} - * ) - * @return A task handle, or null if the task with said Id is not being - * handled by this service. - */ - AsyncTaskHandle getHandle(String taskId); - - /** - * Retrieves a task handle. - * - * @param key - * The task id as provided to - * {@link AsyncTaskManagerService#startTask(org.zanata.async.AsyncTask, java.io.Serializable)} - * @return A task handle, or null if there is no task with the provided id - * being handled by this service. - */ - AsyncTaskHandle getHandleByKey(Serializable key); - - /** - * @return All handles for all tasks being managed by this service. - */ - Collection getAllHandles(); - -} diff --git a/zanata-war/src/main/java/org/zanata/service/CopyTransService.java b/zanata-war/src/main/java/org/zanata/service/CopyTransService.java index 3beb528cd9..2b5f8e91d0 100644 --- a/zanata-war/src/main/java/org/zanata/service/CopyTransService.java +++ b/zanata-war/src/main/java/org/zanata/service/CopyTransService.java @@ -20,11 +20,14 @@ */ package org.zanata.service; +import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.model.HCopyTransOptions; import org.zanata.model.HDocument; -import org.zanata.model.HLocale; import org.zanata.model.HProjectIteration; +import javax.validation.constraints.NotNull; +import java.util.concurrent.Future; + public interface CopyTransService { /** * Copies previous matching translations for all available locales into a @@ -40,20 +43,47 @@ public interface CopyTransService { * @param document * the document to copy translations into */ - void copyTransForDocument(HDocument document); + void copyTransForDocument(HDocument document, CopyTransTaskHandle handle); /** * Similar to - * {@link CopyTransService#copyTransForDocument(org.zanata.model.HDocument)} + * {@link CopyTransService#copyTransForDocument(org.zanata.model.HDocument, org.zanata.async.handle.CopyTransTaskHandle)} * , with custom copy trans options. * * @param document * the document to copy translations into * @param copyTransOptions * The copy Trans options to use. + * @param handle + * Optional Task handle to track progress for the operation. */ void copyTransForDocument(HDocument document, - HCopyTransOptions copyTransOptions); + HCopyTransOptions copyTransOptions, CopyTransTaskHandle handle); + + /** + * + * @param document + * the document to copy translations into + * @param copyTransOptions + * The copy Trans options to use. + * @param handle + * Optional Task handle to track progress for the operation. + */ + Future startCopyTransForDocument(HDocument document, + HCopyTransOptions copyTransOptions, CopyTransTaskHandle handle); + + /** + * + * + * @param iteration + * The project iteration to copy translations into + * @param copyTransOptions + * The copy Trans options to use. + * @param handle Task handle to track progress for the operation. + */ + Future startCopyTransForIteration(HProjectIteration iteration, + HCopyTransOptions copyTransOptions, + @NotNull CopyTransTaskHandle handle); /** * Copies previous matching translations for all available locales and @@ -69,7 +99,8 @@ void copyTransForDocument(HDocument document, * The project iteration to copy translations into * @param copyTransOptions * The copy Trans options to use. + * @param handle Optional Task handle to track progress for the operation. */ void copyTransForIteration(HProjectIteration iteration, - HCopyTransOptions copyTransOptions); + HCopyTransOptions copyTransOptions, CopyTransTaskHandle handle); } diff --git a/zanata-war/src/main/java/org/zanata/service/CopyVersionService.java b/zanata-war/src/main/java/org/zanata/service/CopyVersionService.java index 07294a02ae..84ef32ad5d 100644 --- a/zanata-war/src/main/java/org/zanata/service/CopyVersionService.java +++ b/zanata-war/src/main/java/org/zanata/service/CopyVersionService.java @@ -22,12 +22,15 @@ import javax.annotation.Nonnull; +import org.zanata.async.handle.CopyVersionTaskHandle; import org.zanata.model.HDocument; import org.zanata.model.HProjectIteration; import org.zanata.model.HRawDocument; import org.zanata.model.HTextFlow; import org.zanata.model.HTextFlowTarget; +import java.util.concurrent.Future; + public interface CopyVersionService { //@formatter:off /** @@ -45,7 +48,26 @@ public interface CopyVersionService { */ //@formatter:on void copyVersion(String projectSlug, String versionSlug, - String newVersionSlug); + String newVersionSlug, CopyVersionTaskHandle handle); + + //@formatter:off + /** + * Starts a background version copy. + * + * 1) Copy version settings + * 2) Copy HDocument (in batch) + * 3) Copy textFlow for each of copied document (in batch) + * 4) Copy textFlowTarget for each of copied textFlow (in batch) + * + * @param projectSlug + * @param versionSlug + * @param newVersionSlug + * + * + */ + //@formatter:on + Future startCopyVersion(String projectSlug, String versionSlug, + String newVersionSlug, CopyVersionTaskHandle handle); /** * Return total count of HDocument in HProjectIteration diff --git a/zanata-war/src/main/java/org/zanata/service/DocumentService.java b/zanata-war/src/main/java/org/zanata/service/DocumentService.java index f6378922cb..570638452c 100644 --- a/zanata-war/src/main/java/org/zanata/service/DocumentService.java +++ b/zanata-war/src/main/java/org/zanata/service/DocumentService.java @@ -20,10 +20,12 @@ */ package org.zanata.service; +import org.zanata.async.AsyncTaskHandle; import org.zanata.model.HDocument; import org.zanata.rest.dto.resource.Resource; import java.util.Set; +import java.util.concurrent.Future; /** * @author Carlos Munoz extensions, boolean copyTrans); + /** + * Creates or updates a document asynchronously. The process will be started + * in a different thread. + * + * @param projectSlug + * @param iterationSlug + * @param sourceDoc + * @param extensions + * @param copyTrans + * @param lock + * @return A future object that will eventually contain the result of the + * document save. + * @see {@link org.zanata.service.DocumentService#saveDocument(String, String, org.zanata.rest.dto.resource.Resource, java.util.Set, boolean, boolean)} + */ + public Future saveDocumentAsync(String projectSlug, + String iterationSlug, + Resource sourceDoc, Set extensions, boolean copyTrans, + boolean lock, AsyncTaskHandle handle); + /** * Makes a document obsolete. * diff --git a/zanata-war/src/main/java/org/zanata/service/IndexingService.java b/zanata-war/src/main/java/org/zanata/service/IndexingService.java new file mode 100644 index 0000000000..692179feb6 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/service/IndexingService.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.service; + +import org.zanata.action.ReindexClassOptions; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskHandle; + +import java.util.Map; +import java.util.concurrent.Future; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +public interface IndexingService { + @Async + Future startIndexing( + Map, ReindexClassOptions> indexingOptions, + AsyncTaskHandle handle) + throws Exception; +} diff --git a/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java b/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java index dff47658fe..e437390596 100644 --- a/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java +++ b/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java @@ -5,15 +5,10 @@ import java.util.LinkedHashMap; import java.util.List; -import javax.annotation.Nonnull; import javax.persistence.EntityManagerFactory; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.hibernate.Session; -import org.hibernate.search.FullTextSession; -import org.hibernate.search.Search; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.In; @@ -23,8 +18,8 @@ import org.jboss.seam.annotations.Synchronized; import org.zanata.ServerConstants; import org.zanata.action.ReindexClassOptions; -import org.zanata.async.AsyncTask; -import org.zanata.async.TimedAsyncHandle; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskHandleManager; import org.zanata.model.HAccount; import org.zanata.model.HGlossaryEntry; import org.zanata.model.HGlossaryTerm; @@ -32,10 +27,6 @@ import org.zanata.model.HProjectIteration; import org.zanata.model.HTextFlowTarget; import org.zanata.model.tm.TransMemoryUnit; -import org.zanata.search.AbstractIndexingStrategy; -import org.zanata.search.ClassIndexer; -import org.zanata.search.HTextFlowTargetIndexingStrategy; -import org.zanata.search.SimpleClassIndexingStrategy; @Name("searchIndexManager") @Scope(ScopeType.APPLICATION) @@ -49,7 +40,10 @@ public class SearchIndexManager implements Serializable { EntityManagerFactory entityManagerFactory; @In - AsyncTaskManagerService asyncTaskManagerServiceImpl; + AsyncTaskHandleManager asyncTaskHandleManager; + + @In + IndexingService indexingServiceImpl; // we use a list to ensure predictable order private final List> indexables = new ArrayList>(); @@ -57,7 +51,7 @@ public class SearchIndexManager implements Serializable { new LinkedHashMap, ReindexClassOptions>(); private Class currentClass; - private TimedAsyncHandle handle; + private AsyncTaskHandle handle; @Create public void create() { @@ -113,7 +107,7 @@ public List getReindexOptions() { return result; } - public TimedAsyncHandle getProcessHandle() { + public AsyncTaskHandle getProcessHandle() { return handle; } @@ -130,144 +124,21 @@ public String getCurrentClassName() { */ public void startProcess() { assert handle == null || handle.isDone(); - final ReindexTask reindexTask = new ReindexTask(entityManagerFactory); - String taskId = - asyncTaskManagerServiceImpl.startTask(reindexTask); - this.handle = (TimedAsyncHandle) asyncTaskManagerServiceImpl.getHandle(taskId); + this.handle = new AsyncTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(handle); + try { + indexingServiceImpl.startIndexing(indexingOptions, handle); + } + catch (Exception e) { + // If this happens, it's because of a problem with the async + // framework + throw new RuntimeException(e); + } } public void reindex(boolean purge, boolean reindex, boolean optimize) throws Exception { setOptions(purge, reindex, optimize); - new ReindexTask(entityManagerFactory).call(); - } - - /** - * Private reindex Asynchronous task. NB: Separate from the main Bean class - * as it is not recommended to reuse async tasks. - */ - private class ReindexTask implements - AsyncTask> { - private final EntityManagerFactory entityManagerFactory; - - @Getter - @Nonnull - private final TimedAsyncHandle handle; - - public ReindexTask(EntityManagerFactory entityManagerFactory) { - this.entityManagerFactory = entityManagerFactory; - String name = getClass().getSimpleName(); // +":"+indexingOptions - this.handle = new TimedAsyncHandle(name); - FullTextSession session = openFullTextSession(); - try { - handle.setMaxProgress(getTotalOperations(session)); - } finally { - session.close(); - } - } - - private FullTextSession openFullTextSession() { - return Search.getFullTextSession(entityManagerFactory - .createEntityManager().unwrap(Session.class)); - } - - /** - * Returns the number of total operations to perform - */ - private int getTotalOperations(FullTextSession session) { - // set up progress counter - int totalOperations = 0; - for (Class clazz : indexables) { - ReindexClassOptions opts = indexingOptions.get(clazz); - if (opts.isPurge()) { - totalOperations++; - } - - if (opts.isReindex()) { - totalOperations += getIndexer(clazz).getEntityCount(session); - } - - if (opts.isOptimize()) { - totalOperations++; - } - } - return totalOperations; - } - - private ClassIndexer getIndexer(Class clazz) { - AbstractIndexingStrategy strategy; - // TODO add a strategy which uses TransMemoryStreamingDAO - if (clazz.equals(HTextFlowTarget.class)) { - strategy = - (AbstractIndexingStrategy) new HTextFlowTargetIndexingStrategy(); - } else { - strategy = new SimpleClassIndexingStrategy(clazz); - } - return new ClassIndexer(handle, clazz, strategy); - } - - @Override - public Void call() throws Exception { - FullTextSession session = openFullTextSession(); - try { - handle.setMaxProgress(getTotalOperations(session)); - // TODO this is necessary because isInProgress checks number of - // operations, which may be 0 - // look at updating isInProgress not to care about count - if (getHandle().getMaxProgress() == 0) { - log.info("Reindexing aborted because there are no actions " - + "to perform (may be indexing an empty table)"); - return null; - } - getHandle().startTiming(); - for (Class clazz : indexables) { - if (!getHandle().isCancelled() - && indexingOptions.get(clazz).isPurge()) { - log.info("purging index for {}", clazz); - currentClass = clazz; - session.purgeAll(clazz); - getHandle().increaseProgress(1); - } - if (!getHandle().isCancelled() - && indexingOptions.get(clazz).isReindex()) { - log.info("reindexing {}", clazz); - currentClass = clazz; - getIndexer(clazz).index(session); - } - if (!getHandle().isCancelled() - && indexingOptions.get(clazz).isOptimize()) { - log.info("optimizing {}", clazz); - currentClass = clazz; - session.getSearchFactory().optimize(clazz); - getHandle().increaseProgress(1); - } - } - - if (getHandle().isCancelled()) { - log.info("index operation canceled by user"); - } else { - if (getHandle().getCurrentProgress() != getHandle() - .getMaxProgress()) { - // @formatter: off - log.warn( - "Did not reindex the expected number of " - + "objects. Counted {} but indexed {}. " - + "The index may be out-of-sync. " - + "This may be caused by lack of " - + "sufficient memory, or by database " - + "activity during reindexing.", - getHandle().getMaxProgress(), getHandle() - .getCurrentProgress()); - // @formatter: on - } - - log.info("Re-indexing finished"); - } - getHandle().finishTiming(); - return null; - } finally { - session.close(); - } - } + startProcess(); } } diff --git a/zanata-war/src/main/java/org/zanata/service/TranslationArchiveService.java b/zanata-war/src/main/java/org/zanata/service/TranslationArchiveService.java new file mode 100644 index 0000000000..38720403bd --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/service/TranslationArchiveService.java @@ -0,0 +1,76 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.service; + +import java.util.concurrent.Future; + +import javax.annotation.Nonnull; + +import org.zanata.async.AsyncTaskHandle; + +/** + * This service deals with the archiving / unarchiving of bundles that may + * contain multiple translation files. + * + * @author Carlos Munoz camunoz@redhat.com + */ +public interface TranslationArchiveService { + + /** + * Builds an archive file with translation files for a given project + * iteration in a given language. The contents may be altered depending on + * the project type. + * + * @param projectSlug + * @param iterationSlug + * @param localeId + * @param userName + * @param handle + * @return A file identifier + */ + String buildTranslationFileArchive(@Nonnull String projectSlug, + @Nonnull String iterationSlug, + @Nonnull String localeId, @Nonnull String userName, + AsyncTaskHandle handle) + throws Exception; + + /** + * Asynchronously starts building a project archive. Should have the same + * result as + * {@link org.zanata.service.TranslationArchiveService#buildTranslationFileArchive(String, String, String, String, org.zanata.async.AsyncTaskHandle)} + * but performed in the background. + * + * @param projectSlug + * @param iterationSlug + * @param localeId + * @param userName + * @param handle + * @return + * @see org.zanata.service.TranslationArchiveService#buildTranslationFileArchive(String, + * String, String, String, org.zanata.async.AsyncTaskHandle) + */ + Future startBuildingTranslationFileArchive( + @Nonnull String projectSlug, + @Nonnull String iterationSlug, @Nonnull String localeId, + @Nonnull String userName, + AsyncTaskHandle handle) throws Exception; +} diff --git a/zanata-war/src/main/java/org/zanata/service/TranslationService.java b/zanata-war/src/main/java/org/zanata/service/TranslationService.java index 99b316b163..0243a9a971 100644 --- a/zanata-war/src/main/java/org/zanata/service/TranslationService.java +++ b/zanata-war/src/main/java/org/zanata/service/TranslationService.java @@ -22,7 +22,9 @@ import java.util.List; import java.util.Set; +import java.util.concurrent.Future; +import org.zanata.async.AsyncTaskHandle; import org.zanata.common.ContentState; import org.zanata.common.LocaleId; import org.zanata.common.MergeType; @@ -69,8 +71,9 @@ List revertTranslations(LocaleId localeId, List translationsToRevert); /** - * Translates all text flows in a document. This method is intended to be - * called using a {@link org.zanata.process.RunnableProcess}. + * Translates all text flows in a document. Implementations of this method + * should run in a separate thread and use the returned + * {@link java.util.concurrent.Future} to return a value. * * @param projectSlug * The project to translate @@ -92,14 +95,15 @@ List revertTranslations(LocaleId localeId, * If true, no other caller will be allowed to translate All for * the same project, iteration, document and locale. * @see TranslationService#translateAllInDoc(String, String, String, - * org.zanata.common.LocaleId, - * org.zanata.rest.dto.resource.TranslationsResource, java.util.Set, - * org.zanata.common.MergeType) + * org.zanata.common.LocaleId, org.zanata.rest.dto.resource.TranslationsResource, + * java.util.Set, org.zanata.common.MergeType, + * org.zanata.async.AsyncTaskHandle) */ - public List translateAllInDoc(String projectSlug, + public Future> translateAllInDocAsync(String projectSlug, String iterationSlug, String docId, LocaleId locale, TranslationsResource translations, Set extensions, - MergeType mergeType, boolean lock); + MergeType mergeType, boolean lock, + AsyncTaskHandle handle); /** * Translates all text flows in a document. @@ -124,6 +128,10 @@ public List translateAllInDoc(String projectSlug, * matched to any text flows in the source document or (b) whose * states don't match their contents. */ + List translateAllInDoc(String projectSlug, String iterationSlug, + String docId, LocaleId locale, TranslationsResource translations, + Set extensions, MergeType mergeType, AsyncTaskHandle handle); + List translateAllInDoc(String projectSlug, String iterationSlug, String docId, LocaleId locale, TranslationsResource translations, Set extensions, MergeType mergeType); diff --git a/zanata-war/src/main/java/org/zanata/service/impl/AsyncTaskManagerServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/AsyncTaskManagerServiceImpl.java deleted file mode 100644 index 947862e2e3..0000000000 --- a/zanata-war/src/main/java/org/zanata/service/impl/AsyncTaskManagerServiceImpl.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.service.impl; - -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.ScopeType; -import org.jboss.seam.annotations.Name; -import org.jboss.seam.annotations.Scope; -import org.jboss.seam.annotations.Startup; -import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.AsyncTask; -import org.zanata.async.TaskExecutor; -import org.zanata.service.AsyncTaskManagerService; -import org.zanata.util.ServiceLocator; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * Default Implementation of an Asynchronous task manager service. - * - * This replaces the now deprecated ProcessManagerService. - * - * @author Carlos Munoz camunoz@redhat.com - */ -@Name("asyncTaskManagerServiceImpl") -@Scope(ScopeType.APPLICATION) -@Startup -@Slf4j -public class AsyncTaskManagerServiceImpl implements AsyncTaskManagerService { - - // Map of all active task handles - private Map handlesByKey = Maps.newConcurrentMap(); - - // Cache of recently completed tasks - private Cache 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 - * @param - * @return - */ - @Override - public > String startTask( - AsyncTask task) { - 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 - * @param - */ - @Override - public > void startTask( - AsyncTask task, final Serializable key) { - TaskExecutor taskExecutor = - ServiceLocator.instance().getInstance(TaskExecutor.class); - AsyncTaskHandle 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); - 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) { - // 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 getAllHandles() { - Collection handles = Lists.newArrayList(); - handles.addAll(handlesByKey.values()); - handles.addAll(finishedTasks.asMap().values()); - return handles; - } - - private synchronized long generateNextAvailableKey() { - return lastAssignedKey++; - } - -} diff --git a/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java index 5c0fb26a3e..ca8f71edef 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java @@ -21,13 +21,16 @@ package org.zanata.service.impl; import java.util.List; +import java.util.concurrent.Future; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; -import org.zanata.async.AsyncUtils; -import org.zanata.async.tasks.CopyTransTask.CopyTransTaskHandle; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; +import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.dao.DocumentDAO; import org.zanata.dao.ProjectDAO; import org.zanata.dao.TextFlowDAO; @@ -48,9 +51,12 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import javax.validation.constraints.NotNull; + @Name("copyTransServiceImpl") @Scope(ScopeType.STATELESS) @Slf4j +@ContainsAsyncMethods @AllArgsConstructor @NoArgsConstructor public class CopyTransServiceImpl implements CopyTransService { @@ -69,6 +75,8 @@ public class CopyTransServiceImpl implements CopyTransService { private TextFlowTargetDAO textFlowTargetDAO; @In private TranslationStateCache translationStateCacheImpl; + @In + private TextFlowDAO textFlowDAO; /** * Copies previous matching translations for the given locale into a @@ -193,13 +201,20 @@ private int copyTransForBatch(HDocument document, final int batchStart, } @Override - public void copyTransForDocument(HDocument document) { - copyTransForDocument(document, null); + public void copyTransForDocument(HDocument document, CopyTransTaskHandle handle) { + copyTransForDocument(document, null, handle); } @Override public void copyTransForDocument(HDocument document, - HCopyTransOptions copyTransOpts) { + HCopyTransOptions copyTransOpts, CopyTransTaskHandle handle) { + + Optional taskHandleOpt = + Optional.fromNullable(handle); + if( taskHandleOpt.isPresent() ) { + prepareCopyTransHandle(document, taskHandleOpt.get()); + } + // use project level options if (copyTransOpts == null) { // NB: Need to reload the options from the db @@ -216,8 +231,6 @@ public void copyTransForDocument(HDocument document, } log.info("copyTrans start: document \"{}\"", document.getDocId()); - Optional taskHandleOpt = - AsyncUtils.getEventAsyncHandle(CopyTransTaskHandle.class); List localeList = localeServiceImpl.getSupportedLanguageByProjectIteration( document.getProjectIteration().getProject().getSlug(), @@ -233,11 +246,32 @@ public void copyTransForDocument(HDocument document, log.info("copyTrans finished: document \"{}\"", document.getDocId()); } + @Override + @Async + public Future startCopyTransForDocument(HDocument document, + HCopyTransOptions copyTransOptions, CopyTransTaskHandle handle) { + copyTransForDocument(document, copyTransOptions, handle); + return AsyncTaskResult.taskResult(); + } + + @Override + @Async + public Future startCopyTransForIteration(HProjectIteration iteration, + HCopyTransOptions copyTransOptions, CopyTransTaskHandle handle) { + copyTransForIteration(iteration, copyTransOptions, handle); + return AsyncTaskResult.taskResult(); + } + @Override public void copyTransForIteration(HProjectIteration iteration, - HCopyTransOptions copyTransOptions) { + HCopyTransOptions copyTransOptions, + @NotNull CopyTransTaskHandle handle) { Optional taskHandleOpt = - AsyncUtils.getEventAsyncHandle(CopyTransTaskHandle.class); + Optional.fromNullable(handle); + + if( taskHandleOpt.isPresent() ) { + prepareCopyTransHandle(iteration, taskHandleOpt.get()); + } for (HDocument doc : iteration.getDocuments().values()) { if (taskHandleOpt.isPresent() && taskHandleOpt.get().isCancelled()) { @@ -251,15 +285,57 @@ public void copyTransForIteration(HProjectIteration iteration, iteration.getProject().getId()); if (copyCandidates < 2) { if (taskHandleOpt.isPresent()) { - CopyTransTaskHandle taskHandle = taskHandleOpt.get(); - taskHandleOpt.get() - .increaseProgress(taskHandle.getMaxProgress()); + taskHandleOpt.get().increaseProgress( + taskHandleOpt.get().getMaxProgress()); } return; } } - this.copyTransForDocument(doc, copyTransOptions); + this.copyTransForDocument(doc, copyTransOptions, handle); } } + private void prepareCopyTransHandle( HProjectIteration iteration, CopyTransTaskHandle handle ) { + if( !handle.isPrepared() ) { + // TODO Progress should be handle as long + handle.setMaxProgress( (int)getMaxProgress(iteration) ); + handle.setPrepared(); + } + } + + private void prepareCopyTransHandle( HDocument document, CopyTransTaskHandle handle ) { + if( !handle.isPrepared() ) { + // TODO Progress should be handle as long + handle.setMaxProgress( (int)getMaxProgress(document) ); + handle.setPrepared(); + } + } + + private long getMaxProgress(HProjectIteration iteration) { + log.debug("counting locales"); + List localeList = + localeServiceImpl.getSupportedLanguageByProjectIteration( + iteration.getProject().getSlug(), + iteration.getSlug()); + int localeCount = localeList.size(); + log.debug("counting locales finished"); + log.debug("counting textflows"); + long textFlowCount = textFlowDAO.countActiveTextFlowsInProjectIteration( + iteration.getId()); + log.debug("counting textflows finished"); + return localeCount * textFlowCount; + } + + private long getMaxProgress(HDocument document) { + List localeList = + localeServiceImpl.getSupportedLanguageByProjectIteration(document + .getProjectIteration().getProject().getSlug(), document + .getProjectIteration().getSlug()); + int localeCount = localeList.size(); + + int textFlowCount = + textFlowDAO.countActiveTextFlowsInDocument(document.getId()); + return localeCount * textFlowCount; + } + } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java index e96e4b1dec..1a4cb0fd10 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java @@ -9,8 +9,10 @@ import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; -import org.zanata.async.AsyncUtils; -import org.zanata.async.tasks.CopyVersionTask; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; +import org.zanata.async.handle.CopyVersionTaskHandle; import org.zanata.dao.DocumentDAO; import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.TextFlowDAO; @@ -27,6 +29,7 @@ import org.zanata.model.po.HPoHeader; import org.zanata.model.po.HPoTargetHeader; import org.zanata.model.po.HPotEntryData; +import org.zanata.security.ZanataIdentity; import org.zanata.service.CopyVersionService; import org.zanata.service.VersionStateCache; import org.zanata.util.JPACopier; @@ -34,6 +37,7 @@ import javax.annotation.Nonnull; import java.util.Collections; import java.util.Map; +import java.util.concurrent.Future; /** * @author Alex Eng aeng@redhat.com @@ -42,6 +46,7 @@ @Scope(ScopeType.STATELESS) @Slf4j @AutoCreate +@ContainsAsyncMethods public class CopyVersionServiceImpl implements CopyVersionService { // Document batch size @@ -71,19 +76,30 @@ public class CopyVersionServiceImpl implements CopyVersionService { @In private FilePersistService filePersistService; + @In + private ZanataIdentity identity; + // Stop watch for textFlow and target copy process private Stopwatch copyTfAndTftStopWatch = new Stopwatch(); @Override public void copyVersion(@Nonnull String projectSlug, - @Nonnull String versionSlug, @Nonnull String newVersionSlug) { + @Nonnull String versionSlug, @Nonnull String newVersionSlug, + CopyVersionTaskHandle handle) { + Optional taskHandleOpt = + Optional.fromNullable(handle); + HProjectIteration version = + projectIterationDAO.getBySlug(projectSlug, versionSlug); + + if( taskHandleOpt.isPresent() ) { + prepareCopyVersionHandle(version, taskHandleOpt.get()); + } + Stopwatch overallStopwatch = new Stopwatch().start(); log.info("copy version start: copy {} to {}", projectSlug + ":" + versionSlug, projectSlug + ":" + newVersionSlug); - HProjectIteration version = - projectIterationDAO.getBySlug(projectSlug, versionSlug); if (version == null) { log.error("Cannot find project iteration of {}:{}", projectSlug, versionSlug); @@ -98,10 +114,6 @@ public void copyVersion(@Nonnull String projectSlug, newVersion = projectIterationDAO.makePersistent(newVersion); // Copy of HDocument - Optional taskHandleOpt = - AsyncUtils.getEventAsyncHandle( - CopyVersionTask.CopyVersionTaskHandle.class); - int docSize = documentDAO.getDocCountByVersion(projectSlug, versionSlug); @@ -137,6 +149,25 @@ public void copyVersion(@Nonnull String projectSlug, overallStopwatch); } + @Override + @Async + public Future startCopyVersion(@Nonnull String projectSlug, + @Nonnull String versionSlug, + @Nonnull String newVersionSlug, CopyVersionTaskHandle handle) { + copyVersion(projectSlug, versionSlug, newVersionSlug, handle); + return AsyncTaskResult.taskResult(); + } + + private void prepareCopyVersionHandle(HProjectIteration originalVersion, + CopyVersionTaskHandle handle) { + handle.setTriggeredBy(identity.getAccountUsername()); + int totalDocCount = + getTotalDocCount(originalVersion.getProject().getSlug(), + originalVersion.getSlug()); + handle.setMaxProgress(totalDocCount); + handle.setTotalDoc(totalDocCount); + } + @Override public int getTotalDocCount(@Nonnull String projectSlug, @Nonnull String versionSlug) { diff --git a/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java index efcaae6524..970e7cae8e 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java @@ -21,6 +21,7 @@ package org.zanata.service.impl; import java.util.Set; +import java.util.concurrent.Future; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; @@ -30,6 +31,10 @@ import org.jboss.seam.core.Events; import org.jboss.seam.security.management.JpaIdentityStore; import org.zanata.ApplicationConfiguration; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; import org.zanata.dao.DocumentDAO; import org.zanata.dao.ProjectIterationDAO; import org.zanata.events.DocumentUploadedEvent; @@ -57,6 +62,7 @@ */ @Name("documentServiceImpl") @Scope(ScopeType.STATELESS) +@ContainsAsyncMethods public class DocumentServiceImpl implements DocumentService { @In private ZanataIdentity identity; @@ -115,6 +121,17 @@ public HDocument saveDocument(String projectSlug, String iterationSlug, } } + @Override + @Async + @Transactional + public Future saveDocumentAsync(String projectSlug, String iterationSlug, + Resource sourceDoc, Set extensions, boolean copyTrans, + boolean lock, AsyncTaskHandle handle) { + // TODO Use the pased in handle + return AsyncTaskResult.taskResult(saveDocument(projectSlug, + iterationSlug, sourceDoc, extensions, copyTrans, lock)); + } + @Override @Transactional public HDocument saveDocument(String projectSlug, String iterationSlug, @@ -198,7 +215,7 @@ public void makeObsolete(HDocument document) { */ private void copyTranslations(HDocument document) { if (applicationConfiguration.isCopyTransEnabled()) { - copyTransServiceImpl.copyTransForDocument(document); + copyTransServiceImpl.copyTransForDocument(document, null); } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java new file mode 100644 index 0000000000..924cac5add --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java @@ -0,0 +1,175 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.service.impl; + +import java.util.Map; +import java.util.concurrent.Future; + +import javax.persistence.EntityManagerFactory; + +import lombok.extern.slf4j.Slf4j; + +import org.hibernate.Session; +import org.hibernate.search.FullTextSession; +import org.hibernate.search.Search; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.zanata.action.ReindexClassOptions; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; +import org.zanata.model.HTextFlowTarget; +import org.zanata.search.AbstractIndexingStrategy; +import org.zanata.search.ClassIndexer; +import org.zanata.search.HTextFlowTargetIndexingStrategy; +import org.zanata.search.SimpleClassIndexingStrategy; +import org.zanata.service.IndexingService; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +@Name("indexingServiceImpl") +@Scope(ScopeType.STATELESS) +@AutoCreate +@Slf4j +@ContainsAsyncMethods +public class IndexingServiceImpl implements IndexingService { + + @In + private EntityManagerFactory entityManagerFactory; + + @Override + @Async + public Future startIndexing( + Map, ReindexClassOptions> indexingOptions, + AsyncTaskHandle handle) + throws Exception { + FullTextSession session = openFullTextSession(); + try { + handle.setMaxProgress(getTotalOperations(session, indexingOptions, + handle)); + // TODO this is necessary because isInProgress checks number of + // operations, which may be 0 + // look at updating isInProgress not to care about count + if (handle.getMaxProgress() == 0) { + log.info("Reindexing aborted because there are no actions " + + "to perform (may be indexing an empty table)"); + return AsyncTaskResult.taskResult(); + } + for (Class clazz : indexingOptions.keySet()) { + if (!handle.isCancelled() + && indexingOptions.get(clazz).isPurge()) { + log.info("purging index for {}", clazz); + // currentClass = clazz; + session.purgeAll(clazz); + handle.increaseProgress(1); + } + if (!handle.isCancelled() + && indexingOptions.get(clazz).isReindex()) { + log.info("reindexing {}", clazz); + // currentClass = clazz; + getIndexer(clazz, handle).index(session); + } + if (!handle.isCancelled() + && indexingOptions.get(clazz).isOptimize()) { + log.info("optimizing {}", clazz); + // currentClass = clazz; + session.getSearchFactory().optimize(clazz); + handle.increaseProgress(1); + } + } + + if (handle.isCancelled()) { + log.info("index operation canceled by user"); + } else { + if (handle.getCurrentProgress() != handle + .getMaxProgress()) { + // @formatter: off + log.warn( + "Did not reindex the expected number of " + + "objects. Counted {} but indexed {}. " + + "The index may be out-of-sync. " + + "This may be caused by lack of " + + "sufficient memory, or by database " + + "activity during reindexing.", + handle.getMaxProgress(), handle + .getCurrentProgress()); + // @formatter: on + } + + log.info("Re-indexing finished"); + } + } finally { + session.close(); + } + return AsyncTaskResult.taskResult(); + } + + private FullTextSession openFullTextSession() { + return Search.getFullTextSession(entityManagerFactory + .createEntityManager().unwrap(Session.class)); + } + + /** + * Returns the number of total operations to perform + */ + private int getTotalOperations(FullTextSession session, + Map, ReindexClassOptions> indexingOptions, + AsyncTaskHandle handle) { + // set up progress counter + int totalOperations = 0; + for (Class clazz : indexingOptions.keySet()) { + ReindexClassOptions opts = indexingOptions.get(clazz); + if (opts.isPurge()) { + totalOperations++; + } + + if (opts.isReindex()) { + totalOperations += + getIndexer(clazz, handle).getEntityCount(session); + } + + if (opts.isOptimize()) { + totalOperations++; + } + } + return totalOperations; + } + + private ClassIndexer getIndexer(Class clazz, + AsyncTaskHandle handle) { + AbstractIndexingStrategy strategy; + // TODO add a strategy which uses TransMemoryStreamingDAO + if (clazz.equals(HTextFlowTarget.class)) { + strategy = + (AbstractIndexingStrategy) new HTextFlowTargetIndexingStrategy(); + } else { + strategy = new SimpleClassIndexingStrategy(clazz); + } + return new ClassIndexer(handle, clazz, strategy); + } + +} diff --git a/zanata-war/src/main/java/org/zanata/async/tasks/ZipFileBuildTask.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java similarity index 56% rename from zanata-war/src/main/java/org/zanata/async/tasks/ZipFileBuildTask.java rename to zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java index c71c9fa187..9c70bebf6f 100644 --- a/zanata-war/src/main/java/org/zanata/async/tasks/ZipFileBuildTask.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the * @author tags. See the copyright.txt file in the distribution for a full * listing of individual contributors. * @@ -18,111 +18,94 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF * site: http://www.fsf.org. */ -package org.zanata.async.tasks; - -import java.io.File; -import java.io.FileOutputStream; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; +package org.zanata.service.impl; +import com.google.common.base.Optional; +import lombok.extern.slf4j.Slf4j; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; import org.zanata.adapter.po.PoWriter2; -import org.zanata.async.AsyncTask; +import org.zanata.async.Async; import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; import org.zanata.common.LocaleId; +import org.zanata.common.ProjectType; import org.zanata.dao.DocumentDAO; import org.zanata.dao.LocaleDAO; +import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.TextFlowTargetDAO; import org.zanata.model.HDocument; import org.zanata.model.HLocale; +import org.zanata.model.HProjectIteration; import org.zanata.model.HTextFlowTarget; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.TranslationsResource; import org.zanata.rest.service.ResourceUtils; import org.zanata.service.ConfigurationService; import org.zanata.service.FileSystemService; -import org.zanata.service.impl.ConfigurationServiceImpl; -import org.zanata.service.impl.FileSystemServiceImpl; +import org.zanata.service.TranslationArchiveService; -import com.google.common.base.Optional; -import org.zanata.util.ServiceLocator; +import java.io.File; +import java.io.FileOutputStream; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; -import javax.annotation.Nonnull; +import static org.zanata.common.ProjectType.*; /** - * This is an asynchronous task to build a zip file containing all files for a - * Project Iteration. its expected return value is an input stream with the - * contents of the Zip file. Currently only supports PO files. - * * @author Carlos Munoz camunoz@redhat.com */ -public class ZipFileBuildTask implements - AsyncTask> { - - private final String projectSlug; - private final String iterationSlug; - private final String localeId; - private final String userName; - private final boolean isPoProject; - - private AsyncTaskHandle handle; - - public ZipFileBuildTask(String projectSlug, String iterationSlug, - String localeId, String userName, boolean isPoProject) { - this.projectSlug = projectSlug; - this.iterationSlug = iterationSlug; - this.localeId = localeId; - this.userName = userName; - this.isPoProject = isPoProject; - } +@Name("translationArchiveServiceImpl") +@Scope(ScopeType.STATELESS) +@AutoCreate +@Slf4j +@ContainsAsyncMethods +public class TranslationArchiveServiceImpl implements + TranslationArchiveService { - @Nonnull - @Override - public AsyncTaskHandle getHandle() { - if (handle == null) { - handle = buildHandle(); - } - return handle; - } + @In + private DocumentDAO documentDAO; - private AsyncTaskHandle buildHandle() { - String name = "ZipFileBuildTask: "+projectSlug+"-"+iterationSlug+"-"+localeId; - AsyncTaskHandle newHandle = new AsyncTaskHandle(name); + @In + private LocaleDAO localeDAO; - // Max documents to process - DocumentDAO documentDAO = - ServiceLocator.instance().getInstance(DocumentDAO.class); - final List allIterationDocs = - documentDAO - .getAllByProjectIteration(projectSlug, iterationSlug); - newHandle.setMaxProgress(allIterationDocs.size() + 1); // all files plus - // the zanata.xml - // file + @In + private ProjectIterationDAO projectIterationDAO; - return newHandle; - } + @In + private ResourceUtils resourceUtils; + + @In + private TextFlowTargetDAO textFlowTargetDAO; + + @In + private FileSystemService fileSystemServiceImpl; + + @In + private ConfigurationService configurationServiceImpl; @Override - public String call() throws Exception { - // Needed Components - DocumentDAO documentDAO = - ServiceLocator.instance().getInstance(DocumentDAO.class); - LocaleDAO localeDAO = - ServiceLocator.instance().getInstance(LocaleDAO.class); - ResourceUtils resourceUtils = - ServiceLocator.instance().getInstance(ResourceUtils.class); - TextFlowTargetDAO textFlowTargetDAO = - ServiceLocator.instance().getInstance(TextFlowTargetDAO.class); - FileSystemService fileSystemService = - ServiceLocator.instance().getInstance( - FileSystemServiceImpl.class); - ConfigurationService configurationService = - ServiceLocator.instance().getInstance( - ConfigurationServiceImpl.class); + public String buildTranslationFileArchive(String projectSlug, + String iterationSlug, String localeId, String userName, + AsyncTaskHandle handle) + throws Exception { + + Optional> handleOpt = + Optional.fromNullable(handle); + if( handleOpt.isPresent() ) { + prepareHandle(handleOpt.get(), projectSlug, iterationSlug); + } + boolean isPoProject = isPoProject(projectSlug, iterationSlug); final String projectDirectory = projectSlug + "-" + iterationSlug + "/"; final HLocale hLocale = localeDAO.findByLocaleId(new LocaleId(localeId)); @@ -130,7 +113,7 @@ public String call() throws Exception { final String localeDirectory = projectDirectory + mappedLocale + "/"; final File downloadFile = - fileSystemService.createDownloadStagingFile("zip"); + fileSystemServiceImpl.createDownloadStagingFile("zip"); final FileOutputStream output = new FileOutputStream(downloadFile); final ZipOutputStream zipOutput = new ZipOutputStream(output); zipOutput.setMethod(ZipOutputStream.DEFLATED); @@ -142,29 +125,29 @@ public String call() throws Exception { // Generate the download descriptor file String downloadId = - fileSystemService.createDownloadDescriptorFile(downloadFile, + fileSystemServiceImpl.createDownloadDescriptorFile(downloadFile, projectSlug + "_" + iterationSlug + "_" + localeId + ".zip", userName); // Add the config file at the root of the project directory String configFilename = projectDirectory - + configurationService.getConfigurationFileName(); + + configurationServiceImpl.getConfigurationFileName(); zipOutput.putNextEntry(new ZipEntry(configFilename)); - zipOutput.write(configurationService.getConfigForOfflineTranslation( + zipOutput.write(configurationServiceImpl.getConfigForOfflineTranslation( projectSlug, iterationSlug, hLocale).getBytes()); zipOutput.closeEntry(); - getHandle().increaseProgress(1); + handle.increaseProgress(1); final List allIterationDocs = documentDAO .getAllByProjectIteration(projectSlug, iterationSlug); for (HDocument document : allIterationDocs) { // Stop the process if signaled to do so - if (getHandle().isCancelled()) { + if (handleOpt.isPresent() && handleOpt.get().isCancelled()) { zipOutput.close(); downloadFile.delete(); - fileSystemService.deleteDownloadDescriptorFile(downloadId); + fileSystemServiceImpl.deleteDownloadDescriptorFile(downloadId); return null; } @@ -183,7 +166,9 @@ public String call() throws Exception { poWriter.writePo(zipOutput, "UTF-8", res, translationResource); zipOutput.closeEntry(); - getHandle().increaseProgress(1); + if( handleOpt.isPresent() ) { + handleOpt.get().increaseProgress(1); + } } zipOutput.flush(); @@ -191,4 +176,36 @@ public String call() throws Exception { return downloadId; } + + @Override + @Async + public Future startBuildingTranslationFileArchive(String projectSlug, + String iterationSlug, String localeId, String userName, + AsyncTaskHandle handle) throws Exception { + String archiveId = + buildTranslationFileArchive(projectSlug, iterationSlug, + localeId, userName, handle); + return AsyncTaskResult.taskResult(archiveId); + } + + private void prepareHandle(AsyncTaskHandle handle, + String projectSlug, String iterationSlug) { + // Max documents to process + final List allIterationDocs = + documentDAO + .getAllByProjectIteration(projectSlug, iterationSlug); + handle.setMaxProgress(allIterationDocs.size() + 1); // all files plus + // the zanata.xml + // file + } + + private boolean isPoProject(String projectSlug, String versionSlug) { + HProjectIteration projectIteration = + projectIterationDAO.getBySlug(projectSlug, versionSlug); + ProjectType type = projectIteration.getProjectType(); + if (type == null) { + type = projectIteration.getProject().getDefaultProjectType(); + } + return type == Gettext || type == Podir; + } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java index 7722a7c46e..8eb6b6ea40 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Future; import javax.annotation.Nonnull; import javax.persistence.EntityManager; @@ -46,8 +47,10 @@ import org.jboss.seam.core.Events; import org.jboss.seam.security.management.JpaIdentityStore; import org.jboss.seam.util.Work; +import org.zanata.async.Async; import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.AsyncUtils; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; import org.zanata.common.ContentState; import org.zanata.common.LocaleId; import org.zanata.common.MergeType; @@ -96,6 +99,7 @@ @Name("translationServiceImpl") @Scope(ScopeType.STATELESS) @Transactional +@ContainsAsyncMethods @Slf4j public class TranslationServiceImpl implements TranslationService { @@ -488,12 +492,14 @@ private static boolean ensureContentsSize(HTextFlowTarget target, @Override // This will not run in a transaction. Instead, transactions are controlled // within the method itself. - @Transactional(TransactionPropagationType.NEVER) - public - List translateAllInDoc(String projectSlug, - String iterationSlug, String docId, LocaleId locale, - TranslationsResource translations, Set extensions, - MergeType mergeType, boolean lock) { + @Transactional(TransactionPropagationType.NEVER) + @Async + public + Future> translateAllInDocAsync(String projectSlug, + String iterationSlug, String docId, LocaleId locale, + TranslationsResource translations, Set extensions, + MergeType mergeType, boolean lock, + AsyncTaskHandle handle) { // Lock this document for push Lock transLock = null; if (lock) { @@ -506,13 +512,13 @@ List translateAllInDoc(String projectSlug, try { messages = this.translateAllInDoc(projectSlug, iterationSlug, docId, - locale, translations, extensions, mergeType); + locale, translations, extensions, mergeType, handle); } finally { if (lock) { lockManagerServiceImpl.release(transLock); } } - return messages; + return AsyncTaskResult.taskResult(messages); } /** @@ -555,6 +561,16 @@ public List translateAllInDoc(final String projectSlug, final String iterationSlug, final String docId, final LocaleId locale, final TranslationsResource translations, final Set extensions, final MergeType mergeType) { + return translateAllInDoc(projectSlug, iterationSlug, docId, locale, + translations, extensions, mergeType, null); + } + + @Override + public List translateAllInDoc(final String projectSlug, + final String iterationSlug, final String docId, + final LocaleId locale, final TranslationsResource translations, + final Set extensions, final MergeType mergeType, + AsyncTaskHandle handle) { final HProjectIteration hProjectIteration = projectIterationDAO.getBySlug(projectSlug, iterationSlug); @@ -584,7 +600,7 @@ public List translateAllInDoc(final String projectSlug, localeServiceImpl.validateLocaleByProjectIteration(locale, projectSlug, iterationSlug); final Optional handleOp = - AsyncUtils.getEventAsyncHandle(AsyncTaskHandle.class); + Optional.fromNullable(handle); if (handleOp.isPresent()) { handleOp.get().setMaxProgress( diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java index 1154d5d457..697764cde8 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java @@ -29,15 +29,17 @@ import org.jboss.seam.annotations.Scope; import org.jboss.seam.security.Identity; import org.zanata.async.AsyncTaskHandle; -import org.zanata.async.tasks.ZipFileBuildTask; +import org.zanata.async.AsyncTaskHandleManager; import org.zanata.dao.ProjectIterationDAO; import org.zanata.model.HProjectIteration; import org.zanata.security.ZanataIdentity; -import org.zanata.service.AsyncTaskManagerService; +import org.zanata.service.TranslationArchiveService; import org.zanata.webtrans.server.ActionHandlerFor; import org.zanata.webtrans.shared.rpc.DownloadAllFilesAction; import org.zanata.webtrans.shared.rpc.DownloadAllFilesResult; +import java.io.Serializable; + /** * * @author Alex Eng aeng@redhat.com @@ -53,12 +55,13 @@ public class DownloadAllFilesHandler extends private ZanataIdentity identity; @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private ProjectIterationDAO projectIterationDAO; @In - private ProjectIterationDAO projectIterationDAO; + private TranslationArchiveService translationArchiveServiceImpl; - private AsyncTaskHandle zipFilePrepHandle; + @In + private AsyncTaskHandleManager asyncTaskHandleManager; @Override public DownloadAllFilesResult execute(DownloadAllFilesAction action, @@ -67,25 +70,24 @@ public DownloadAllFilesResult execute(DownloadAllFilesAction action, projectIterationDAO.getBySlug(action.getProjectSlug(), action.getVersionSlug()); if (identity.hasPermission(version, "download-all")) { - if (this.zipFilePrepHandle != null - && !this.zipFilePrepHandle.isDone()) { - // Cancel any other processes - this.zipFilePrepHandle.cancel(); - } - - // Build a new task + AsyncTaskHandle handle = new AsyncTaskHandle(); + Serializable taskKey = + asyncTaskHandleManager.registerTaskHandle(handle); // TODO This should be in a service and share code with the JSF // pages that do the same thing - ZipFileBuildTask task = - new ZipFileBuildTask(action.getProjectSlug(), - action.getVersionSlug(), action.getLocaleId(), - Identity.instance().getCredentials().getUsername(), - action.isPoProject()); + try { + translationArchiveServiceImpl.startBuildingTranslationFileArchive( + action.getProjectSlug(), action.getVersionSlug(), + action.getLocaleId(), Identity.instance().getCredentials() + .getUsername(), handle); + } + catch (Exception e) { + throw new ActionException(e); + } - // Fire the zip file building process and wait until it is ready to - // return - String taskId = asyncTaskManagerServiceImpl.startTask(task); - return new DownloadAllFilesResult(true, taskId); + // NB Keys are currently strings, but this is tied to the + // implementation + return new DownloadAllFilesResult(true, taskKey.toString()); } return new DownloadAllFilesResult(false, null); @@ -98,8 +100,4 @@ public void rollback(DownloadAllFilesAction action, throws ActionException { } - public AsyncTaskHandle getZipFilePrepHandle() { - return zipFilePrepHandle; - } - } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDownloadAllFilesProgressHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDownloadAllFilesProgressHandler.java index 3f94000922..cebf6d2647 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDownloadAllFilesProgressHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDownloadAllFilesProgressHandler.java @@ -30,7 +30,7 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.zanata.async.AsyncTaskHandle; -import org.zanata.service.AsyncTaskManagerService; +import org.zanata.async.AsyncTaskHandleManager; import org.zanata.webtrans.server.ActionHandlerFor; import org.zanata.webtrans.shared.rpc.GetDownloadAllFilesProgress; import org.zanata.webtrans.shared.rpc.GetDownloadAllFilesProgressResult; @@ -47,7 +47,7 @@ public class GetDownloadAllFilesProgressHandler extends AbstractActionHandler { @In - private AsyncTaskManagerService asyncTaskManagerServiceImpl; + private AsyncTaskHandleManager asyncTaskHandleManager; @Override public GetDownloadAllFilesProgressResult execute( @@ -58,11 +58,11 @@ public GetDownloadAllFilesProgressResult execute( String downloadId = ""; AsyncTaskHandle handle = - asyncTaskManagerServiceImpl.getHandle(action.getProcessId()); + asyncTaskHandleManager.getHandleByKey(action.getProcessId()); if (handle != null) { if (handle.isDone()) { try { - downloadId = handle.get(); + downloadId = handle.getResult(); } catch (InterruptedException e) { throw new ActionException( "Zip file preparation was interrupted", e); diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml index 2f648fd2aa..6ad5551807 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml @@ -489,7 +489,7 @@ diff --git a/zanata-war/src/test/java/org/zanata/async/AsyncTaskHandleTest.java b/zanata-war/src/test/java/org/zanata/async/AsyncTaskHandleTest.java new file mode 100644 index 0000000000..45131694ed --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/async/AsyncTaskHandleTest.java @@ -0,0 +1,112 @@ +/* + * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +public class AsyncTaskHandleTest { + + @Test + public void testStartTiming() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + handle.startTiming(); + + assertThat(handle.isStarted()).isTrue(); + assertThat(handle.getStartTime()).isGreaterThan(0); + } + + @Test + public void testFinishTiming() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + handle.startTiming(); + Thread.sleep(10); // Sleep as if something was executed + handle.finishTiming(); + + assertThat(handle.getFinishTime()).isGreaterThan(0); + assertThat(handle.getFinishTime()).isGreaterThan(handle.getStartTime()); + } + + @Test + public void testResult() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + AsyncTaskResult result = new AsyncTaskResult(); + handle.setFutureResult(result); + + result.set("result"); + + assertThat(handle.getResult()).isEqualTo("result"); + } + + @Test(expectedExceptions = Exception.class, + expectedExceptionsMessageRegExp = ".*Exception thrown.*") + public void testException() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + AsyncTaskResult result = new AsyncTaskResult(); + handle.setFutureResult(result); + + result.setException(new Exception("Exception thrown")); + + handle.getResult(); + } + + @Test + public void testIsDone() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + AsyncTaskResult result = new AsyncTaskResult(); + handle.setFutureResult(result); + + result.set("result"); + + assertThat(handle.isDone()).isTrue(); + } + + @Test + public void testIsCancelled() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + AsyncTaskResult result = new AsyncTaskResult(); + handle.setFutureResult(result); + handle.cancel(true); + + assertThat(handle.isCancelled()).isTrue(); + } + + @Test + public void testEstimatedTimeRemaining() throws Exception { + AsyncTaskHandle handle = new AsyncTaskHandle(); + handle.setMinProgress(0); + handle.setMaxProgress(10); + AsyncTaskResult result = new AsyncTaskResult(); + handle.setFutureResult(result); + handle.startTiming(); + + assertThat(handle.getEstimatedTimeRemaining().isPresent()).describedAs( + "Estimated time remaining is not available").isFalse(); + + handle.increaseProgress(1); + assertThat(handle.getEstimatedTimeRemaining().isPresent()).isTrue(); + } +} diff --git a/zanata-war/src/test/java/org/zanata/async/AsyncTaskITCase.java b/zanata-war/src/test/java/org/zanata/async/AsyncTaskITCase.java index 3661ac17ee..b28cf9edd8 100644 --- a/zanata-war/src/test/java/org/zanata/async/AsyncTaskITCase.java +++ b/zanata-war/src/test/java/org/zanata/async/AsyncTaskITCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the * @author tags. See the copyright.txt file in the distribution for a full * listing of individual contributors. * @@ -20,42 +20,29 @@ */ package org.zanata.async; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.zanata.async.AsyncTaskResult.taskResult; + import java.util.List; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.Future; +import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.In; -import org.junit.Assert; +import org.jboss.seam.annotations.Name; import org.junit.Test; import org.zanata.ArquillianTest; import com.google.common.collect.Lists; -import javax.annotation.Nonnull; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; - /** - * Integration tests for the Asynchrnous task framework. - * - * @author Carlos Munoz camunoz@redhat.com + * @author Carlos Munoz camunoz@redhat.com */ public class AsyncTaskITCase extends ArquillianTest { - @In - private TaskExecutor taskExecutor; - private CountDownLatch completed = new CountDownLatch(1); - private Runnable onComplete = new Runnable() { - @Override - public void run() { - completed.countDown(); - } - }; + @In + TestAsyncBean testAsyncBean; @Override protected void prepareDBUnitOperations() { @@ -67,101 +54,100 @@ public void taskReturnsValue() throws Exception { final String expectedRetVal = "EXPECTED"; // Start an asynchronous process - AsyncTaskHandle handle = - taskExecutor.startTask(new SimpleAsyncTask("taskReturnsValue") { - @Override - public String call() throws Exception { - return expectedRetVal; - } - }, onComplete); + Future result = testAsyncBean.asyncString(); // Wait for it to finish and get the result - String comp = handle.get(); - awaitComplete(); + String resultVal = result.get(); // Must be the same as the component that was inserted outside of the // task - assertThat(comp, equalTo(expectedRetVal)); + assertThat(resultVal).isEqualTo(expectedRetVal); } - private void awaitComplete() throws InterruptedException { - boolean done = completed.await(10, TimeUnit.SECONDS); - assertThat(done, is(true)); + @Test + public void taskDoesNotReturnValue() throws Exception { + // Given a task handle + AsyncTaskHandle handle = new AsyncTaskHandle(); + + // Start an asynchronous process + testAsyncBean.doesNotReturn(handle); + + // Wait for it to finish and get the result + handle.getResult(); + + // Must have executed the logic inside the method + assertThat(handle.getCurrentProgress()).isEqualTo(100); } @Test public void executionError() throws Exception { // Start an asynchronous process that throws an exception - AsyncTaskHandle handle = - taskExecutor.startTask(new SimpleAsyncTask("executionError") { - @Override - public String call() throws Exception { - throw new RuntimeException("Expected Exception"); - } - }, onComplete); - - // Wait for it to finish and get the result - waitUntilTaskIsDone(handle); - assertThat(handle.isDone(), is(true)); - awaitComplete(); try { - handle.get(); // Should throw an exception - Assert.fail(); - } catch (ExecutionException e) { - // expected + Future result = testAsyncBean.throwsError(); + result.get(); + failBecauseExceptionWasNotThrown(ExecutionException.class); + } catch (Exception e) { + // Original exception is wrapped around a + // java.concurrent.ExecutionException + assertThat(e.getCause()).hasMessage("Expected Exception"); } } @Test public void progressUpdates() throws Exception { final List progressUpdates = Lists.newArrayList(); - // "Mock" the task handle so that progress updates are recorded - final AsyncTaskHandle taskHandle = new AsyncTaskHandle("progressUpdates") { - @Override - public void setCurrentProgress(int progress) { - super.setCurrentProgress(progress); - progressUpdates.add(progress); - } - }; + + // Custom handle so that progress updates are recorded + final AsyncTaskHandle taskHandle = + new AsyncTaskHandle() { + @Override + public void setCurrentProgress(int progress) { + super.setCurrentProgress(progress); + progressUpdates.add(progress); + } + }; // Start an asynchronous process that updates its progress - AsyncTaskHandle handle = - taskExecutor - .startTask(new AsyncTask>() { - @Nonnull - @Override - public AsyncTaskHandle getHandle() { - return taskHandle; - } - - @Override - public Void call() throws Exception { - getHandle().setCurrentProgress(25); - getHandle().setCurrentProgress(50); - getHandle().setCurrentProgress(75); - getHandle().setCurrentProgress(100); - return null; - } - }, onComplete); + Future result = testAsyncBean.progressUpdates(taskHandle); - // Wait for it to finish and get the result - waitUntilTaskIsDone(handle); - awaitComplete(); + // Wait for it to finish + result.get(); // Progress update calls should match the task's internal updates - assertThat(handle.getCurrentProgress(), is(100)); - assertThat(progressUpdates.size(), is(4)); - assertThat(progressUpdates, contains(25, 50, 75, 100)); + assertThat(taskHandle.getCurrentProgress()).isEqualTo(100); + assertThat(progressUpdates.size()).isEqualTo(4); + assertThat(progressUpdates).contains(25, 50, 75, 100); } - /** - * This is an active wait for a task to finish. Only use for short lived - * tasks. - */ - private static void waitUntilTaskIsDone(AsyncTaskHandle handle) { - while (!handle.isDone()) { - // Wait until it's done. + + @Name("testAsyncBean") + @AutoCreate + @ContainsAsyncMethods + public static class TestAsyncBean { + + @Async + public Future asyncString() { + return taskResult("EXPECTED"); + } + + @Async + public void doesNotReturn(AsyncTaskHandle handle) { + handle.setCurrentProgress(100); + return; } - } + @Async + public Future throwsError() { + throw new RuntimeException("Expected Exception"); + } + + @Async + public Future progressUpdates(AsyncTaskHandle handle) { + handle.setCurrentProgress(25); + handle.setCurrentProgress(50); + handle.setCurrentProgress(75); + handle.setCurrentProgress(100); + return taskResult(); + } + } } diff --git a/zanata-war/src/test/java/org/zanata/rest/service/CopyTransRestTest.java b/zanata-war/src/test/java/org/zanata/rest/service/CopyTransRestTest.java index 0e1225fb98..626527aa5c 100644 --- a/zanata-war/src/test/java/org/zanata/rest/service/CopyTransRestTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/service/CopyTransRestTest.java @@ -32,7 +32,6 @@ import org.zanata.seam.SeamAutowire; import org.zanata.security.ZanataCredentials; import org.zanata.security.ZanataIdentity; -import org.zanata.service.impl.AsyncTaskManagerServiceImpl; import org.zanata.service.impl.CopyTransServiceImpl; import org.zanata.service.impl.LocaleServiceImpl; @@ -69,7 +68,6 @@ protected void prepareResources() { SeamAutowire seamAutowire = getSeamAutowire(); seamAutowire.use("session", getSession()).use("identity", mockIdentity) .useImpl(CopyTransServiceImpl.class) - .useImpl(AsyncTaskManagerServiceImpl.class) .useImpl(LocaleServiceImpl.class) .simulateSessionContext(true); diff --git a/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplPerformanceTest.java b/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplPerformanceTest.java index 157ef8d681..2894e8d393 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplPerformanceTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplPerformanceTest.java @@ -189,7 +189,6 @@ public void setUp() throws Exception { seam.autowire(AccountDAO.class).getByUsername("demo")) .useImpl(LocaleServiceImpl.class) .useImpl(TranslationMemoryServiceImpl.class) - .useImpl(AsyncTaskManagerServiceImpl.class) .useImpl(VersionStateCacheImpl.class) .useImpl(ValidationServiceImpl.class).ignoreNonResolvable(); @@ -320,7 +319,8 @@ public void testCopyTransForDocument() throws Exception { HCopyTransOptions.ConditionRuleAction.DOWNGRADE_TO_FUZZY, HCopyTransOptions.ConditionRuleAction.DOWNGRADE_TO_FUZZY, HCopyTransOptions.ConditionRuleAction.DOWNGRADE_TO_FUZZY); - copyTransService.copyTransForDocument(copyTransTargetDoc, options); + copyTransService + .copyTransForDocument(copyTransTargetDoc, options, null); Long totalTranslation = getEm().createQuery("select count(*) from HTextFlowTarget", diff --git a/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplTest.java index 8dfb040280..6222ba3ce0 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceImplTest.java @@ -34,6 +34,7 @@ import org.testng.annotations.Test; import org.zanata.SlowTest; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.common.ContentState; import org.zanata.common.ContentType; import org.zanata.common.EntityStatus; @@ -105,12 +106,10 @@ protected void beforeMethod() throws Exception { seam.autowire(AccountDAO.class).getByUsername("demo")) .useImpl(LocaleServiceImpl.class) .useImpl(TranslationMemoryServiceImpl.class) - .useImpl(AsyncTaskManagerServiceImpl.class) .useImpl(VersionStateCacheImpl.class) .useImpl(TranslationStateCacheImpl.class) .useImpl(ValidationServiceImpl.class).ignoreNonResolvable(); - seam.autowire(SearchIndexManager.class).reindex(true, true, false); AutowireTransaction.instance().rollback(); } @@ -205,7 +204,8 @@ public void testCopyTrans(CopyTransExecution execution) { execution.getProjectMismatchAction()); CopyTransService copyTransService = seam.autowire(CopyTransServiceImpl.class); - copyTransService.copyTransForIteration(projectIteration, options); + copyTransService.copyTransForIteration(projectIteration, options, + new CopyTransTaskHandle()); getEm().flush(); // Validate execution diff --git a/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceUnitTest.java b/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceUnitTest.java index 3bf7f279ca..d390f9fc02 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceUnitTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/CopyTransServiceUnitTest.java @@ -39,6 +39,7 @@ import org.zanata.common.LocaleId; import org.zanata.dao.DocumentDAO; import org.zanata.dao.ProjectDAO; +import org.zanata.dao.TextFlowDAO; import org.zanata.dao.TextFlowTargetDAO; import org.zanata.model.HCopyTransOptions; import org.zanata.model.HDocument; @@ -79,6 +80,8 @@ public class CopyTransServiceUnitTest { CopyTransWorkFactory copyTransWorkFactory; @Mock TranslationStateCache translationStateCacheImpl; + @Mock + TextFlowDAO textFlowDAO; @BeforeMethod public void initializeSeam() { @@ -100,7 +103,7 @@ private void shouldUseProjectOptions(boolean useProjectOpts) throws Exception { new CopyTransServiceImpl( localeServiceImpl, projectDAO, documentDAO, copyTransWorkFactory, textFlowTargetDAO, - translationStateCacheImpl); + translationStateCacheImpl, textFlowDAO); HCopyTransOptions projOptions = new HCopyTransOptions(IGNORE, IGNORE, IGNORE); @@ -139,7 +142,7 @@ private void shouldUseProjectOptions(boolean useProjectOpts) throws Exception { optionsOut = optionsIn; } - ctService.copyTransForDocument(doc, optionsIn); + ctService.copyTransForDocument(doc, optionsIn, null); verify(copyTransWorkFactory).createCopyTransWork(de, optionsOut, doc, requireReview, textFlows); diff --git a/zanata-war/src/test/java/org/zanata/service/impl/CopyVersionServiceImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/CopyVersionServiceImplTest.java index b2f14cbaea..983ab9d37f 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/CopyVersionServiceImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/CopyVersionServiceImplTest.java @@ -37,6 +37,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.async.handle.CopyVersionTaskHandle; import org.zanata.common.EntityStatus; import org.zanata.dao.DocumentDAO; import org.zanata.dao.LocaleDAO; @@ -125,7 +126,6 @@ protected void beforeMethod() throws Exception { .use("identity", identity) .use("filePersistService", fileSystemPersistService) .useImpl(VersionStateCacheImpl.class) - .useImpl(AsyncTaskManagerServiceImpl.class) .ignoreNonResolvable() .autowire(CopyVersionServiceImpl.class); } @@ -135,7 +135,7 @@ public void testCopyVersionNotExist() { String projectSlug = "non-exists-project"; String versionSlug = "1.0"; String newVersionSlug = "new-version"; - service.copyVersion(projectSlug, versionSlug, newVersionSlug); + service.copyVersion(projectSlug, versionSlug, newVersionSlug, null); verifyZeroInteractions(identity); verifyZeroInteractions(credentials); } @@ -158,7 +158,7 @@ public void testTextFlowBatching() { insertTextFlowAndTargetToDoc(existingDoc, tfCount, false); spyService.copyVersion(existingProjectSlug, existingVersionSlug, - newVersionSlug); + newVersionSlug, new CopyVersionTaskHandle()); int expectedTfBatchRuns = (tfCount / spyService.TF_BATCH_SIZE) @@ -186,7 +186,7 @@ public void testTextFlowTargetBatching() { int tftSize = insertTextFlowAndTargetToDoc(existingDoc, 1, true); spyService.copyVersion(existingProjectSlug, existingVersionSlug, - newVersionSlug); + newVersionSlug, new CopyVersionTaskHandle()); int expectedTftBatchRuns = (tftSize / spyService.TFT_BATCH_SIZE) @@ -259,7 +259,8 @@ public void testCopyVersion2() { private void runCopyVersion(String projectSlug, String versionSlug, String newVersionSlug) { - service.copyVersion(projectSlug, versionSlug, newVersionSlug); + service.copyVersion(projectSlug, versionSlug, newVersionSlug, + new CopyVersionTaskHandle()); HProjectIteration existingVersion = projectIterationDAO.getBySlug( projectSlug, versionSlug); diff --git a/zanata-war/src/test/java/org/zanata/service/impl/TranslationFinderTest.java b/zanata-war/src/test/java/org/zanata/service/impl/TranslationFinderTest.java index 6be8587598..8221c86a2e 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/TranslationFinderTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/TranslationFinderTest.java @@ -66,7 +66,7 @@ public void beforeClass() throws Exception { Search.getFullTextEntityManager(getEm())) .use("entityManagerFactory", getEmf()) .use("session", new FullTextSessionImpl(getSession())) - .useImpl(AsyncTaskManagerServiceImpl.class) + .useImpl(IndexingServiceImpl.class) .ignoreNonResolvable(); seam.autowire(SearchIndexManager.class).reindex(true, true, false); LocaleDAO localeDAO = seam.autowire(LocaleDAO.class); diff --git a/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java index d4da75aac8..0bc7be70e7 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java @@ -73,7 +73,7 @@ public void beforeClass() throws Exception { Search.getFullTextEntityManager(getEm())) .use("entityManagerFactory", getEmf()) .use("session", new FullTextSessionImpl(getSession())) - .useImpl(AsyncTaskManagerServiceImpl.class) + .useImpl(IndexingServiceImpl.class) .ignoreNonResolvable() .autowire(TranslationMemoryServiceImpl.class); seam.autowire(SearchIndexManager.class).reindex(true, true, false);