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
2525import java .util .Map ;
2626import java .util .Objects ;
2727import java .util .Set ;
28+ import java .util .concurrent .CountDownLatch ;
29+ import java .util .concurrent .TimeUnit ;
2830import java .util .function .Function ;
2931
32+ import org .eclipse .scout .rt .dataobject .fixture .CollectionFixtureDo ;
3033import org .eclipse .scout .rt .dataobject .fixture .EntityContributionFixtureDo ;
3134import org .eclipse .scout .rt .dataobject .fixture .EntityFixtureDo ;
3235import org .eclipse .scout .rt .dataobject .fixture .FirstSimpleContributionFixtureDo ;
36+ import org .eclipse .scout .rt .dataobject .fixture .Lorem1FixtureDo ;
3337import org .eclipse .scout .rt .dataobject .fixture .OtherEntityFixtureDo ;
3438import org .eclipse .scout .rt .dataobject .fixture .SecondSimpleContributionFixtureDo ;
3539import 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 ;
3643import org .eclipse .scout .rt .platform .util .Assertions .AssertionException ;
3744import org .eclipse .scout .rt .platform .util .CollectionUtility ;
45+ import org .eclipse .scout .rt .platform .util .SleepUtil ;
3846import org .eclipse .scout .rt .platform .util .StringUtility ;
3947import org .eclipse .scout .rt .platform .util .date .DateUtility ;
4048import 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