1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.jar;
20
21 import java.io.File;
22 import java.nio.file.FileSystems;
23 import java.util.Arrays;
24 import java.util.Map;
25 import java.util.Optional;
26
27 import org.apache.maven.archiver.MavenArchiveConfiguration;
28 import org.apache.maven.archiver.MavenArchiver;
29 import org.apache.maven.execution.MavenSession;
30 import org.apache.maven.plugin.AbstractMojo;
31 import org.apache.maven.plugin.MojoExecutionException;
32 import org.apache.maven.plugins.annotations.Component;
33 import org.apache.maven.plugins.annotations.Parameter;
34 import org.apache.maven.project.MavenProject;
35 import org.apache.maven.project.MavenProjectHelper;
36 import org.apache.maven.shared.model.fileset.FileSet;
37 import org.apache.maven.shared.model.fileset.util.FileSetManager;
38 import org.apache.maven.toolchain.ToolchainManager;
39 import org.codehaus.plexus.archiver.Archiver;
40 import org.codehaus.plexus.archiver.jar.JarArchiver;
41 import org.codehaus.plexus.archiver.util.DefaultFileSet;
42
43
44
45
46
47
48
49 public abstract class AbstractJarMojo extends AbstractMojo {
50
51 private static final String[] DEFAULT_EXCLUDES = new String[] {"**/package.html"};
52
53 private static final String[] DEFAULT_INCLUDES = new String[] {"**/**"};
54
55 private static final String MODULE_DESCRIPTOR_FILE_NAME = "module-info.class";
56
57 private static final String SEPARATOR = FileSystems.getDefault().getSeparator();
58
59 @Component
60 private ToolchainsJdkSpecification toolchainsJdkSpecification;
61
62 @Component
63 private ToolchainManager toolchainManager;
64
65
66
67
68
69 @Parameter
70 private String[] includes;
71
72
73
74
75
76 @Parameter
77 private String[] excludes;
78
79
80
81
82 @Parameter(defaultValue = "${project.build.directory}", required = true)
83 private File outputDirectory;
84
85
86
87
88 @Parameter(defaultValue = "${project.build.finalName}", readonly = true)
89 private String finalName;
90
91
92
93
94 @Component
95 private Map<String, Archiver> archivers;
96
97
98
99
100 @Parameter(defaultValue = "${project}", readonly = true, required = true)
101 private MavenProject project;
102
103
104
105
106 @Parameter(defaultValue = "${session}", readonly = true, required = true)
107 private MavenSession session;
108
109
110
111
112
113 @Parameter
114 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
115
116
117
118
119
120
121
122
123 @Parameter(property = "jar.useDefaultManifestFile", defaultValue = "false")
124 @Deprecated
125 private boolean useDefaultManifestFile;
126
127
128
129
130 @Component
131 private MavenProjectHelper projectHelper;
132
133
134
135
136
137
138
139
140
141
142
143
144 @Parameter(property = "maven.jar.forceCreation", defaultValue = "false")
145 private boolean forceCreation;
146
147
148
149
150 @Parameter(defaultValue = "false")
151 private boolean skipIfEmpty;
152
153
154
155
156
157
158
159
160
161 @Parameter(defaultValue = "${project.build.outputTimestamp}")
162 private String outputTimestamp;
163
164
165
166
167
168
169
170 @Parameter(property = "maven.jar.detectMultiReleaseJar", defaultValue = "true")
171 private boolean detectMultiReleaseJar;
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 @Parameter(defaultValue = "true")
203 private boolean addDefaultExcludes;
204
205
206
207
208
209 protected abstract File getClassesDirectory();
210
211
212
213
214
215
216 protected final MavenProject getProject() {
217 return project;
218 }
219
220
221
222
223
224 protected abstract String getClassifier();
225
226
227
228
229
230 protected abstract String getType();
231
232
233
234
235
236
237
238
239
240 protected File getJarFile(File basedir, String resultFinalName, String classifier) {
241 if (basedir == null) {
242 throw new IllegalArgumentException("basedir is not allowed to be null");
243 }
244 if (resultFinalName == null) {
245 throw new IllegalArgumentException("finalName is not allowed to be null");
246 }
247
248 String fileName = resultFinalName + (hasClassifier() ? "-" + classifier : "") + ".jar";
249
250 return new File(basedir, fileName);
251 }
252
253
254
255
256
257
258 public File createArchive() throws MojoExecutionException {
259 File jarFile = getJarFile(outputDirectory, finalName, getClassifier());
260
261 FileSetManager fileSetManager = new FileSetManager();
262 FileSet jarContentFileSet = new FileSet();
263 jarContentFileSet.setDirectory(getClassesDirectory().getAbsolutePath());
264 jarContentFileSet.setIncludes(Arrays.asList(getIncludes()));
265 jarContentFileSet.setExcludes(Arrays.asList(getExcludes()));
266
267 String[] includedFiles = fileSetManager.getIncludedFiles(jarContentFileSet);
268
269 if (detectMultiReleaseJar
270 && Arrays.stream(includedFiles)
271 .anyMatch(p -> p.startsWith("META-INF" + SEPARATOR + "versions" + SEPARATOR))) {
272 getLog().debug("Adding 'Multi-Release: true' manifest entry.");
273 archive.addManifestEntry("Multi-Release", "true");
274 }
275
276
277
278
279
280
281
282
283
284 boolean containsModuleDescriptor =
285 Arrays.stream(includedFiles).anyMatch(p -> p.endsWith(MODULE_DESCRIPTOR_FILE_NAME));
286
287 String archiverName = containsModuleDescriptor ? "mjar" : "jar";
288
289 MavenArchiver archiver = new MavenArchiver();
290 archiver.setCreatedBy("Maven JAR Plugin", "org.apache.maven.plugins", "maven-jar-plugin");
291 archiver.setArchiver((JarArchiver) archivers.get(archiverName));
292 archiver.setOutputFile(jarFile);
293
294 Optional.ofNullable(toolchainManager.getToolchainFromBuildContext("jdk", session))
295 .ifPresent(toolchain -> toolchainsJdkSpecification
296 .getJDKSpecification(toolchain)
297 .ifPresent(jdkSpec -> {
298 archive.addManifestEntry("Build-Jdk-Spec", jdkSpec);
299 archive.addManifestEntry(
300 "Build-Tool-Jdk-Spec", System.getProperty("java.specification.version"));
301 archiver.setBuildJdkSpecDefaultEntry(false);
302 getLog().info("Set Build-Jdk-Spec based on toolchain in maven-jar-plugin " + toolchain);
303 }));
304
305
306 archiver.configureReproducibleBuild(outputTimestamp);
307
308 archive.setForced(forceCreation);
309
310 try {
311 File contentDirectory = getClassesDirectory();
312 if (!contentDirectory.exists()) {
313 if (!forceCreation) {
314 getLog().warn("JAR will be empty - no content was marked for inclusion!");
315 }
316 } else {
317 archiver.getArchiver().addFileSet(getFileSet(contentDirectory));
318 }
319
320 archiver.createArchive(session, project, archive);
321
322 return jarFile;
323 } catch (Exception e) {
324
325 throw new MojoExecutionException("Error assembling JAR", e);
326 }
327 }
328
329
330
331
332
333 @Override
334 public void execute() throws MojoExecutionException {
335 if (useDefaultManifestFile) {
336 throw new MojoExecutionException("You are using 'useDefaultManifestFile' which has been removed"
337 + " from the maven-jar-plugin. "
338 + "Please see the >>Major Version Upgrade to version 3.0.0<< on the plugin site.");
339 }
340
341 if (skipIfEmpty
342 && (!getClassesDirectory().exists() || getClassesDirectory().list().length < 1)) {
343 getLog().info("Skipping packaging of the " + getType());
344 } else {
345 File jarFile = createArchive();
346
347 if (hasClassifier()) {
348 projectHelper.attachArtifact(getProject(), getType(), getClassifier(), jarFile);
349 } else {
350 if (projectHasAlreadySetAnArtifact()) {
351 throw new MojoExecutionException("You have to use a classifier "
352 + "to attach supplemental artifacts to the project instead of replacing them.");
353 }
354 getProject().getArtifact().setFile(jarFile);
355 }
356 }
357 }
358
359 private boolean projectHasAlreadySetAnArtifact() {
360 if (getProject().getArtifact().getFile() == null) {
361 return false;
362 }
363
364 return getProject().getArtifact().getFile().isFile();
365 }
366
367
368
369
370
371
372 protected boolean hasClassifier() {
373 return getClassifier() != null && !getClassifier().trim().isEmpty();
374 }
375
376 private String[] getIncludes() {
377 if (includes != null && includes.length > 0) {
378 return includes;
379 }
380 return DEFAULT_INCLUDES;
381 }
382
383 private String[] getExcludes() {
384 if (excludes != null && excludes.length > 0) {
385 return excludes;
386 }
387 return DEFAULT_EXCLUDES;
388 }
389
390 private org.codehaus.plexus.archiver.FileSet getFileSet(File contentDirectory) {
391 DefaultFileSet fileSet = DefaultFileSet.fileSet(contentDirectory)
392 .prefixed("")
393 .includeExclude(getIncludes(), getExcludes())
394 .includeEmptyDirs(true);
395
396 fileSet.setUsingDefaultExcludes(addDefaultExcludes);
397 return fileSet;
398 }
399 }