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.lifecycle;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.Arrays;
26  import java.util.Comparator;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Objects;
31  import java.util.stream.Collectors;
32  
33  import org.apache.maven.api.services.LifecycleRegistry;
34  import org.apache.maven.api.services.Lookup;
35  import org.apache.maven.api.services.LookupException;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  /**
40   * @since 3.0
41   */
42  // TODO The configuration for the lifecycle needs to be externalized so that I can use the annotations properly for the
43  // wiring and reference and external source for the lifecycle configuration.
44  @Named
45  @Singleton
46  public class DefaultLifecycles {
47      public static final String[] STANDARD_LIFECYCLES = {"clean", "default", "site", "wrapper"};
48  
49      private final Logger logger = LoggerFactory.getLogger(getClass());
50  
51      // @Configuration(source="org/apache/maven/lifecycle/lifecycles.xml")
52  
53      private final Lookup lookup;
54  
55      private final LifecycleRegistry registry;
56  
57      private Map<String, Lifecycle> customLifecycles;
58  
59      private boolean lifecyclesPrinted;
60  
61      public DefaultLifecycles() {
62          this.lookup = null;
63          this.registry = null;
64      }
65  
66      /**
67       * @deprecated Use {@link #DefaultLifecycles(LifecycleRegistry,Lookup)} instead
68       */
69      @Deprecated
70      public DefaultLifecycles(Map<String, Lifecycle> lifecycles, org.codehaus.plexus.logging.Logger logger) {
71          this.customLifecycles = lifecycles;
72          this.lookup = null;
73          this.registry = null;
74      }
75  
76      @Inject
77      public DefaultLifecycles(LifecycleRegistry registry, Lookup lookup) {
78          this.lookup = lookup;
79          this.registry = registry;
80      }
81  
82      /**
83       * Get lifecycle based on phase
84       *
85       * @param phase
86       * @return
87       */
88      public Lifecycle get(String phase) {
89          return getPhaseToLifecycleMap().get(phase);
90      }
91  
92      /**
93       * We use this to map all phases to the lifecycle that contains it. This is used so that a user can specify the
94       * phase they want to execute and we can easily determine what lifecycle we need to run.
95       *
96       * @return A map of lifecycles, indexed on id
97       */
98      public Map<String, Lifecycle> getPhaseToLifecycleMap() {
99          if (logger.isDebugEnabled() && !lifecyclesPrinted) {
100             for (Lifecycle lifecycle : getLifeCycles()) {
101                 logger.debug("Lifecycle {}", lifecycle);
102             }
103             lifecyclesPrinted = true;
104         }
105 
106         // If people are going to make their own lifecycles then we need to tell people how to namespace them correctly
107         // so that they don't interfere with internally defined lifecycles.
108 
109         Map<String, Lifecycle> phaseToLifecycleMap = new HashMap<>();
110 
111         for (Lifecycle lifecycle : getLifeCycles()) {
112             for (String phase : lifecycle.getPhases()) {
113                 // The first definition wins.
114                 Lifecycle original = phaseToLifecycleMap.put(phase, lifecycle);
115                 if (original != null && logger.isWarnEnabled()) {
116                     logger.warn(
117                             "Duplicated lifecycle phase {}. Defined in {} but also in {}",
118                             phase,
119                             original.getId(),
120                             lifecycle.getId());
121                 }
122             }
123             if (lifecycle.getDelegate() != null) {
124                 for (org.apache.maven.api.Lifecycle.Alias alias :
125                         lifecycle.getDelegate().aliases()) {
126                     Lifecycle original = phaseToLifecycleMap.put(alias.v3Phase(), lifecycle);
127                     if (original != null && logger.isWarnEnabled()) {
128                         logger.warn(
129                                 "Duplicated lifecycle phase {}. Defined in {} but also in {}",
130                                 alias.v3Phase(),
131                                 original.getId(),
132                                 lifecycle.getId());
133                     }
134                 }
135             }
136         }
137 
138         return phaseToLifecycleMap;
139     }
140 
141     /**
142      * Returns an ordered list of lifecycles
143      */
144     public List<Lifecycle> getLifeCycles() {
145         List<String> lifecycleIds = Arrays.asList(STANDARD_LIFECYCLES);
146 
147         Comparator<String> comparator = (l, r) -> {
148             int lx = lifecycleIds.indexOf(l);
149             int rx = lifecycleIds.indexOf(r);
150 
151             if (lx < 0 || rx < 0) {
152                 return rx - lx;
153             } else {
154                 return lx - rx;
155             }
156         };
157 
158         Map<String, Lifecycle> lifecyclesMap = lookupLifecycles();
159 
160         // ensure canonical order of standard lifecycles
161         return lifecyclesMap.values().stream()
162                 .peek(l -> Objects.requireNonNull(l.getId(), "A lifecycle must have an id."))
163                 .sorted(Comparator.comparing(Lifecycle::getId, comparator))
164                 .collect(Collectors.toList());
165     }
166 
167     private Map<String, Lifecycle> lookupLifecycles() {
168         // TODO: Remove the following code when maven-compat is gone
169         // This code is here to ensure maven-compat's EmptyLifecycleExecutor keeps on working.
170         if (lookup == null) {
171             return customLifecycles != null ? customLifecycles : new HashMap<>();
172         }
173 
174         // Lifecycles cannot be cached as extensions might add custom lifecycles later in the execution.
175         try {
176             return registry != null
177                     ? registry.stream().collect(Collectors.toMap(lf -> lf.id(), lf -> new Lifecycle(registry, lf)))
178                     : Map.of();
179         } catch (LookupException e) {
180             throw new IllegalStateException("Unable to lookup lifecycles from the plexus container", e);
181         }
182     }
183 
184     public String getLifecyclePhaseList() {
185         return getLifeCycles().stream().flatMap(l -> l.getPhases().stream()).collect(Collectors.joining(", "));
186     }
187 }