1
1
package io .quarkus .maven ;
2
2
3
3
import static io .quarkus .analytics .dto .segment .TrackEventType .DEV_MODE ;
4
+ import static io .quarkus .maven .QuarkusBootstrapMojo .CLOSE_BOOTSTRAPPED_APP_PARAM ;
5
+ import static io .quarkus .maven .QuarkusBootstrapMojo .MODE_PARAM ;
4
6
import static io .smallrye .common .expression .Expression .Flag .LENIENT_SYNTAX ;
5
7
import static io .smallrye .common .expression .Expression .Flag .NO_TRIM ;
6
8
import static java .util .Collections .emptyMap ;
@@ -557,27 +559,31 @@ public void close() throws IOException {
557
559
}
558
560
return ;
559
561
}
560
- final Set < Path > changed = new HashSet <> ();
562
+ List < String > changedPoms = List . of ();
561
563
for (Map .Entry <Path , Long > e : pomFiles .entrySet ()) {
562
564
long t = Files .getLastModifiedTime (e .getKey ()).toMillis ();
563
565
if (t > e .getValue ()) {
564
- changed .add (e .getKey ());
566
+ if (changedPoms .isEmpty ()) {
567
+ // unless it's a git or some other command, there won't be many POMs modified in 100 milliseconds
568
+ changedPoms = new ArrayList <>(1 );
569
+ }
570
+ changedPoms .add (e .getKey ().toString ());
565
571
pomFiles .put (e .getKey (), t );
566
572
}
567
573
}
568
- if (!changed .isEmpty ()) {
569
- getLog (). info ( "Changes detected to " + changed + ", restarting dev mode" );
574
+ if (!changedPoms .isEmpty ()) {
575
+ logPomChanges ( changedPoms );
570
576
571
577
// stop the runner before we build the new one as the debug port being free
572
578
// is tested when building the runner
573
579
runner .stop ();
574
580
575
581
final DevModeRunner newRunner ;
576
582
try {
577
- bootstrapId = handleAutoCompile ();
583
+ bootstrapId = handleAutoCompile (changedPoms );
578
584
newRunner = new DevModeRunner (runner .commandLine .getDebugPort (), bootstrapId );
579
585
} catch (Exception e ) {
580
- getLog ().info ("Could not load changed pom.xml file, changes not applied" , e );
586
+ getLog ().info ("Could not load changedPoms pom.xml file, changes not applied" , e );
581
587
continue ;
582
588
}
583
589
newRunner .run ();
@@ -590,6 +596,15 @@ public void close() throws IOException {
590
596
}
591
597
}
592
598
599
+ private void logPomChanges (List <String > changedPoms ) {
600
+ final StringBuilder sb = new StringBuilder ().append ("Restarting dev mode following changes in " );
601
+ sb .append (changedPoms .get (0 ));
602
+ for (int i = 1 ; i < changedPoms .size (); ++i ) {
603
+ sb .append (", " ).append (changedPoms .get (i ));
604
+ }
605
+ getLog ().info (sb .toString ());
606
+ }
607
+
593
608
/**
594
609
* if the process is forcibly killed then the terminal may be left in raw mode, which
595
610
* messes everything up. This attempts to fix that by saving the state so it can be restored
@@ -649,7 +664,18 @@ private void restoreTerminalState() {
649
664
}
650
665
651
666
private String handleAutoCompile () throws MojoExecutionException {
667
+ return handleAutoCompile (List .of ());
668
+ }
652
669
670
+ /**
671
+ * Invokes Maven project goals that are meant to be executed before quarkus:dev,
672
+ * unless they have already been executed.
673
+ *
674
+ * @param reloadPoms a list of POM files that should be reloaded from disk instead of read from the reactor
675
+ * @return bootstrap id
676
+ * @throws MojoExecutionException in case of an error
677
+ */
678
+ private String handleAutoCompile (List <String > reloadPoms ) throws MojoExecutionException {
653
679
List <String > goals = session .getGoals ();
654
680
// check for default goal(s) if none were specified explicitly,
655
681
// see also org.apache.maven.lifecycle.internal.DefaultLifecycleTaskSegmentCalculator
@@ -761,10 +787,7 @@ private String handleAutoCompile() throws MojoExecutionException {
761
787
}
762
788
}
763
789
764
- final Map <String , String > quarkusGoalParams = Map .of (
765
- "mode" , LaunchMode .DEVELOPMENT .name (),
766
- QuarkusBootstrapMojo .CLOSE_BOOTSTRAPPED_APP , "false" ,
767
- "bootstrapId" , bootstrapId );
790
+ Map <String , String > quarkusGoalParams = null ;
768
791
for (int phaseIndex = latestHandledPhaseIndex + 1 ; phaseIndex < PRE_DEV_MODE_PHASES .size (); ++phaseIndex ) {
769
792
var executions = phaseExecutions .get (PRE_DEV_MODE_PHASES .get (phaseIndex ));
770
793
if (executions == null ) {
@@ -774,6 +797,9 @@ private String handleAutoCompile() throws MojoExecutionException {
774
797
var executedGoals = executedPluginGoals .getOrDefault (pe .plugin .getId (), List .of ());
775
798
for (String goal : pe .execution .getGoals ()) {
776
799
if (!executedGoals .contains (goal )) {
800
+ if (quarkusGoalParams == null ) {
801
+ quarkusGoalParams = getQuarkusGoalParams (bootstrapId , reloadPoms );
802
+ }
777
803
try {
778
804
executeGoal (pe , goal ,
779
805
pe .getPluginId ().equals (quarkusPluginId ) ? quarkusGoalParams : Map .of ());
@@ -793,6 +819,35 @@ private String handleAutoCompile() throws MojoExecutionException {
793
819
return bootstrapId ;
794
820
}
795
821
822
+ /**
823
+ * Returns a map of parameters for the Quarkus plugin goals to be invoked.
824
+ *
825
+ * @param bootstrapId bootstrap id
826
+ * @param reloadPoms POM files to be reloaded from disk instead of taken from the reactor
827
+ * @return map of parameters for the Quarkus plugin goals
828
+ */
829
+ private static Map <String , String > getQuarkusGoalParams (String bootstrapId , List <String > reloadPoms ) {
830
+ final Map <String , String > result = new HashMap <>(4 );
831
+ result .put (QuarkusBootstrapMojo .MODE_PARAM , LaunchMode .DEVELOPMENT .name ());
832
+ result .put (QuarkusBootstrapMojo .CLOSE_BOOTSTRAPPED_APP_PARAM , "false" );
833
+ result .put (QuarkusBootstrapMojo .BOOTSTRAP_ID_PARAM , bootstrapId );
834
+ if (reloadPoms != null && !reloadPoms .isEmpty ()) {
835
+ String reloadPomsStr ;
836
+ if (reloadPoms .size () == 1 ) {
837
+ reloadPomsStr = reloadPoms .get (0 );
838
+ } else {
839
+ final StringBuilder sb = new StringBuilder ();
840
+ sb .append (reloadPoms .get (0 ));
841
+ for (int i = 1 ; i < reloadPoms .size (); ++i ) {
842
+ sb .append ("," ).append (reloadPoms .get (i ));
843
+ }
844
+ reloadPomsStr = sb .toString ();
845
+ }
846
+ result .put (QuarkusBootstrapMojo .RELOAD_POMS_PARAM , reloadPomsStr );
847
+ }
848
+ return result ;
849
+ }
850
+
796
851
private String getCurrentGoal () {
797
852
return mojoExecution .getMojoDescriptor ().getPluginDescriptor ().getGoalPrefix () + ":"
798
853
+ mojoExecution .getGoal ();
@@ -1004,7 +1059,7 @@ && matchesExecution(executionId, exec.getId())) {
1004
1059
}
1005
1060
}
1006
1061
1007
- if (( Xpp3Dom ) plugin .getConfiguration () != null ) {
1062
+ if (plugin .getConfiguration () != null ) {
1008
1063
mergedConfig = mergedConfig == null ? (Xpp3Dom ) plugin .getConfiguration ()
1009
1064
: Xpp3Dom .mergeXpp3Dom (mergedConfig , (Xpp3Dom ) plugin .getConfiguration (), true );
1010
1065
}
@@ -1300,17 +1355,15 @@ void run() throws Exception {
1300
1355
process = processBuilder .start ();
1301
1356
1302
1357
//https://github.com/quarkusio/quarkus/issues/232
1303
- Runtime .getRuntime ().addShutdownHook (new Thread (new Runnable () {
1304
- @ Override
1305
- public void run () {
1306
- process .destroy ();
1307
- try {
1308
- process .waitFor ();
1309
- } catch (InterruptedException e ) {
1310
- getLog ().warn ("Unable to properly wait for dev-mode end" , e );
1311
- }
1312
- }
1313
- }, "Development Mode Shutdown Hook" ));
1358
+ Runtime .getRuntime ().addShutdownHook (new Thread (this ::safeStop , "Development Mode Shutdown Hook" ));
1359
+ }
1360
+
1361
+ private void safeStop () {
1362
+ try {
1363
+ stop ();
1364
+ } catch (InterruptedException e ) {
1365
+ getLog ().warn ("Unable to properly wait for dev-mode end" , e );
1366
+ }
1314
1367
}
1315
1368
1316
1369
void stop () throws InterruptedException {
@@ -1467,7 +1520,7 @@ private DevModeCommandLine newLauncher(String actualDebugPort, String bootstrapI
1467
1520
.setRootProjectDir (rootProjectDir );
1468
1521
1469
1522
// There are a couple of reasons we don't want to use the original Maven session:
1470
- // 1) a reload could be triggered by a change in a pom.xml, in which case the Maven session might not be in sync any more with the effective POM;
1523
+ // 1) a reload could be triggered by a change in a pom.xml, in which case the Maven session might not be in sync anymore with the effective POM;
1471
1524
// 2) in case there is a local module that has a snapshot version, which is also available in a remote snapshot repository,
1472
1525
// the Maven resolver will be checking for newer snapshots in the remote repository and might end up resolving the artifact from there.
1473
1526
final BootstrapMavenContext mvnCtx = workspaceProvider .createMavenContext (mvnConfig );
0 commit comments