diff --git a/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java b/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java index bedd646c94..afb42d5537 100644 --- a/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java +++ b/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java @@ -25,20 +25,18 @@ import org.zanata.common.ContentState; import org.zanata.common.LocaleId; -import org.zanata.ui.model.statistic.WordStatistic; @Data public final class DocumentStatisticUpdatedEvent { public static final String EVENT_NAME = "org.zanata.event.DocumentStatisticUpdated"; - private final WordStatistic oldStats; - private final WordStatistic newStats; - private final Long projectIterationId; private final Long documentId; private final LocaleId localeId; + private final int wordCount; + private final ContentState previousState; private final ContentState newState; } 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 24780cdf8b..641de67467 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 @@ -62,6 +62,7 @@ import org.zanata.service.TranslationStateCache; import org.zanata.service.VersionStateCache; import org.zanata.ui.model.statistic.WordStatistic; +import org.zanata.util.StatisticsUtil; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; @@ -244,23 +245,32 @@ private void processWebHookDocumentMilestoneEvent( Collection contentStates, String message, int percentMilestone) { - boolean shouldPublish = - hasContentStateReachedMilestone(event.getOldStats(), - event.getNewStats(), contentStates, percentMilestone); - - if (shouldPublish) { - HProjectIteration version = - projectIterationDAO.findById(event.getProjectIterationId()); - HProject project = version.getProject(); - - if (!project.getWebHooks().isEmpty()) { - HDocument document = documentDAO.getById(event.getDocumentId()); - DocumentMilestoneEvent milestoneEvent = - new DocumentMilestoneEvent(project.getSlug(), - version.getSlug(), document.getDocId(), - event.getLocaleId(), message); - for (WebHook webHook : project.getWebHooks()) { - publishDocumentMilestoneEvent(webHook, milestoneEvent); + HProjectIteration version = + projectIterationDAO.findById(event.getProjectIterationId()); + HProject project = version.getProject(); + + if (!project.getWebHooks().isEmpty()) { + WordStatistic stats = + translationStateCacheImpl.getDocumentStatistics( + event.getDocumentId(), event.getLocaleId()); + + WordStatistic oldStats = StatisticsUtil.copyWordStatistic(stats); + if(oldStats != null) { + oldStats.decrement(event.getNewState(), event.getWordCount()); + oldStats.increment(event.getPreviousState(), event.getWordCount()); + + boolean shouldPublish = hasContentStateReachedMilestone(oldStats, stats, + contentStates, percentMilestone); + + if (shouldPublish) { + HDocument document = documentDAO.getById(event.getDocumentId()); + DocumentMilestoneEvent milestoneEvent = + new DocumentMilestoneEvent(project.getSlug(), + version.getSlug(), document.getDocId(), + event.getLocaleId(), message); + for (WebHook webHook : project.getWebHooks()) { + publishDocumentMilestoneEvent(webHook, milestoneEvent); + } } } } @@ -321,9 +331,11 @@ private void clearStatsCacheForUpdatedDocument(HDocument document) { @VisibleForTesting public void init(ProjectIterationDAO projectIterationDAO, - DocumentDAO documentDAO, Messages msgs) { + DocumentDAO documentDAO, + TranslationStateCache translationStateCacheImpl, Messages msgs) { this.projectIterationDAO = projectIterationDAO; this.documentDAO = documentDAO; + this.translationStateCacheImpl = translationStateCacheImpl; this.msgs = msgs; } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java index e44771eafb..874330fe75 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java @@ -181,18 +181,8 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) { new DocumentLocaleKey(event.getDocumentId(), event.getLocaleId()); - WordStatistic stats = documentStatisticCache.get(key); - - if (stats != null) { - HTextFlow textFlow = getTextFlowDAO().findById( - event.getTextFlowId()); - - stats.decrement(event.getPreviousState(), - textFlow.getWordCount().intValue()); - stats.increment(event.getNewState(), - textFlow.getWordCount().intValue()); - documentStatisticCache.put(key, stats); - } + //invalidate document statistic cache + clearDocumentStatistics(event.getDocumentId(), event.getLocaleId()); // update document status information updateDocStatusCache(key, event.getTextFlowTargetId()); @@ -204,7 +194,7 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) { private void updateDocStatusCache(DocumentLocaleKey key, Long updatedTargetId) { DocumentStatus documentStatus = docStatusCache.get(key); - if(documentStatus != null) { + if (documentStatus != null) { HTextFlowTarget target = getTextFlowTargetDAO().findById(updatedTargetId, false); updateDocumentStatus(getDocumentDAO(), documentStatus, diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java index 437947f711..dbc6e99af7 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java @@ -1,8 +1,6 @@ package org.zanata.service.impl; -import com.google.common.annotations.VisibleForTesting; 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.Observer; @@ -11,11 +9,9 @@ import org.zanata.dao.TextFlowDAO; import org.zanata.events.DocumentStatisticUpdatedEvent; import org.zanata.events.TextFlowTargetStateEvent; -import org.zanata.service.DocumentService; import org.zanata.service.TranslationStateCache; -import org.zanata.ui.model.statistic.WordStatistic; -import org.zanata.util.StatisticsUtil; +import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; /** @@ -51,21 +47,14 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) { // Fire asynchronous event public void publishAsyncEvent(TextFlowTargetStateEvent event) { if (Events.exists()) { - WordStatistic stats = - translationStateCacheImpl.getDocumentStatistics( - event.getDocumentId(), event.getLocaleId()); - int wordCount = textFlowDAO.getWordCount(event.getTextFlowId()); - WordStatistic oldStats = StatisticsUtil.copyWordStatistic(stats); - oldStats.decrement(event.getNewState(), wordCount); - oldStats.increment(event.getPreviousState(), wordCount); - Events.instance().raiseAsynchronousEvent( DocumentStatisticUpdatedEvent.EVENT_NAME, - new DocumentStatisticUpdatedEvent(oldStats, stats, + new DocumentStatisticUpdatedEvent( event.getProjectIterationId(), event.getDocumentId(), event.getLocaleId(), + wordCount, event.getPreviousState(), event.getNewState())); } } diff --git a/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java b/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java index 6cef08e48a..3ebb6e5bcf 100644 --- a/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java +++ b/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java @@ -29,15 +29,15 @@ protected AbstractStatistic(int approved, int needReview, int untranslated, this.rejected = rejected; } - public void increment(ContentState state, int count) { + public synchronized void increment(ContentState state, int count) { set(state, get(state) + count); } - public void decrement(ContentState state, int count) { + public synchronized void decrement(ContentState state, int count) { set(state, get(state) - count); } - public void set(ContentState state, int value) { + public synchronized void set(ContentState state, int value) { switch (state) { case Translated: translated = value; @@ -60,7 +60,7 @@ public void set(ContentState state, int value) { } } - public int get(ContentState state) { + public synchronized int get(ContentState state) { switch (state) { case Translated: return translated; @@ -78,7 +78,7 @@ public int get(ContentState state) { } } - public void add(AbstractStatistic other) { + public synchronized void add(AbstractStatistic other) { this.approved += other.approved; this.needReview += other.needReview; this.untranslated += other.untranslated; @@ -94,31 +94,31 @@ protected void set(AbstractStatistic other) { this.rejected = other.rejected; } - public int getTotal() { + public synchronized int getTotal() { return approved + needReview + untranslated + translated + rejected; } - public int getApproved() { + public synchronized int getApproved() { return approved; } - public int getNeedReview() { + public synchronized int getNeedReview() { return needReview; } - public int getUntranslated() { + public synchronized int getUntranslated() { return untranslated; } - public int getTranslated() { + public synchronized int getTranslated() { return translated; } - public int getRejected() { + public synchronized int getRejected() { return rejected; } - public double getPercentage(ContentState contentState) { + public synchronized double getPercentage(ContentState contentState) { switch (contentState) { case Translated: return getPercentTranslated(); @@ -136,23 +136,23 @@ public double getPercentage(ContentState contentState) { } } - public double getPercentTranslated() { + public synchronized double getPercentTranslated() { return getPercentage(getTranslated()); } - public double getPercentFuzzy() { + public synchronized double getPercentFuzzy() { return getPercentage(getNeedReview()); } - public double getPercentRejected() { + public synchronized double getPercentRejected() { return getPercentage(getRejected()); } - public double getPercentApproved() { + public synchronized double getPercentApproved() { return getPercentage(getApproved()); } - public double getPercentUntranslated() { + public synchronized double getPercentUntranslated() { return getPercentage(getUntranslated()); } @@ -166,7 +166,7 @@ private double getPercentage(double value) { } @Override - public boolean equals(Object obj) { + public synchronized boolean equals(Object obj) { if (obj == this) return true; if (obj == null) diff --git a/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java b/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java index ad99f8fa5f..1aed3c7d1b 100644 --- a/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java +++ b/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java @@ -1,17 +1,14 @@ package org.zanata.ui.model.statistic; -import lombok.Getter; -import lombok.Setter; - /** * @author Alex Eng aeng@redhat.com + * + * TODO: make this class immutable */ public class WordStatistic extends AbstractStatistic { private static final long serialVersionUID = -8807499518683834883L; - @Getter - @Setter private double remainingHours; public WordStatistic() { @@ -23,7 +20,7 @@ public WordStatistic(int approved, int needReview, int untranslated, super(approved, needReview, untranslated, translated, rejected); } - public String toString() { + public synchronized String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()); sb.append("\ntranslated-" + getTranslated()); @@ -34,4 +31,12 @@ public String toString() { return sb.toString(); } + + public synchronized double getRemainingHours() { + return remainingHours; + } + + public synchronized void setRemainingHours(double remainingHours) { + this.remainingHours = remainingHours; + } } diff --git a/zanata-war/src/test/java/org/zanata/service/impl/DocumentServiceImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/DocumentServiceImplTest.java index 34ab882e54..1253f21ce6 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/DocumentServiceImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/DocumentServiceImplTest.java @@ -46,6 +46,7 @@ import org.zanata.model.HProjectIteration; import org.zanata.model.WebHook; import org.zanata.service.DocumentService; +import org.zanata.service.TranslationStateCache; import org.zanata.ui.model.statistic.WordStatistic; import org.zanata.util.StatisticsUtil; @@ -63,6 +64,9 @@ public class DocumentServiceImplTest { @Mock private DocumentDAO documentDAO; + @Mock + private TranslationStateCache translationStateCacheImpl; + @Mock private Messages msgs; @@ -82,7 +86,8 @@ public class DocumentServiceImplTest { public void setup() { MockitoAnnotations.initMocks(this); documentService = new DocumentServiceImpl(); - documentService.init(projectIterationDAO, documentDAO, msgs); + documentService.init(projectIterationDAO, documentDAO, + translationStateCacheImpl, msgs); HProjectIteration version = Mockito.mock(HProjectIteration.class); HProject project = Mockito.mock(HProject.class); @@ -108,6 +113,8 @@ public void documentMilestoneEventTranslatedTest() { DocumentServiceImpl spyService = Mockito.spy(documentService); WordStatistic stats = new WordStatistic(0, 0, 0, 10, 0); + when(translationStateCacheImpl.getDocumentStatistics(docId, localeId)) + .thenReturn(stats); runDocumentStatisticUpdatedTest(spyService, ContentState.New, ContentState.Translated, stats); @@ -128,6 +135,8 @@ public void documentMilestoneEventTranslatedNot100Test() { DocumentServiceImpl spyService = Mockito.spy(documentService); WordStatistic stats = new WordStatistic(0, 1, 0, 9, 0); + when(translationStateCacheImpl.getDocumentStatistics(docId, localeId)) + .thenReturn(stats); runDocumentStatisticUpdatedTest(spyService, ContentState.New, ContentState.Translated, stats); @@ -148,6 +157,8 @@ public void documentMilestoneEventApprovedTest() { DocumentServiceImpl spyService = Mockito.spy(documentService); WordStatistic stats = new WordStatistic(10, 0, 0, 0, 0); + when(translationStateCacheImpl.getDocumentStatistics(docId, localeId)) + .thenReturn(stats); runDocumentStatisticUpdatedTest(spyService, ContentState.Translated, ContentState.Approved, stats); @@ -167,6 +178,8 @@ public void documentMilestoneEventApprovedNot100Test() { DocumentServiceImpl spyService = Mockito.spy(documentService); WordStatistic stats = new WordStatistic(9, 0, 0, 1, 0); + when(translationStateCacheImpl.getDocumentStatistics(docId, localeId)) + .thenReturn(stats); runDocumentStatisticUpdatedTest(spyService, ContentState.Translated, ContentState.Approved, stats); @@ -186,6 +199,8 @@ public void documentMilestoneEventApprovedNot100Test() { public void documentMilestoneEventSameStateTest1() { DocumentServiceImpl spyService = Mockito.spy(documentService); WordStatistic stats = new WordStatistic(10, 0, 0, 0, 0); + when(translationStateCacheImpl.getDocumentStatistics(docId, localeId)) + .thenReturn(stats); runDocumentStatisticUpdatedTest(spyService, ContentState.Approved, ContentState.Approved, stats); @@ -206,8 +221,11 @@ public void documentMilestoneEventSameStateTest2() { DocumentServiceImpl spyService = Mockito.spy(documentService); WordStatistic stats = new WordStatistic(0, 0, 0, 10, 0); + when(translationStateCacheImpl.getDocumentStatistics(docId, localeId)) + .thenReturn(stats); + runDocumentStatisticUpdatedTest(spyService, ContentState.Translated, - ContentState.Translated, stats); + ContentState.Translated, stats); DocumentMilestoneEvent milestoneEvent = new DocumentMilestoneEvent(projectSlug, versionSlug, @@ -225,13 +243,10 @@ private void runDocumentStatisticUpdatedTest( ContentState oldState, ContentState newState, WordStatistic stats) { int wordCount = 10; - WordStatistic oldStats = StatisticsUtil.copyWordStatistic(stats); - oldStats.decrement(newState, wordCount); - oldStats.increment(oldState, wordCount); DocumentStatisticUpdatedEvent event = - new DocumentStatisticUpdatedEvent(oldStats, stats, versionId, - docId, localeId, oldState, newState); + new DocumentStatisticUpdatedEvent(versionId, + docId, localeId, wordCount, oldState, newState); spyService.documentStatisticUpdated(event); }