1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.buildcache;
20
21 import javax.annotation.Nonnull;
22 import javax.annotation.PostConstruct;
23 import javax.inject.Inject;
24 import javax.inject.Named;
25
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap;
31 import java.util.stream.Collectors;
32
33 import org.apache.maven.SessionScoped;
34 import org.apache.maven.buildcache.xml.Build;
35 import org.apache.maven.execution.AbstractExecutionListener;
36 import org.apache.maven.execution.ExecutionEvent;
37 import org.apache.maven.execution.MavenExecutionRequest;
38 import org.apache.maven.execution.MavenSession;
39 import org.apache.maven.lifecycle.DefaultLifecycles;
40 import org.apache.maven.lifecycle.Lifecycle;
41 import org.apache.maven.plugin.MojoExecution;
42 import org.apache.maven.project.MavenProject;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 @SessionScoped
47 @Named
48 public class LifecyclePhasesHelper extends AbstractExecutionListener {
49
50 private static final Logger LOGGER = LoggerFactory.getLogger(LifecyclePhasesHelper.class);
51
52 private final MavenSession session;
53 private final DefaultLifecycles defaultLifecycles;
54 private final List<String> phases;
55 private final String lastCleanPhase;
56
57 private final ConcurrentMap<MavenProject, MojoExecution> forkedProjectToOrigin = new ConcurrentHashMap<>();
58
59 @Inject
60 public LifecyclePhasesHelper(
61 MavenSession session, DefaultLifecycles defaultLifecycles, @Named("clean") Lifecycle cleanLifecycle) {
62 this.session = session;
63 this.defaultLifecycles = Objects.requireNonNull(defaultLifecycles);
64 this.phases = defaultLifecycles.getLifeCycles().stream()
65 .flatMap(lf -> lf.getPhases().stream())
66 .collect(Collectors.toList());
67 this.lastCleanPhase = CacheUtils.getLast(cleanLifecycle.getPhases());
68 }
69
70 @PostConstruct
71 public void init() {
72 MavenExecutionRequest request = session.getRequest();
73 ChainedListener lifecycleListener = new ChainedListener(request.getExecutionListener());
74 lifecycleListener.chainListener(this);
75 request.setExecutionListener(lifecycleListener);
76 }
77
78 @Override
79 public void forkedProjectStarted(ExecutionEvent event) {
80 LOGGER.debug(
81 "Started forked project. Project: {}, instance: {}, originating mojo: {}",
82 event.getProject(),
83 System.identityHashCode(event.getProject()),
84 event.getMojoExecution());
85 forkedProjectToOrigin.put(event.getProject(), event.getMojoExecution());
86 }
87
88 @Override
89 public void forkedProjectSucceeded(ExecutionEvent event) {
90 LOGGER.debug(
91 "Finished forked project. Project: {}, instance: {}",
92 event.getProject(),
93 System.identityHashCode(event.getProject()));
94 forkedProjectToOrigin.remove(event.getProject(), event.getMojoExecution());
95 }
96
97 @Override
98 public void forkedProjectFailed(ExecutionEvent event) {
99 LOGGER.debug(
100 "Finished forked project. Project: {}, instance: {}",
101 event.getProject(),
102 System.identityHashCode(event.getProject()));
103 forkedProjectToOrigin.remove(event.getProject(), event.getMojoExecution());
104 }
105
106 @Nonnull
107 public String resolveHighestLifecyclePhase(MavenProject project, List<MojoExecution> mojoExecutions) {
108 return resolveMojoExecutionLifecyclePhase(project, CacheUtils.getLast(mojoExecutions));
109 }
110
111
112
113
114 public boolean isLaterPhaseThanClean(String phase) {
115 return isLaterPhase(phase, lastCleanPhase);
116 }
117
118 public boolean isLaterPhaseThanBuild(String phase, Build build) {
119 return isLaterPhase(phase, build.getHighestCompletedGoal());
120 }
121
122
123
124
125
126 public boolean isLaterPhase(String phase, String other) {
127 if (!phases.contains(phase)) {
128 throw new IllegalArgumentException("Unsupported phase: " + phase);
129 }
130 if (!phases.contains(other)) {
131 throw new IllegalArgumentException("Unsupported phase: " + other);
132 }
133
134 return phases.indexOf(phase) > phases.indexOf(other);
135 }
136
137
138
139
140 public List<MojoExecution> getCleanSegment(MavenProject project, List<MojoExecution> mojoExecutions) {
141 List<MojoExecution> list = new ArrayList<>(mojoExecutions.size());
142 for (MojoExecution mojoExecution : mojoExecutions) {
143 String lifecyclePhase = resolveMojoExecutionLifecyclePhase(project, mojoExecution);
144
145 if (isLaterPhaseThanClean(lifecyclePhase)) {
146 break;
147 }
148 list.add(mojoExecution);
149 }
150 return list;
151 }
152
153
154
155
156
157
158
159
160 private String resolveMojoExecutionLifecyclePhase(MavenProject project, MojoExecution mojoExecution) {
161
162 MojoExecution forkOrigin = forkedProjectToOrigin.get(project);
163
164
165 if (forkOrigin == null) {
166 return mojoExecution.getLifecyclePhase();
167 } else {
168 if (LOGGER.isDebugEnabled()) {
169 LOGGER.debug(
170 "Mojo execution {} is forked, returning phase {} from originating mojo {}",
171 CacheUtils.mojoExecutionKey(mojoExecution),
172 forkOrigin.getLifecyclePhase(),
173 CacheUtils.mojoExecutionKey(forkOrigin));
174 }
175 return forkOrigin.getLifecyclePhase();
176 }
177 }
178
179
180
181
182 public List<MojoExecution> getCachedSegment(MavenProject project, List<MojoExecution> mojoExecutions, Build build) {
183 List<MojoExecution> list = new ArrayList<>(mojoExecutions.size());
184 for (MojoExecution mojoExecution : mojoExecutions) {
185
186 String lifecyclePhase = resolveMojoExecutionLifecyclePhase(project, mojoExecution);
187
188 if (!isLaterPhaseThanClean(lifecyclePhase)) {
189 continue;
190 }
191 if (isLaterPhaseThanBuild(lifecyclePhase, build)) {
192 break;
193 }
194 list.add(mojoExecution);
195 }
196 return list;
197 }
198
199
200
201
202 public List<MojoExecution> getPostCachedSegment(
203 MavenProject project, List<MojoExecution> mojoExecutions, Build build) {
204 List<MojoExecution> list = new ArrayList<>(mojoExecutions.size());
205 for (MojoExecution mojoExecution : mojoExecutions) {
206
207
208 String lifecyclePhase = resolveMojoExecutionLifecyclePhase(project, mojoExecution);
209
210 if (isLaterPhaseThanBuild(lifecyclePhase, build)) {
211 list.add(mojoExecution);
212 }
213 }
214 return list;
215 }
216
217 public boolean isForkedProject(MavenProject project) {
218 return forkedProjectToOrigin.containsKey(project);
219 }
220 }