Skip to content

Commit 9b56e0e

Browse files
committed
Immediately create and add Collection/List/Set nodes for DoEntity
It is not possible to create/put them later as this would lead to concurrency issues (e.g. multiple threads trying to get the same entity which might trigger concurrent putNode calls). 396980
1 parent 3ec5ab4 commit 9b56e0e

File tree

2 files changed

+184
-40
lines changed
  • org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject
  • org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject

2 files changed

+184
-40
lines changed

org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DoEntityTest.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2025 BSI Business Systems Integration AG
2+
* Copyright (c) 2010, 2026 BSI Business Systems Integration AG
33
*
44
* This program and the accompanying materials are made
55
* available under the terms of the Eclipse Public License 2.0
@@ -25,16 +25,24 @@
2525
import java.util.Map;
2626
import java.util.Objects;
2727
import java.util.Set;
28+
import java.util.concurrent.CountDownLatch;
29+
import java.util.concurrent.TimeUnit;
2830
import java.util.function.Function;
2931

32+
import org.eclipse.scout.rt.dataobject.fixture.CollectionFixtureDo;
3033
import org.eclipse.scout.rt.dataobject.fixture.EntityContributionFixtureDo;
3134
import org.eclipse.scout.rt.dataobject.fixture.EntityFixtureDo;
3235
import org.eclipse.scout.rt.dataobject.fixture.FirstSimpleContributionFixtureDo;
36+
import org.eclipse.scout.rt.dataobject.fixture.Lorem1FixtureDo;
3337
import org.eclipse.scout.rt.dataobject.fixture.OtherEntityFixtureDo;
3438
import org.eclipse.scout.rt.dataobject.fixture.SecondSimpleContributionFixtureDo;
3539
import org.eclipse.scout.rt.platform.BEANS;
40+
import org.eclipse.scout.rt.platform.job.IFuture;
41+
import org.eclipse.scout.rt.platform.job.JobState;
42+
import org.eclipse.scout.rt.platform.job.Jobs;
3643
import org.eclipse.scout.rt.platform.util.Assertions.AssertionException;
3744
import org.eclipse.scout.rt.platform.util.CollectionUtility;
45+
import org.eclipse.scout.rt.platform.util.SleepUtil;
3846
import org.eclipse.scout.rt.platform.util.StringUtility;
3947
import org.eclipse.scout.rt.platform.util.date.DateUtility;
4048
import org.junit.Test;
@@ -865,6 +873,7 @@ public void testValueNode() {
865873
public void testDoList() {
866874
DoEntity entity = BEANS.get(DoEntity.class);
867875
DoList<String> doList = entity.doList("attribute");
876+
assertSame(doList, entity.doList("attribute"));
868877
assertEquals(CollectionUtility.emptyArrayList(), doList.get());
869878

870879
List<String> values = Arrays.asList("value");
@@ -893,6 +902,7 @@ public void testListNode() {
893902
public void testDoSet() {
894903
DoEntity entity = BEANS.get(DoEntity.class);
895904
DoSet<String> doSet = entity.doSet("attribute");
905+
assertSame(doSet, entity.doSet("attribute"));
896906
assertEquals(CollectionUtility.emptyHashSet(), doSet.get());
897907

898908
Set<String> value = Collections.singleton("foo");
@@ -921,6 +931,7 @@ public void testSetNode() {
921931
public void testDoCollection() {
922932
DoEntity entity = BEANS.get(DoEntity.class);
923933
DoCollection<String> doCollection = entity.doCollection("attribute");
934+
assertSame(doCollection, entity.doCollection("attribute"));
924935
assertEquals(CollectionUtility.emptyArrayList(), doCollection.get());
925936

926937
Collection<String> value = Arrays.asList("foo");
@@ -944,4 +955,47 @@ public void testCollectionNode() {
944955
assertThrows(AssertionException.class, () -> entity.getValueNode("attribute"));
945956
assertThrows(AssertionException.class, () -> entity.getSetNode("attribute"));
946957
}
958+
959+
@Test
960+
public void testConcurrentGetCollection() {
961+
testConcurrentGet_internal(BEANS.get(CollectionFixtureDo.class), CollectionFixtureDo::getSimpleDoCollection);
962+
}
963+
964+
@Test
965+
public void testConcurrentGetList() {
966+
testConcurrentGet_internal(BEANS.get(EntityFixtureDo.class), EntityFixtureDo::getOtherEntities);
967+
}
968+
969+
@Test
970+
public void testConcurrentGetSet() {
971+
testConcurrentGet_internal(BEANS.get(CollectionFixtureDo.class), CollectionFixtureDo::getSimpleDoSet);
972+
}
973+
974+
@Test
975+
public void testConcurrentGetValue() {
976+
testConcurrentGet_internal(BEANS.get(Lorem1FixtureDo.class), Lorem1FixtureDo::getValue1);
977+
}
978+
979+
protected <E extends IDoEntity, R> void testConcurrentGet_internal(E entity, Function<E, R> getFunction) {
980+
CountDownLatch latch = new CountDownLatch(1);
981+
List<IFuture<R>> jobs = new ArrayList<>();
982+
983+
// a lot of jobs to test thread-safety of get calls
984+
for (int i = 0; i < 1000; i++) {
985+
jobs.add(Jobs.schedule(() -> {
986+
latch.await();
987+
return getFunction.apply(entity);
988+
}, Jobs.newInput()));
989+
}
990+
991+
// wait for all jobs to be running
992+
while (!jobs.stream().map(IFuture::getState).allMatch(JobState.RUNNING::equals)) {
993+
SleepUtil.sleepSafe(10, TimeUnit.MILLISECONDS);
994+
}
995+
latch.countDown();
996+
997+
// all objects must be the same (equality is not enough), hence compare all other objects with the first object
998+
R firstElement = jobs.iterator().next().awaitDoneAndGet(); // get the first element
999+
assertTrue(jobs.stream().map(IFuture::awaitDoneAndGet).allMatch(o -> firstElement == o));
1000+
}
9471001
}

0 commit comments

Comments
 (0)