PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-spring-scanner-maven-plugin/src/main/java/com/atlassian/plugin/spring/scanner/maven/AtlassianSpringScannerMojo.java

https://bitbucket.org/fgdreis/atlassian-spring-scanner
Java | 287 lines | 213 code | 38 blank | 36 comment | 14 complexity | 418dfc5323d3aef74c69a25b5aca6e0c MD5 | raw file
  1. package com.atlassian.plugin.spring.scanner.maven;
  2. import com.atlassian.plugin.spring.scanner.core.AtlassianSpringByteCodeScanner;
  3. import com.atlassian.plugin.spring.scanner.core.ByteCodeScannerConfiguration;
  4. import com.google.common.base.Function;
  5. import com.google.common.base.Predicate;
  6. import com.google.common.collect.Iterables;
  7. import com.google.common.collect.Lists;
  8. import com.google.common.collect.Sets;
  9. import org.apache.maven.artifact.Artifact;
  10. import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
  11. import org.apache.maven.model.Dependency;
  12. import org.apache.maven.plugin.AbstractMojo;
  13. import org.apache.maven.plugin.MojoExecutionException;
  14. import org.apache.maven.plugin.MojoFailureException;
  15. import org.apache.maven.plugins.annotations.Component;
  16. import org.apache.maven.plugins.annotations.LifecyclePhase;
  17. import org.apache.maven.plugins.annotations.Mojo;
  18. import org.apache.maven.plugins.annotations.Parameter;
  19. import org.apache.maven.plugins.annotations.ResolutionScope;
  20. import org.apache.maven.project.MavenProject;
  21. import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
  22. import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
  23. import org.apache.maven.shared.dependency.graph.DependencyNode;
  24. import org.reflections.util.ClasspathHelper;
  25. import javax.annotation.Nullable;
  26. import java.io.File;
  27. import java.net.MalformedURLException;
  28. import java.net.URL;
  29. import java.util.ArrayList;
  30. import java.util.Collections;
  31. import java.util.List;
  32. import java.util.NoSuchElementException;
  33. import java.util.Set;
  34. import java.util.stream.Collectors;
  35. import static java.lang.String.format;
  36. import static org.reflections.util.Utils.isEmpty;
  37. /**
  38. * Maven plugin for atlassian-spring-scanning.
  39. * <p>
  40. * Use it by configuring the pom with:
  41. * <pre><code>
  42. * &lt;build&gt;
  43. * &lt;plugins&gt;
  44. * &lt;plugin&gt;
  45. * &lt;groupId&gt;com.atlassian.plugins&lt;/groupId&gt;
  46. * &lt;artifactId&gt;atlassian-spring-scanner-maven-plugin&lt;/artifactId&gt;
  47. * &lt;version&gt;${project.version}#60;/version&gt;
  48. * &lt;executions&gt;
  49. * &lt;execution&gt;
  50. * &lt;goals&gt;
  51. * &lt;goal&gt;atlassian-spring-scanner&lt;/goal&gt;
  52. * &lt;/goals&gt;
  53. * &lt;phase&gt;process-classes&lt;/phase&gt;
  54. * &lt;/execution&gt;
  55. * &lt;/executions&gt;
  56. * &lt;configuration&gt;
  57. * &lt;... optional configuration here&gt;
  58. * &lt;/configuration&gt;
  59. * &lt;/plugin&gt;
  60. * &lt;/plugins&gt;
  61. * &lt;/build&gt;
  62. * </code></pre>
  63. */
  64. @Mojo(
  65. name = "atlassian-spring-scanner",
  66. defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
  67. requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME
  68. )
  69. public class AtlassianSpringScannerMojo extends AbstractMojo {
  70. private static final String OUR_NAME = "Atlassian Spring Byte Code Scanner";
  71. private static final String DEFAULT_INCLUDE_EXCLUDE = "-java\\..*, -javax\\..*, -sun\\..*, -com\\.sun\\..*";
  72. @Parameter(defaultValue = "${project}", readonly = true)
  73. private MavenProject project;
  74. @Parameter(defaultValue = DEFAULT_INCLUDE_EXCLUDE)
  75. private String includeExclude;
  76. @Parameter(defaultValue = "false")
  77. private Boolean parallel;
  78. @Parameter(defaultValue = "false")
  79. private Boolean verbose;
  80. @Parameter()
  81. private List<Dependency> scannedDependencies = new ArrayList<Dependency>();
  82. @Component(hint = "default")
  83. private DependencyGraphBuilder dependencyGraphBuilder;
  84. public void execute() throws MojoExecutionException, MojoFailureException {
  85. getLog().info("Starting " + OUR_NAME + "...");
  86. getLog().info("");
  87. long then = System.currentTimeMillis();
  88. String outputDirectory = resolveOutputDirectory();
  89. if (!new File(outputDirectory).exists()) {
  90. getLog().warn(format("Skipping because %s was not found", outputDirectory));
  91. return;
  92. }
  93. warnInvalidScannedDependencies();
  94. ByteCodeScannerConfiguration.Builder config = ByteCodeScannerConfiguration.builder()
  95. .setOutputDirectory(outputDirectory)
  96. .setClassPathUrls(parseUrls())
  97. .setIncludeExclude(includeExclude)
  98. .setLog(makeLogger())
  99. .setVerbose(verbose);
  100. // go!
  101. AtlassianSpringByteCodeScanner scanner = new AtlassianSpringByteCodeScanner(config.build());
  102. long ms = System.currentTimeMillis() - then;
  103. getLog().info("");
  104. getLog().info(format("\tAnalysis ran in %d ms.", ms));
  105. getLog().info(format("\tEncountered %d total classes", scanner.getStats().getClassesEncountered()));
  106. getLog().info(format("\tProcessed %d annotated classes", scanner.getStats().getComponentClassesEncountered()));
  107. if (!scanner.getErrors().getErrorsEncountered().isEmpty()) {
  108. final String error = format("\t %d errors encountered during class analysis: \n\t %s",
  109. scanner.getErrors().getErrorsEncountered().size(),
  110. scanner.getErrors().getErrorsEncountered().stream().collect(Collectors.joining("\n\t")));
  111. getLog().error(error);
  112. throw new IllegalStateException(error);
  113. }
  114. }
  115. private org.slf4j.Logger makeLogger() {
  116. return new MavenLogAdapter(getLog());
  117. }
  118. private Set<URL> parseUrls() throws MojoExecutionException {
  119. final Set<URL> urls = Sets.newHashSet();
  120. URL outputDirUrl = parseOutputDirUrl();
  121. urls.add(outputDirUrl);
  122. if (!isEmpty(includeExclude)) {
  123. for (String string : includeExclude.split(",")) {
  124. String trimmed = string.trim();
  125. char prefix = trimmed.charAt(0);
  126. String pattern = trimmed.substring(1);
  127. if (prefix == '+') {
  128. logVerbose(format("\tAdding include / exclude %s", prefix));
  129. urls.addAll(ClasspathHelper.forPackage(pattern));
  130. }
  131. }
  132. }
  133. final Set<URL> dependencyJars = Sets.newLinkedHashSet();
  134. final Iterable<Artifact> projectArtifacts = getProjectArtifacts();
  135. final Iterable<Artifact> scannedArtifacts = resolveArtifacts(getScannedArtifacts(), projectArtifacts);
  136. final Iterable<Artifact> ignoredArtifacts = Iterables.filter(projectArtifacts, new Predicate<Artifact>() {
  137. @Override
  138. public boolean apply(@Nullable Artifact input) {
  139. return !Iterables.any(scannedArtifacts, artifactMatchesGAV(input));
  140. }
  141. });
  142. for (Artifact artifact : scannedArtifacts) {
  143. logVerbose(format("\t(/) Including dependency for scanning %s:%s:%s", artifact.getGroupId(), artifact.getArtifactId(), artifact.getScope()));
  144. File file = artifact.getFile();
  145. try {
  146. URL url = file.toURI().toURL();
  147. dependencyJars.add(url);
  148. } catch (MalformedURLException e) {
  149. getLog().warn(format("Enable to create URL from plugin artifact : %s", file), e);
  150. }
  151. }
  152. for (Artifact artifact : ignoredArtifacts) {
  153. logVerbose(format("\t(X) Ignoring dependency for scanning %s:%s:%s", artifact.getGroupId(), artifact.getArtifactId(), artifact.getScope()));
  154. }
  155. urls.addAll(dependencyJars);
  156. getLog().info("\t(/) The following directory will be scanned for annotations :");
  157. getLog().info(format("\t\t%s", outputDirUrl));
  158. if (dependencyJars.size() > 0) {
  159. getLog().info("");
  160. getLog().info("\t(/) The following dependencies will also be scanned for annotations : ");
  161. getLog().info("");
  162. for (URL jar : dependencyJars) {
  163. getLog().info(format("\t\t%s", jar));
  164. }
  165. }
  166. return urls;
  167. }
  168. private void logVerbose(String message) {
  169. if (verbose) {
  170. getLog().info(message);
  171. }
  172. }
  173. private boolean isSensibleScope(final Artifact artifact) {
  174. return !Artifact.SCOPE_TEST.equals(artifact.getScope());
  175. }
  176. private URL parseOutputDirUrl() throws MojoExecutionException {
  177. try {
  178. File outputDirectoryFile = new File(resolveOutputDirectory() + '/');
  179. return outputDirectoryFile.toURI().toURL();
  180. } catch (MalformedURLException e) {
  181. throw new MojoExecutionException(e.getMessage(), e);
  182. }
  183. }
  184. private String resolveOutputDirectory() {
  185. return getProject().getBuild().getOutputDirectory();
  186. }
  187. private MavenProject getProject() {
  188. return project;
  189. }
  190. private Predicate<Artifact> artifactMatchesGAV(final Artifact artifact) {
  191. return new Predicate<Artifact>() {
  192. @Override
  193. public boolean apply(final Artifact input) {
  194. return artifact.getGroupId().equals(input.getGroupId()) &&
  195. artifact.getArtifactId().equals(input.getArtifactId()) &&
  196. artifact.getVersion().equals(input.getVersion()
  197. );
  198. }
  199. };
  200. }
  201. /**
  202. * The artifacts in the project list are resolved, the scanned ones are not but rather are logical and hence we
  203. * have to make them real
  204. *
  205. * @param scannedArtifacts the GA artifacts we want scanned
  206. * @param projectArtifacts the resolved list including the above
  207. * @return resolved versions of the artifacts
  208. */
  209. private Iterable<Artifact> resolveArtifacts(List<Artifact> scannedArtifacts, final Iterable<Artifact> projectArtifacts) {
  210. return Iterables.transform(scannedArtifacts, new Function<Artifact, Artifact>() {
  211. @Override
  212. public Artifact apply(Artifact input) {
  213. try {
  214. return Iterables.find(projectArtifacts, artifactMatchesGAV(input));
  215. } catch (NoSuchElementException e) {
  216. throw new RuntimeException("Unable to find " + input, e);
  217. }
  218. }
  219. });
  220. }
  221. private DependencyNode getDependencyGraph(MavenProject project) {
  222. try {
  223. return dependencyGraphBuilder.buildDependencyGraph(project, new ArtifactFilter() {
  224. @Override
  225. public boolean include(Artifact artifact) {
  226. return isSensibleScope(artifact);
  227. }
  228. });
  229. } catch (DependencyGraphBuilderException e) {
  230. throw new RuntimeException(e);
  231. }
  232. }
  233. private List<Artifact> getProjectArtifacts() {
  234. ArrayList<Artifact> artifacts = Lists.newArrayList(project.getArtifacts());
  235. Collections.sort(artifacts);
  236. return artifacts;
  237. }
  238. private List<Artifact> getScannedArtifacts() {
  239. DependencyNode dependencyGraph = getDependencyGraph(project);
  240. return ScannedDependencyArtifactBuilder.buildScannedArtifacts(dependencyGraph, scannedDependencies);
  241. }
  242. private void warnInvalidScannedDependencies() {
  243. for (Dependency dependency : scannedDependencies) {
  244. if ((dependency.getArtifactId().contains("*")) && (!"*".equals(dependency.getArtifactId()))) {
  245. getLog().warn(format("Invalid artifact ID %s in scannedDependencies. Partial wildcards are not currently supported.", dependency.getArtifactId()));
  246. }
  247. }
  248. }
  249. }