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

Commit

Permalink
rhbz1069428 - fix potential hibernate exception when SourceDocResourc…
Browse files Browse the repository at this point in the history
…e is accessed concurrently
  • Loading branch information
Patrick Huang committed Feb 25, 2014
1 parent 54d2040 commit 3f3b0b5
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 11 deletions.
@@ -0,0 +1,96 @@
package org.zanata.feature.concurrentedit;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

import org.hamcrest.Matchers;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.zanata.feature.DetailedTest;
import org.zanata.rest.dto.resource.Resource;
import org.zanata.util.AddUsersRule;
import org.zanata.util.ZanataRestCaller;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.zanata.util.ZanataRestCaller.buildSourceResource;
import static org.zanata.util.ZanataRestCaller.buildTextFlow;

/**
* @author Patrick Huang <a
* href="mailto:pahuang@redhat.com">pahuang@redhat.com</a>
*/
@Category(DetailedTest.class)
public class ConcurrentAccessTest {

@ClassRule
public static AddUsersRule addUsersRule = new AddUsersRule();

@Test
public void concurrentDocumentCreationWillNotCauseHibernateException()
throws InterruptedException {
final String projectSlug = "project";
final String iterationSlug = "master";
final AtomicInteger counter = new AtomicInteger(1);
new ZanataRestCaller().createProjectAndVersion(projectSlug,
iterationSlug, "gettext");

int threadCount = 5;
Callable<Integer> task = new Callable<Integer>() {

@Override
public Integer call() throws Exception {
int suffix = counter.getAndIncrement();
return new ZanataRestCaller().postSourceDocResource(
projectSlug, iterationSlug, buildResource(suffix),
false);
}
};
List<Callable<Integer>> tasks = Collections.nCopies(threadCount, task);
ExecutorService executorService =
Executors.newFixedThreadPool(threadCount);

List<Future<Integer>> futures = executorService.invokeAll(tasks);

List<Integer> result = getStatusCodes(futures);

List<Integer> expectedReturnCode =
Collections.nCopies(threadCount, 201);
assertThat(result, Matchers.equalTo(expectedReturnCode));
}

private static Resource buildResource(int suffix) {
return buildSourceResource("doc" + suffix,
buildTextFlow("res" + suffix, "content" + suffix));
}

private static List<Integer> getStatusCodes(List<Future<Integer>> futures) {
return Lists.transform(futures,
new Function<Future<Integer>, Integer>() {

@Override
public Integer apply(Future<Integer> input) {
return getResult(input);
}
});
}

private static Integer getResult(Future<Integer> input) {
try {
return input.get();
} catch (InterruptedException e) {
throw Throwables.propagate(e);
} catch (ExecutionException e) {
throw Throwables.propagate(e);
}
}
}
Expand Up @@ -28,7 +28,7 @@
* <a href="mailto:djansen@redhat.com">djansen@redhat.com</a>
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({ ConcurrentEditTest.class })
@Suite.SuiteClasses({ ConcurrentEditTest.class, ConcurrentAccessTest.class })

public class ConcurrentEditTestSuite {
}
Expand Up @@ -93,14 +93,15 @@ public void deleteSourceDoc(String projectSlug, String iterationSlug,
response.releaseConnection();
}

public void postSourceDocResource(String projectSlug, String iterationSlug,
public int postSourceDocResource(String projectSlug, String iterationSlug,
Resource resource, boolean copytrans) {
ClientResponse<String> response = zanataProxyFactory
.getSourceDocResource(projectSlug, iterationSlug).post(
resource,
Collections.<String>emptySet(), copytrans);
ClientUtility.checkResult(response);
int status = response.getStatus();
response.releaseConnection();
return status;
}

public void putSourceDocResource(String projectSlug, String iterationSlug,
Expand Down
Expand Up @@ -58,12 +58,6 @@ public class VersionStateCacheImpl implements VersionStateCache {
private static final String VERSION_STATISTIC_CACHE_NAME = BASE
+ ".versionStatisticCache";

@In
private LocaleDAO localeDAO;

@In
private TextFlowDAO textFlowDAO;

private CacheManager cacheManager;

private CacheWrapper<VersionLocaleKey, WordStatistic> versionStatisticCache;
Expand Down Expand Up @@ -100,7 +94,7 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) {
new VersionLocaleKey(event.getProjectIterationId(),
event.getLocaleId());
WordStatistic stats = versionStatisticCache.get(key);
HTextFlow textFlow = textFlowDAO.findById(event.getTextFlowId());
HTextFlow textFlow = getTextFlowDAO().findById(event.getTextFlowId());

if (stats != null) {
stats.decrement(event.getPreviousState(),
Expand All @@ -120,7 +114,7 @@ public WordStatistic getVersionStatistics(Long projectIterationId,

@Override
public void clearVersionStatsCache(Long versionId) {
for (HLocale locale : localeDAO.findAll()) {
for (HLocale locale : getLocaleDAO().findAll()) {
VersionLocaleKey key =
new VersionLocaleKey(versionId, locale.getLocaleId());
versionStatisticCache.remove(key);
Expand All @@ -145,4 +139,12 @@ public WordStatistic load(VersionLocaleKey key) throws Exception {
return wordStatistic;
}
}

public LocaleDAO getLocaleDAO() {
return (LocaleDAO) Component.getInstance(LocaleDAO.class);
}

public TextFlowDAO getTextFlowDAO() {
return (TextFlowDAO) Component.getInstance(TextFlowDAO.class);
}
}

0 comments on commit 3f3b0b5

Please sign in to comment.