View Javadoc
1   /*
2    * Copyright 2013-2020 the original author or authors.
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    *      https://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 an "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.springframework.cloud.contract.maven.verifier;
18  
19  import java.io.File;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.List;
23  
24  import org.apache.maven.execution.MavenSession;
25  import org.apache.maven.plugin.AbstractMojo;
26  import org.apache.maven.plugin.MojoExecution;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.plugin.MojoFailureException;
29  import org.apache.maven.plugins.annotations.Component;
30  import org.apache.maven.plugins.annotations.LifecyclePhase;
31  import org.apache.maven.plugins.annotations.Mojo;
32  import org.apache.maven.plugins.annotations.Parameter;
33  import org.apache.maven.project.MavenProject;
34  import org.apache.maven.project.MavenProjectHelper;
35  import org.codehaus.plexus.archiver.Archiver;
36  import org.codehaus.plexus.archiver.jar.JarArchiver;
37  
38  import static org.springframework.cloud.contract.maven.verifier.ChangeDetector.inputFilesChangeDetected;
39  
40  /**
41   * Picks the converted .json files and creates a jar. Requires convert to be executed
42   * first.
43   *
44   * @author Mariusz Smykula
45   */
46  @Mojo(name = "generateStubs", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true)
47  public class GenerateStubsMojo extends AbstractMojo {
48  
49  	@Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
50  	private File projectBuildDirectory;
51  
52  	@Parameter(defaultValue = "${project.build.finalName}", readonly = true, required = true)
53  	private String projectFinalName;
54  
55  	@Parameter(property = "stubsDirectory", defaultValue = "${project.build.directory}/stubs")
56  	private File outputDirectory;
57  
58  	/**
59  	 * Set this to "true" to bypass the whole Verifier execution.
60  	 */
61  	@Parameter(property = "spring.cloud.contract.verifier.skip", defaultValue = "false")
62  	private boolean skip;
63  
64  	/**
65  	 * Set this to "true" to bypass only JAR creation.
66  	 */
67  	@Parameter(property = "spring.cloud.contract.verifier.jar.skip", defaultValue = "false")
68  	private boolean jarSkip;
69  
70  	@Component
71  	private MavenProjectHelper projectHelper;
72  
73  	/**
74  	 * Patterns that should not be taken into account for processing.
75  	 */
76  	@Parameter
77  	private String[] excludedFiles;
78  
79  	@Parameter(defaultValue = "${project}", readonly = true)
80  	private MavenProject project;
81  
82  	@Component(role = Archiver.class, hint = "jar")
83  	private JarArchiver archiver;
84  
85  	@Parameter(defaultValue = "stubs")
86  	private String classifier;
87  
88  	/**
89  	 * If set to true then stubs jar is created only when stubs have changed since last
90  	 * build.
91  	 */
92  	@Parameter(property = "incrementalContractStubsJar", defaultValue = "true")
93  	private boolean incrementalContractStubsJar = true;
94  
95  	@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
96  	private MojoExecution mojoExecution;
97  
98  	@Parameter(defaultValue = "${session}", readonly = true, required = true)
99  	private MavenSession session;
100 
101 	/**
102 	 * When enabled, this flag will tell stub runner to throw an exception when no stubs /
103 	 * contracts were found.
104 	 */
105 	@Parameter(property = "failOnNoContracts", defaultValue = "true")
106 	private boolean failOnNoContracts;
107 
108 	public void execute() throws MojoExecutionException, MojoFailureException {
109 		if (this.skip || this.jarSkip) {
110 			getLog().info("Skipping Spring Cloud Contract Verifier execution: spring.cloud.contract.verifier.skip="
111 					+ this.skip + ", spring.cloud.contract.verifier.jar.skip=" + this.jarSkip);
112 			return;
113 		}
114 		else if (stubsOutputMissing(this.outputDirectory) && !this.failOnNoContracts) {
115 			getLog().warn(
116 					"The stubs output directory is missing, the flag to fail on no stubs if off - will continue without throwing an exception");
117 			return;
118 		}
119 		else if (stubsOutputMissing(this.outputDirectory) && this.failOnNoContracts) {
120 			throw new MojoExecutionException("Stubs could not be found: [" + this.outputDirectory.getAbsolutePath()
121 					+ "] .\nPlease make sure that spring-cloud-contract:convert was invoked");
122 		}
123 		if (this.incrementalContractStubsJar && !inputFilesChangeDetected(outputDirectory, mojoExecution, session)) {
124 			getLog().info("Nothing to generate - stubs jar is up to date");
125 			return;
126 		}
127 		File stubsJarFile = createStubJar(this.outputDirectory);
128 		this.projectHelper.attachArtifact(this.project, "jar", this.classifier, stubsJarFile);
129 	}
130 
131 	private File createStubJar(File stubsOutputDir) throws MojoFailureException, MojoExecutionException {
132 		if (!stubsOutputDir.exists()) {
133 			throw new MojoExecutionException("Stubs could not be found: [" + stubsOutputDir.getAbsolutePath()
134 					+ "] .\nPlease make sure that spring-cloud-contract:convert was invoked");
135 		}
136 		String stubArchiveName = this.projectFinalName + "-" + this.classifier + ".jar";
137 		File stubsJarFile = new File(this.projectBuildDirectory, stubArchiveName);
138 		String[] excludes = excludes();
139 		getLog().info(
140 				"Files matching this pattern will be excluded from " + "stubs generation " + Arrays.toString(excludes));
141 		try {
142 			this.archiver.addDirectory(stubsOutputDir, new String[] { "**/*.*" },
143 					excludedFilesEmpty() ? new String[0] : this.excludedFiles);
144 			this.archiver.setCompress(true);
145 			this.archiver.setDestFile(stubsJarFile);
146 			this.archiver.addConfiguredManifest(ManifestCreator.createManifest(this.project));
147 			this.archiver.createArchive();
148 		}
149 		catch (Exception e) {
150 			throw new MojoFailureException("Exception while packaging " + this.classifier + " jar.", e);
151 		}
152 		return stubsJarFile;
153 	}
154 
155 	private boolean stubsOutputMissing(File stubsOutputDir) {
156 		return !stubsOutputDir.exists();
157 	}
158 
159 	private String[] excludes() {
160 		List<String> excludes = new ArrayList<>();
161 		if (!excludedFilesEmpty()) {
162 			excludes.addAll(Arrays.asList(this.excludedFiles));
163 		}
164 		String[] array = new String[excludes.size()];
165 		array = excludes.toArray(array);
166 		return array;
167 	}
168 
169 	private boolean excludedFilesEmpty() {
170 		return this.excludedFiles == null || this.excludedFiles.length == 0;
171 	}
172 
173 }