View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cling.invoker.mvnup.goals;
20  
21  import java.nio.file.Path;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.maven.api.cli.mvnup.UpgradeOptions;
28  import org.apache.maven.api.di.Inject;
29  import org.apache.maven.api.di.Named;
30  import org.apache.maven.api.di.Singleton;
31  import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
32  import org.jdom2.Document;
33  
34  /**
35   * Orchestrates the execution of different upgrade strategies.
36   * Determines which strategies to apply based on options and executes them in priority order.
37   * The DI container automatically sorts the injected strategies by their @Priority annotations.
38   */
39  @Named
40  @Singleton
41  public class StrategyOrchestrator {
42  
43      private final List<UpgradeStrategy> strategies;
44  
45      @Inject
46      public StrategyOrchestrator(List<UpgradeStrategy> strategies) {
47          // DI container automatically sorts strategies by priority (highest first)
48          this.strategies = strategies;
49      }
50  
51      /**
52       * Executes all applicable strategies for the given context and POM map.
53       *
54       * @param context the upgrade context
55       * @param pomMap map of all POM files in the project
56       * @return the overall result of all strategy executions
57       */
58      public UpgradeResult executeStrategies(UpgradeContext context, Map<Path, Document> pomMap) {
59          context.println();
60          context.info("Maven Upgrade Tool");
61          logUpgradeOptions(context);
62  
63          UpgradeResult overallResult = UpgradeResult.empty();
64          List<String> executedStrategies = new ArrayList<>();
65  
66          // Execute each applicable strategy
67          for (UpgradeStrategy strategy : strategies) {
68              context.indent();
69              if (strategy.isApplicable(context)) {
70                  context.info("");
71                  context.action("Executing strategy: " + strategy.getDescription());
72                  context.indent();
73                  executedStrategies.add(strategy.getDescription());
74  
75                  try {
76                      UpgradeResult result = strategy.apply(context, pomMap);
77  
78                      // Merge results using the smart merge functionality
79                      overallResult = overallResult.merge(result);
80  
81                      if (result.success()) {
82                          context.success("Strategy completed successfully");
83                      } else {
84                          context.warning("Strategy completed with " + result.errorCount() + " error(s)");
85                      }
86                  } catch (Exception e) {
87                      context.failure("Strategy execution failed: " + e.getMessage());
88                      // Create a failure result for this strategy and merge it
89                      Set<Path> allPoms = pomMap.keySet();
90                      UpgradeResult failureResult = UpgradeResult.failure(allPoms, Set.of());
91                      overallResult = overallResult.merge(failureResult);
92                  } finally {
93                      context.unindent();
94                  }
95              } else {
96                  context.detail("Skipping strategy: " + strategy.getDescription() + " (not applicable)");
97              }
98              context.unindent();
99          }
100 
101         // Log overall summary
102         logOverallSummary(context, overallResult, executedStrategies);
103 
104         return overallResult;
105     }
106 
107     /**
108      * Logs the upgrade options that are enabled.
109      */
110     private void logUpgradeOptions(UpgradeContext context) {
111         UpgradeOptions options = context.options();
112 
113         context.action("Upgrade options:");
114         context.indent();
115 
116         if (options.all().orElse(false)) {
117             context.detail("--all (enables all upgrade options)");
118         } else {
119             if (options.modelVersion().isPresent()) {
120                 context.detail("--model-version " + options.modelVersion().get());
121             }
122             if (options.model().orElse(false)) {
123                 context.detail("--model");
124             }
125             if (options.plugins().orElse(false)) {
126                 context.detail("--plugins");
127             }
128             if (options.infer().orElse(false)) {
129                 context.detail("--infer");
130             }
131 
132             // Show defaults if no options specified
133             if (options.modelVersion().isEmpty()
134                     && options.model().isEmpty()
135                     && options.plugins().isEmpty()
136                     && options.infer().isEmpty()) {
137                 context.detail("(using defaults: --model --plugins --infer)");
138             }
139         }
140 
141         context.unindent();
142     }
143 
144     /**
145      * Logs the overall summary of all strategy executions.
146      */
147     private void logOverallSummary(
148             UpgradeContext context, UpgradeResult overallResult, List<String> executedStrategies) {
149 
150         context.println();
151         context.info("Overall Upgrade Summary:");
152         context.indent();
153         context.info(overallResult.processedCount() + " POM(s) processed");
154         context.info(overallResult.modifiedCount() + " POM(s) modified");
155         context.info(overallResult.unmodifiedCount() + " POM(s) needed no changes");
156         context.info(overallResult.errorCount() + " error(s) encountered");
157         context.unindent();
158 
159         if (!executedStrategies.isEmpty()) {
160             context.println();
161             context.info("Executed Strategies:");
162             context.indent();
163             for (String strategy : executedStrategies) {
164                 context.detail(strategy);
165             }
166             context.unindent();
167         }
168 
169         if (overallResult.modifiedCount() > 0 && overallResult.errorCount() == 0) {
170             context.success("All upgrades completed successfully!");
171         } else if (overallResult.modifiedCount() > 0 && overallResult.errorCount() > 0) {
172             context.warning("Upgrades completed with some errors");
173         } else if (overallResult.modifiedCount() == 0 && overallResult.errorCount() == 0) {
174             context.success("No upgrades needed - all POMs are up to date");
175         } else {
176             context.failure("Upgrade process failed");
177         }
178     }
179 }