2626import java .util .Map ;
2727import java .util .Properties ;
2828import java .util .Vector ;
29+ import java .util .concurrent .CountDownLatch ;
2930
3031import org .apache .maven .plugin .AbstractMojo ;
3132import org .apache .maven .plugin .MojoExecutionException ;
3233import org .apache .maven .plugins .annotations .Component ;
3334import org .apache .maven .plugins .annotations .LifecyclePhase ;
3435import org .apache .maven .plugins .annotations .Mojo ;
3536import org .apache .maven .plugins .annotations .Parameter ;
37+ import org .apache .maven .plugin .logging .Log ;
3638
3739/**
3840 * Checks the thread-safe retrieval of components from active component collections.
4244@ Mojo (name = "check-thread-safety" , defaultPhase = LifecyclePhase .VALIDATE )
4345public class CheckThreadSafetyMojo extends AbstractMojo {
4446
47+ private static final String MAVEN_CORE_IT_LOG = "[MAVEN-CORE-IT-LOG] " ;
4548 /**
4649 * Project base directory used for manual path alignment.
4750 */
@@ -70,86 +73,97 @@ public class CheckThreadSafetyMojo extends AbstractMojo {
7073 * Runs this mojo.
7174 *
7275 * @throws MojoExecutionException If the output file could not be created.
76+ *
77+ * @implNote threads need to use different realms to trigger changes of the collections.
7378 */
7479 public void execute () throws MojoExecutionException {
75- Properties componentProperties = new Properties ();
76-
77- getLog ().info ("[MAVEN-CORE-IT-LOG] Testing concurrent component access" );
78-
79- ClassLoader pluginRealm = getClass ().getClassLoader ();
80- ClassLoader coreRealm = MojoExecutionException .class .getClassLoader ();
81-
82- final Map map = componentMap ;
83- final List list = componentList ;
84- final List go = new Vector ();
85- final List exceptions = new Vector ();
86-
87- Thread [] threads = new Thread [2 ];
80+ getLog ().info (MAVEN_CORE_IT_LOG + "Testing concurrent component access" );
81+ final Thread [] threads = new Thread [2 ];
82+ final List <Exception > exceptions = new Vector <>();
83+ final CountDownLatch startLatch = new CountDownLatch (1 );
8884 for (int i = 0 ; i < threads .length ; i ++) {
89- // NOTE: The threads need to use different realms to trigger changes of the collections
90- final ClassLoader cl = (i % 2 ) == 0 ? pluginRealm : coreRealm ;
91- threads [i ] = new Thread () {
92- private final ClassLoader tccl = cl ;
93-
94- public void run () {
95- getLog ().info ("[MAVEN-CORE-IT-LOG] Thread " + this + " uses " + tccl );
96- Thread .currentThread ().setContextClassLoader (tccl );
97- while (go .isEmpty ()) {
98- // wait for start
99- }
100- for (int j = 0 ; j < 10 * 1000 ; j ++) {
101- try {
102- for (Object o : map .values ()) {
103- o .toString ();
104- }
105- for (Object aList : list ) {
106- aList .toString ();
107- }
108- } catch (Exception e ) {
109- getLog ().warn ("[MAVEN-CORE-IT-LOG] Thread " + this + " encountered concurrency issue" , e );
110- exceptions .add (e );
111- }
112- }
113- }
114- };
85+ threads [i ] = new Thread (new CheckThreadSafetyTask (
86+ (i % 2 ) == 0 ? getClass ().getClassLoader () : MojoExecutionException .class .getClassLoader (),
87+ startLatch ,
88+ componentMap ,
89+ componentList ,
90+ exceptions ,
91+ getLog ()
92+ ));
11593 threads [i ].start ();
11694 }
11795
118- go .add (null );
96+ startLatch .countDown (); // signal all threads to start
97+ joinOrInterrupt (threads );
98+ storeComponentProperties (exceptions );
99+ getLog ().info (MAVEN_CORE_IT_LOG + "Created output file " + outputFile );
100+ }
101+
102+ private void joinOrInterrupt (Thread [] threads ) {
119103 for (Thread thread : threads ) {
120104 try {
121105 thread .join ();
122106 } catch (InterruptedException e ) {
123- getLog ().warn ("[MAVEN-CORE-IT-LOG] Interrupted while joining " + thread );
107+ Thread .currentThread ().interrupt ();
108+ getLog ().warn (MAVEN_CORE_IT_LOG + "Interrupted while joining " + thread );
124109 }
125110 }
111+ }
126112
113+ private void storeComponentProperties ( List <Exception > exceptions ) throws MojoExecutionException {
114+ final Properties componentProperties = new Properties ();
127115 componentProperties .setProperty ("components" , Integer .toString (componentList .size ()));
128116 componentProperties .setProperty ("exceptions" , Integer .toString (exceptions .size ()));
129117
130118 if (!outputFile .isAbsolute ()) {
131119 outputFile = new File (basedir , outputFile .getPath ());
132120 }
133121
134- getLog ().info ("[MAVEN-CORE-IT-LOG] Creating output file " + outputFile );
135-
136- OutputStream out = null ;
137- try {
122+ getLog ().info (MAVEN_CORE_IT_LOG + "Creating output file " + outputFile );
123+ try (OutputStream out = new FileOutputStream (outputFile )) {
138124 outputFile .getParentFile ().mkdirs ();
139- out = new FileOutputStream (outputFile );
140125 componentProperties .store (out , "MAVEN-CORE-IT-LOG" );
141126 } catch (IOException e ) {
142127 throw new MojoExecutionException ("Output file could not be created: " + outputFile , e );
143- } finally {
144- if (out != null ) {
128+ }
129+ }
130+
131+ private record CheckThreadSafetyTask (
132+ ClassLoader tccl ,
133+ CountDownLatch startLatch ,
134+ Map <String , TestComponent > map ,
135+ List <TestComponent > list ,
136+ List <Exception > exceptions ,
137+ Log log
138+ ) implements Runnable {
139+
140+ @ Override
141+ public void run () {
142+ log .info (MAVEN_CORE_IT_LOG + "Thread " + Thread .currentThread () + " uses " + tccl );
143+ Thread .currentThread ().setContextClassLoader (tccl );
144+ try {
145+ startLatch .await (); // wait for the start signal
146+ checkThreadSafety ();
147+ } catch (InterruptedException e ) {
148+ Thread .currentThread ().interrupt ();
149+ log .warn (MAVEN_CORE_IT_LOG + "Thread " + Thread .currentThread () + " was interrupted while waiting" );
150+ }
151+ }
152+
153+ private void checkThreadSafety () {
154+ for (int j = 0 ; j < 10 * 1000 ; j ++) {
145155 try {
146- out .close ();
147- } catch (IOException e ) {
148- // just ignore
156+ for (Object o : map .values ()) {
157+ o .toString ();
158+ }
159+ for (Object aList : list ) {
160+ aList .toString ();
161+ }
162+ } catch (Exception e ) {
163+ log .warn (MAVEN_CORE_IT_LOG + "Thread " + Thread .currentThread () + " encountered concurrency issue" , e );
164+ exceptions .add (e );
149165 }
150166 }
151167 }
152-
153- getLog ().info ("[MAVEN-CORE-IT-LOG] Created output file " + outputFile );
154168 }
155- }
169+ }
0 commit comments