59242715d2ac609d34a140c30ff4a6ac9573f18d
[sdc.git] /
1 /*
2  * Copyright © 2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on a "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.onboarding;
18
19 import static org.openecomp.sdc.onboarding.BuildHelper.getArtifactPathInLocalRepo;
20 import static org.openecomp.sdc.onboarding.BuildHelper.getChecksum;
21 import static org.openecomp.sdc.onboarding.BuildHelper.getSourceChecksum;
22 import static org.openecomp.sdc.onboarding.BuildHelper.readState;
23 import static org.openecomp.sdc.onboarding.Constants.ANY_EXT;
24 import static org.openecomp.sdc.onboarding.Constants.COLON;
25 import static org.openecomp.sdc.onboarding.Constants.DOT;
26 import static org.openecomp.sdc.onboarding.Constants.EMPTY_JAR;
27 import static org.openecomp.sdc.onboarding.Constants.GENERATED_SOURCE_CHECKSUM;
28 import static org.openecomp.sdc.onboarding.Constants.INSTRUMENT_ONLY;
29 import static org.openecomp.sdc.onboarding.Constants.INSTRUMENT_WITH_TEST_ONLY;
30 import static org.openecomp.sdc.onboarding.Constants.JAR;
31 import static org.openecomp.sdc.onboarding.Constants.JAVA_EXT;
32 import static org.openecomp.sdc.onboarding.Constants.MAIN;
33 import static org.openecomp.sdc.onboarding.Constants.MAIN_CHECKSUM;
34 import static org.openecomp.sdc.onboarding.Constants.MAIN_SOURCE_CHECKSUM;
35 import static org.openecomp.sdc.onboarding.Constants.PREFIX;
36 import static org.openecomp.sdc.onboarding.Constants.RESOURCES_CHANGED;
37 import static org.openecomp.sdc.onboarding.Constants.RESOURCE_CHECKSUM;
38 import static org.openecomp.sdc.onboarding.Constants.RESOURCE_ONLY;
39 import static org.openecomp.sdc.onboarding.Constants.RESOURCE_WITH_TEST_ONLY;
40 import static org.openecomp.sdc.onboarding.Constants.SHA1;
41 import static org.openecomp.sdc.onboarding.Constants.SKIP_INSTALL;
42 import static org.openecomp.sdc.onboarding.Constants.SKIP_MAIN_SOURCE_COMPILE;
43 import static org.openecomp.sdc.onboarding.Constants.SKIP_PMD;
44 import static org.openecomp.sdc.onboarding.Constants.SKIP_RESOURCE_COLLECTION;
45 import static org.openecomp.sdc.onboarding.Constants.SKIP_TEST_RUN;
46 import static org.openecomp.sdc.onboarding.Constants.SKIP_TEST_SOURCE_COMPILE;
47 import static org.openecomp.sdc.onboarding.Constants.TEST;
48 import static org.openecomp.sdc.onboarding.Constants.TEST_CHECKSUM;
49 import static org.openecomp.sdc.onboarding.Constants.TEST_ONLY;
50 import static org.openecomp.sdc.onboarding.Constants.TEST_RESOURCE_ONLY;
51 import static org.openecomp.sdc.onboarding.Constants.TEST_SOURCE_CHECKSUM;
52 import static org.openecomp.sdc.onboarding.Constants.UNICORN;
53
54 import java.io.File;
55 import java.io.IOException;
56 import java.io.UncheckedIOException;
57 import java.nio.file.Files;
58 import java.nio.file.Paths;
59 import java.nio.file.StandardOpenOption;
60 import java.security.NoSuchAlgorithmException;
61 import java.util.Arrays;
62 import java.util.Collection;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Optional;
67 import java.util.Set;
68 import org.apache.maven.artifact.Artifact;
69 import org.apache.maven.execution.MavenSession;
70 import org.apache.maven.model.Plugin;
71 import org.apache.maven.model.PluginExecution;
72 import org.apache.maven.plugin.AbstractMojo;
73 import org.apache.maven.plugin.InvalidPluginDescriptorException;
74 import org.apache.maven.plugin.MavenPluginManager;
75 import org.apache.maven.plugin.MojoExecutionException;
76 import org.apache.maven.plugin.MojoFailureException;
77 import org.apache.maven.plugin.MojoNotFoundException;
78 import org.apache.maven.plugin.PluginDescriptorParsingException;
79 import org.apache.maven.plugin.PluginResolutionException;
80 import org.apache.maven.plugin.descriptor.MojoDescriptor;
81 import org.apache.maven.plugins.annotations.Component;
82 import org.apache.maven.plugins.annotations.LifecyclePhase;
83 import org.apache.maven.plugins.annotations.Mojo;
84 import org.apache.maven.plugins.annotations.Parameter;
85 import org.apache.maven.plugins.annotations.ResolutionScope;
86 import org.apache.maven.project.MavenProject;
87
88
89 @Mojo(name = "pre-compile-helper", threadSafe = true, defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
90         requiresDependencyResolution = ResolutionScope.TEST)
91 public class PreCompileHelperMojo extends AbstractMojo {
92
93     @Parameter(defaultValue = "${project}")
94     private MavenProject project;
95     @Parameter(defaultValue = "${project.artifact.groupId}:${project.artifact.artifactId}")
96     private String moduleCoordinates;
97     @Parameter(defaultValue = "${session}")
98     private MavenSession session;
99     @Parameter
100     private String excludePackaging;
101     @Parameter
102     private List<String> excludeDependencies;
103     @Parameter
104     private BuildState buildState;
105     @Parameter
106     private File mainSourceLocation;
107     @Parameter
108     private File testSourceLocation;
109     @Parameter
110     private File generatedSourceLocation;
111     @Component
112     private MavenPluginManager pluginManager;
113     @Parameter
114     private File mainResourceLocation;
115     @Parameter
116     private File testResourceLocation;
117     private Map<String, Object> resourceBuildData;
118
119     private static Map<String, String> checksumMap;
120     private long mainChecksum = 0;
121     private long testChecksum = 0;
122     private long resourceChecksum = 0;
123     Optional<String> artifactPath;
124
125     static {
126         checksumMap = readCurrentPMDState("pmd.dat");
127     }
128
129     public void execute() throws MojoExecutionException, MojoFailureException {
130
131
132         if (project.getPackaging().equals(excludePackaging)) {
133             return;
134         }
135         init();
136         processPMDCheck();
137         project.getProperties().setProperty(EMPTY_JAR, "");
138         if (!System.getProperties().containsKey(UNICORN)) {
139             return;
140         }
141         resourceChecksum = getChecksum(mainResourceLocation, ANY_EXT);
142         project.getProperties().setProperty(RESOURCE_CHECKSUM, String.valueOf(resourceChecksum));
143         byte[] sourceChecksum = calculateChecksum(mainChecksum, resourceChecksum).getBytes();
144         boolean instrumented = isCurrentModuleInstrumented();
145         artifactPath = getArtifactPathInLocalRepo(session.getLocalRepository().getUrl(), project, sourceChecksum);
146
147         boolean isFirstBuild = buildState.getBuildTime(moduleCoordinates) == 0 || !artifactPath.isPresent();
148
149         Map<String, Object> moduleBuildData = getCurrentModuleBuildData();
150         Map<String, Object> lastTimeModuleBuildData = buildState.readModuleBuildData();
151         resourceBuildData = getCurrentResourceBuildData();
152         Map<String, Object> lastTimeResourceBuildData = buildState.readResourceBuildData();
153
154         boolean buildDataSameWithPreviousBuild =
155                 isBuildDataSameWithPreviousBuild(lastTimeModuleBuildData, moduleBuildData);
156         boolean resourceMainBuildDataSameWithPreviousBuild =
157                 isResourceMainBuildDataSameWithPreviousBuild(lastTimeResourceBuildData);
158
159         boolean mainToBeCompiled = isCompileNeeded(HashMap.class.cast(moduleBuildData.get(MAIN)).keySet(), isFirstBuild,
160                 buildDataSameWithPreviousBuild);
161
162         boolean resourceDataSame = resourceBuildData.equals(lastTimeResourceBuildData);
163
164         boolean testToBeCompiled =
165                 lastTimeModuleBuildData == null || !moduleBuildData.get(TEST).equals(lastTimeModuleBuildData.get(TEST));
166         setMainBuildAttribute(mainToBeCompiled, testToBeCompiled);
167         generateSignature(sourceChecksum);
168         setTestBuild(resourceDataSame, resourceMainBuildDataSameWithPreviousBuild, testToBeCompiled, mainToBeCompiled);
169         setInstrumentedBuild(testToBeCompiled, mainToBeCompiled, instrumented);
170
171         if (!moduleBuildData.equals(lastTimeModuleBuildData) || isFirstBuild) {
172             buildState.addModuleBuildData(moduleCoordinates, moduleBuildData);
173         }
174         setResourceBuild(resourceMainBuildDataSameWithPreviousBuild, mainToBeCompiled, testToBeCompiled);
175         setJarFlags(mainToBeCompiled, instrumented, !resourceMainBuildDataSameWithPreviousBuild);
176         setInstallFlags(mainToBeCompiled, instrumented, project.getPackaging(),
177                 !resourceMainBuildDataSameWithPreviousBuild);
178
179         setArtifactPath(mainToBeCompiled, instrumented, JAR.equals(project.getPackaging()),
180                 resourceMainBuildDataSameWithPreviousBuild);
181     }
182
183     private void generateSignature(byte[] sourceChecksum) {
184         try {
185             Paths.get(project.getBuild().getDirectory()).toFile().mkdirs();
186             Files.write(Paths.get(project.getBuild().getDirectory(), project.getBuild().getFinalName() + DOT + UNICORN),
187                     sourceChecksum, StandardOpenOption.CREATE);
188         } catch (IOException e) {
189             throw new UncheckedIOException(e);
190         }
191     }
192
193     private String calculateChecksum(long mainChecksum, long resourceChecksum) throws MojoExecutionException {
194         try {
195             return getSourceChecksum(mainChecksum + COLON + resourceChecksum, SHA1);
196         } catch (NoSuchAlgorithmException e) {
197             throw new MojoExecutionException(e.getMessage(), e);
198         }
199     }
200
201     private boolean isResourceMainBuildDataSameWithPreviousBuild(Map<String, Object> lastTimeResourceBuildData) {
202         return lastTimeResourceBuildData != null && (lastTimeResourceBuildData.get(MAIN) != null && resourceBuildData
203                                                                                                             .get(MAIN)
204                                                                                                             .equals(lastTimeResourceBuildData
205                                                                                                                             .get(MAIN)));
206     }
207
208     private boolean isBuildDataSameWithPreviousBuild(Map<String, Object> lastTimeModuleBuildData,
209             Map<String, Object> moduleBuildData) {
210         return lastTimeModuleBuildData != null && (lastTimeModuleBuildData.get(MAIN) != null && moduleBuildData
211                                                                                                         .get(MAIN)
212                                                                                                         .equals(lastTimeModuleBuildData
213                                                                                                                         .get(MAIN)));
214     }
215
216     private void setInstrumentedBuild(boolean testToBeCompiled, boolean mainToBeCompiled, boolean instrumented) {
217         if (!testToBeCompiled && !mainToBeCompiled && instrumented) {
218             project.getProperties().setProperty(INSTRUMENT_ONLY, Boolean.TRUE.toString());
219             project.getProperties().remove(SKIP_MAIN_SOURCE_COMPILE);
220             project.getProperties().remove(SKIP_TEST_SOURCE_COMPILE);
221         }
222         if (testToBeCompiled && !mainToBeCompiled && instrumented) {
223             project.getProperties().setProperty(INSTRUMENT_WITH_TEST_ONLY, Boolean.TRUE.toString());
224             project.getProperties().remove(SKIP_MAIN_SOURCE_COMPILE);
225         }
226         if (instrumented) {
227             buildState.markTestsMandatoryModule(moduleCoordinates);
228             project.getProperties().setProperty(SKIP_TEST_RUN, Boolean.FALSE.toString());
229         }
230     }
231
232     private void setArtifactPath(boolean mainToBeCompiled, boolean instrumented, boolean isJar,
233             boolean resourceDataSame) {
234         if (!mainToBeCompiled && !instrumented && isJar && resourceDataSame) {
235             project.getProperties().setProperty("artifactPathToCopy", artifactPath.orElse(null));
236         }
237     }
238
239     private void setResourceBuild(boolean resourceMainBuildDataSameWithPreviousBuild, boolean mainToBeCompiled,
240             boolean testToBeCompiled) {
241         if (resourceMainBuildDataSameWithPreviousBuild) {
242             project.getProperties().setProperty(SKIP_RESOURCE_COLLECTION, Boolean.TRUE.toString());
243         } else {
244             project.getProperties().setProperty(RESOURCES_CHANGED, Boolean.TRUE.toString());
245         }
246         if (!resourceMainBuildDataSameWithPreviousBuild && !mainToBeCompiled) {
247             project.getProperties().remove(SKIP_MAIN_SOURCE_COMPILE);
248             if (!testToBeCompiled) {
249                 project.getProperties().remove(SKIP_TEST_SOURCE_COMPILE);
250                 project.getProperties().setProperty(RESOURCE_ONLY, Boolean.TRUE.toString());
251             } else {
252                 project.getProperties().setProperty(RESOURCE_WITH_TEST_ONLY, Boolean.TRUE.toString());
253             }
254         }
255     }
256
257     private void setTestBuild(boolean resourceDataSame, boolean resourceMainBuildDataSameWithPreviousBuild,
258             boolean testToBeCompiled, boolean mainToBeCompiled) {
259         if (!resourceDataSame) {
260             buildState.addResourceBuildData(moduleCoordinates, resourceBuildData);
261             project.getProperties().setProperty(SKIP_TEST_RUN, Boolean.FALSE.toString());
262             if (resourceMainBuildDataSameWithPreviousBuild && !testToBeCompiled && !mainToBeCompiled) {
263                 project.getProperties().setProperty(TEST_RESOURCE_ONLY, Boolean.TRUE.toString());
264                 project.getProperties().remove(SKIP_MAIN_SOURCE_COMPILE);
265                 project.getProperties().remove(SKIP_TEST_SOURCE_COMPILE);
266             }
267         }
268     }
269
270     private void setMainBuildAttribute(boolean mainToBeCompiled, boolean testToBeCompiled) {
271         if (!mainToBeCompiled) {
272             project.getProperties().setProperty(SKIP_MAIN_SOURCE_COMPILE, Boolean.TRUE.toString());
273         }
274         if (testToBeCompiled && !mainToBeCompiled) {
275             project.getProperties().setProperty(TEST_ONLY, Boolean.TRUE.toString());
276             project.getProperties().remove(SKIP_MAIN_SOURCE_COMPILE);
277         }
278
279         if (mainToBeCompiled || testToBeCompiled) {
280             project.getProperties().setProperty(SKIP_TEST_RUN, Boolean.FALSE.toString());
281         } else {
282             project.getProperties().setProperty(SKIP_TEST_SOURCE_COMPILE, Boolean.TRUE.toString());
283         }
284     }
285
286     private void setJarFlags(boolean compile, boolean instrumented, boolean resourceChanged) {
287         if (compile || instrumented || resourceChanged || PREFIX == UNICORN) {
288             project.getProperties().setProperty(EMPTY_JAR, "");
289         } else {
290             project.getProperties().setProperty(EMPTY_JAR, "**/*");
291             project.getProperties().setProperty("mvnDsc", "false");
292         }
293     }
294
295     private void setInstallFlags(boolean compile, boolean instrumented, String packaging, boolean resourceChanged) {
296         if (!compile && !instrumented && !resourceChanged && JAR.equals(packaging)) {
297             project.getProperties().setProperty(SKIP_INSTALL, Boolean.TRUE.toString());
298         }
299     }
300
301     private boolean isCompileNeeded(Collection<String> dependencyCoordinates, boolean isFirstBuild,
302             boolean buildDataSame) {
303         return isFirstBuild || !buildDataSame || buildState.isCompileMust(moduleCoordinates, dependencyCoordinates);
304     }
305
306     private boolean isCurrentModuleInstrumented() {
307         try {
308             return scanModuleFor(LifecyclePhase.PROCESS_CLASSES.id(), LifecyclePhase.PROCESS_TEST_CLASSES.id(),
309                     LifecyclePhase.COMPILE.id(), LifecyclePhase.TEST_COMPILE.id());
310         } catch (Exception e) {
311             getLog().debug(e);
312             return true;
313         }
314     }
315
316     boolean isCodeGenerator() {
317         try {
318             return scanModuleFor(LifecyclePhase.GENERATE_RESOURCES.id(), LifecyclePhase.GENERATE_SOURCES.id(),
319                     LifecyclePhase.GENERATE_TEST_RESOURCES.id(), LifecyclePhase.GENERATE_TEST_SOURCES.id());
320         } catch (Exception e) {
321             getLog().debug(e);
322             return true;
323         }
324     }
325
326     private Map<String, Object> getCurrentModuleBuildData() {
327         Map<String, Object> moduleBuildData = new HashMap<>();
328         moduleBuildData.put(MAIN, new HashMap<String, String>());
329         moduleBuildData.put(TEST, new HashMap<String, String>());
330         HashMap.class.cast(moduleBuildData.get(MAIN))
331                      .put(MAIN_SOURCE_CHECKSUM, project.getProperties().getProperty(MAIN_CHECKSUM));
332         HashMap.class.cast(moduleBuildData.get(TEST))
333                      .put(TEST_SOURCE_CHECKSUM, project.getProperties().getProperty(TEST_CHECKSUM));
334         if (isCodeGenerator()) {
335             HashMap.class.cast(moduleBuildData.get(MAIN))
336                          .put(GENERATED_SOURCE_CHECKSUM, getChecksum(generatedSourceLocation, JAVA_EXT));
337         }
338         if (project.getArtifacts() == null || project.getArtifacts().isEmpty()) {
339             return moduleBuildData;
340         }
341         for (Artifact dependency : project.getArtifacts()) {
342             String fileNme = dependency.getFile().getName();
343             if (excludeDependencies.contains(dependency.getScope())) {
344                 HashMap.class.cast(moduleBuildData.get(TEST))
345                              .put(dependency.getGroupId() + COLON + dependency.getArtifactId(),
346                                      fileNme.endsWith(dependency.getVersion() + DOT + JAR) ? dependency.getVersion() :
347                                              fileNme);
348                 continue;
349             }
350             HashMap.class.cast(moduleBuildData.get(MAIN))
351                          .put(dependency.getGroupId() + COLON + dependency.getArtifactId(),
352                                  fileNme.endsWith(dependency.getVersion() + DOT + JAR) ? dependency.getVersion() :
353                                          fileNme);
354         }
355         return moduleBuildData;
356     }
357
358     private static Map<String, String> readCurrentPMDState(String fileName) {
359         Optional<HashMap> val = readState(fileName, HashMap.class);
360         return val.orElseGet(HashMap::new);
361     }
362
363     private boolean isPMDMandatory(Set<Artifact> dependencies) {
364         for (Artifact artifact : dependencies) {
365             if (BuildState.isPMDRun(artifact.getGroupId() + COLON + artifact.getArtifactId())) {
366                 return true;
367             }
368         }
369         return false;
370     }
371
372     private boolean scanModuleFor(String... types)
373             throws InvalidPluginDescriptorException, PluginResolutionException, MojoNotFoundException,
374                            PluginDescriptorParsingException {
375         for (Plugin plugin : project.getBuildPlugins()) {
376             if (!("org.apache.maven.plugins".equals(plugin.getGroupId()) && plugin.getArtifactId().startsWith("maven"))
377                         && !plugin.getGroupId().startsWith("org.openecomp.sdc")) {
378                 boolean success = scanPlugin(plugin, types);
379                 if (success) {
380                     return true;
381                 }
382             }
383         }
384         return false;
385     }
386
387     private boolean scanPlugin(Plugin plugin, String... types)
388             throws InvalidPluginDescriptorException, PluginDescriptorParsingException, MojoNotFoundException,
389                            PluginResolutionException {
390         for (PluginExecution pluginExecution : plugin.getExecutions()) {
391             if (pluginExecution.getPhase() != null) {
392                 boolean phaseAvailable = Arrays.asList(types).contains(pluginExecution.getPhase());
393                 if (phaseAvailable) {
394                     return true;
395                 }
396             }
397             for (String goal : pluginExecution.getGoals()) {
398                 MojoDescriptor md = pluginManager.getMojoDescriptor(plugin, goal, project.getRemotePluginRepositories(),
399                         session.getRepositorySession());
400                 if (Arrays.asList(types).contains(md.getPhase())) {
401                     return true;
402                 }
403             }
404         }
405         return false;
406     }
407
408     private Map<String, Object> getCurrentResourceBuildData() {
409         HashMap<String, Object> resourceBuildStateData = new HashMap<>();
410         resourceBuildStateData.put(MAIN, project.getProperties().getProperty(RESOURCE_CHECKSUM));
411         resourceBuildStateData.put(TEST, getChecksum(testResourceLocation, ANY_EXT));
412         resourceBuildStateData.put("dependency", getDependencies().hashCode());
413         return resourceBuildStateData;
414     }
415
416     private Map<String, String> getDependencies() {
417         Map<String, String> dependencies = new HashMap<>();
418         for (Artifact d : project.getArtifacts()) {
419             dependencies.put(d.getGroupId() + COLON + d.getArtifactId(), d.getVersion());
420         }
421         return dependencies;
422     }
423
424     private void init() {
425         if (mainSourceLocation == null) {
426             mainSourceLocation = Paths.get(project.getBuild().getSourceDirectory()).toFile();
427         }
428         if (testSourceLocation == null) {
429             testSourceLocation = Paths.get(project.getBuild().getTestSourceDirectory()).toFile();
430         }
431         if (mainResourceLocation == null) {
432             mainResourceLocation = Paths.get(project.getBuild().getResources().get(0).getDirectory()).toFile();
433         }
434         if (testResourceLocation == null) {
435             testResourceLocation = Paths.get(project.getBuild().getTestResources().get(0).getDirectory()).toFile();
436         }
437     }
438
439     private void processPMDCheck() {
440         mainChecksum = getChecksum(mainSourceLocation, JAVA_EXT);
441         testChecksum = getChecksum(testSourceLocation, JAVA_EXT);
442         project.getProperties().setProperty(MAIN_CHECKSUM, String.valueOf(mainChecksum));
443         project.getProperties().setProperty(TEST_CHECKSUM, String.valueOf(testChecksum));
444         String checksum = mainChecksum + COLON + testChecksum;
445         if (!checksum.equals(checksumMap.get(moduleCoordinates)) || isPMDMandatory(project.getArtifacts())) {
446             project.getProperties().setProperty(SKIP_PMD, Boolean.FALSE.toString());
447             BuildState.recordPMDRun(moduleCoordinates);
448         }
449     }
450 }