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.internal.concurrent;
20  
21  import java.util.Collection;
22  import java.util.HashSet;
23  import java.util.LinkedHashMap;
24  import java.util.Map;
25  import java.util.Objects;
26  import java.util.Set;
27  import java.util.TreeMap;
28  import java.util.concurrent.atomic.AtomicBoolean;
29  import java.util.concurrent.atomic.AtomicInteger;
30  import java.util.stream.Stream;
31  
32  import org.apache.maven.api.Lifecycle;
33  import org.apache.maven.api.annotations.Nonnull;
34  import org.apache.maven.api.annotations.Nullable;
35  import org.apache.maven.plugin.MojoExecution;
36  import org.apache.maven.project.MavenProject;
37  
38  public class BuildStep {
39      public static final int CREATED = 0;
40      public static final int PLANNING = 1;
41      public static final int SCHEDULED = 2;
42      public static final int EXECUTED = 3;
43      public static final int FAILED = 4;
44      public static final int SKIPPED = 5;
45  
46      public static final String PLAN = "$plan$";
47      public static final String SETUP = "$setup$";
48      public static final String TEARDOWN = "$teardown$";
49  
50      @Nonnull
51      final MavenProject project;
52  
53      @Nonnull
54      final String name;
55  
56      @Nullable
57      final Lifecycle.Phase phase;
58  
59      final Map<Integer, Map<String, MojoExecution>> mojos = new TreeMap<>();
60      final Collection<BuildStep> predecessors = new HashSet<>();
61      final Collection<BuildStep> successors = new HashSet<>();
62      final AtomicInteger status = new AtomicInteger();
63      final AtomicBoolean skip = new AtomicBoolean();
64      volatile Exception exception;
65  
66      public BuildStep(String name, MavenProject project, Lifecycle.Phase phase) {
67          this.name = Objects.requireNonNull(name, "name cannot be null");
68          this.project = Objects.requireNonNull(project, "project cannot be null");
69          this.phase = phase;
70      }
71  
72      public boolean isCreated() {
73          return status.get() == CREATED;
74      }
75  
76      public boolean isDone() {
77          int state = status.get();
78          return state == EXECUTED || state == FAILED || state == SKIPPED;
79      }
80  
81      public Stream<BuildStep> allPredecessors() {
82          return preds(new HashSet<>()).stream();
83      }
84  
85      private Set<BuildStep> preds(Set<BuildStep> preds) {
86          if (preds.add(this)) {
87              this.predecessors.forEach(n -> n.preds(preds));
88          }
89          return preds;
90      }
91  
92      public boolean isSuccessorOf(BuildStep step) {
93          return isSuccessorOf(new HashSet<>(), step);
94      }
95  
96      private boolean isSuccessorOf(Set<BuildStep> visited, BuildStep step) {
97          if (this == step) {
98              return true;
99          } else if (visited.add(this)) {
100             return this.predecessors.stream().anyMatch(n -> n.isSuccessorOf(visited, step));
101         } else {
102             return false;
103         }
104     }
105 
106     public void skip() {
107         skip.set(true);
108         mojos.clear();
109     }
110 
111     public void addMojo(MojoExecution mojo, int priority) {
112         if (!skip.get()) {
113             mojos.computeIfAbsent(priority, k -> new LinkedHashMap<>())
114                     .put(mojo.getGoal() + ":" + mojo.getExecutionId(), mojo);
115         }
116     }
117 
118     public void executeAfter(BuildStep stepToExecuteBefore) {
119         if (!isSuccessorOf(stepToExecuteBefore)) {
120             predecessors.add(stepToExecuteBefore);
121             stepToExecuteBefore.successors.add(this);
122         }
123     }
124 
125     public void executeBefore(BuildStep stepToExecuteAfter) {
126         stepToExecuteAfter.executeAfter(this);
127     }
128 
129     public Stream<MojoExecution> executions() {
130         return mojos.values().stream().flatMap(m -> m.values().stream());
131     }
132 
133     @Override
134     public boolean equals(Object o) {
135         if (this == o) {
136             return true;
137         }
138         if (o == null || getClass() != o.getClass()) {
139             return false;
140         }
141         BuildStep that = (BuildStep) o;
142         return Objects.equals(project, that.project) && Objects.equals(name, that.name);
143     }
144 
145     @Override
146     public int hashCode() {
147         return Objects.hash(project, name);
148     }
149 
150     @Override
151     public String toString() {
152         return "BuildStep[" + "project="
153                 + project.getGroupId() + ":" + project.getArtifactId() + ", phase="
154                 + name + ']';
155     }
156 }