1 package org.codehaus.mojo.jaxb2;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.apache.maven.model.Resource;
23 import org.apache.maven.plugin.AbstractMojo;
24 import org.apache.maven.plugin.MojoExecution;
25 import org.apache.maven.plugin.MojoExecutionException;
26 import org.apache.maven.plugin.MojoFailureException;
27 import org.apache.maven.plugin.logging.Log;
28 import org.apache.maven.plugins.annotations.Component;
29 import org.apache.maven.plugins.annotations.Parameter;
30 import org.apache.maven.project.MavenProject;
31 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
32 import org.codehaus.mojo.jaxb2.shared.Validate;
33 import org.codehaus.mojo.jaxb2.shared.environment.EnvironmentFacet;
34 import org.codehaus.mojo.jaxb2.shared.filters.Filter;
35 import org.codehaus.mojo.jaxb2.shared.filters.pattern.FileFilterAdapter;
36 import org.codehaus.mojo.jaxb2.shared.filters.pattern.PatternFileFilter;
37 import org.codehaus.mojo.jaxb2.shared.version.DependencyInfo;
38 import org.codehaus.mojo.jaxb2.shared.version.DependsFileParser;
39 import org.sonatype.plexus.build.incremental.BuildContext;
40
41 import java.io.File;
42 import java.io.FileFilter;
43 import java.io.IOException;
44 import java.net.URL;
45 import java.nio.file.Path;
46 import java.nio.file.Paths;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.Locale;
52 import java.util.Map;
53 import java.util.SortedMap;
54 import java.util.TreeMap;
55 import java.util.concurrent.atomic.AtomicInteger;
56 import java.util.regex.Pattern;
57
58 /**
59 * Abstract Mojo which collects common infrastructure, required and needed
60 * by all subclass Mojos in the JAXB2 maven plugin codebase.
61 *
62 * @author <a href="mailto:lj@jguru.se">Lennart Jörelid</a>
63 */
64 public abstract class AbstractJaxbMojo extends AbstractMojo {
65
66 /**
67 * Standard name of the generated JAXB episode file.
68 */
69 public static final String STANDARD_EPISODE_FILENAME = "sun-jaxb.episode";
70
71 /**
72 * Standard name of the package-info.java file which may contain
73 * JAXB annotations and Package JavaDoc.
74 */
75 public static final String PACKAGE_INFO_FILENAME = "package-info.java";
76
77 /**
78 * Platform-independent newline control string.
79 */
80 public static final String NEWLINE = System.getProperty("line.separator");
81
82 /**
83 * Pattern matching strings containing whitespace (or consisting only of whitespace).
84 */
85 public static final Pattern CONTAINS_WHITESPACE = Pattern.compile("(\\S*\\s+\\S*)+", Pattern.UNICODE_CASE);
86
87 /**
88 * <p>Standard excludes Filters for all Java generator Mojos.
89 * The List is unmodifiable, and contains Filters on the following form:</p>
90 * <pre>
91 * <code>
92 * // The standard exclude filters contain simple, exclude pattern filters.
93 * final List<Filter<File>> tmp = new ArrayList<Filter<File>>();
94 * tmp.add(new PatternFileFilter(Arrays.asList({"README.*", "\\.xml", "\\.txt"}), true));
95 * tmp.add(new FileFilterAdapter(new FileFilter() {
96 *
97 * @Override
98 * public boolean accept(final File aFileOrDir) {
99 *
100 * // Check sanity
101 * if (aFileOrDir == null) {
102 * return false;
103 * }
104 *
105 * final String name = aFileOrDir.getName();
106 *
107 * // Ignore hidden files and CVS directories
108 * return name.startsWith(".")
109 * || (aFileOrDir.isDirectory() && name.equals("CVS"));
110 *
111 * }
112 * }));
113 * </code>
114 * </pre>
115 * <p><b>Note</b>! Since the plugin is currently developed in jdk 1.7-compliant code, we cannot
116 * use lambdas within Filters just yet.</p>
117 */
118 public static final List<Filter<File>> STANDARD_EXCLUDE_FILTERS;
119
120 private static final List<String> RELEVANT_GROUPIDS =
121 Arrays.asList("org.glassfish.jaxb", "javax.xml.bind");
122 private static final String OWN_ARTIFACT_ID = "jaxb2-maven-plugin";
123 private static final String SYSTEM_FILE_ENCODING_PROPERTY = "file.encoding";
124 private static final String[] STANDARD_EXCLUDE_SUFFIXES = {"README.*", "\\.xml", "\\.txt"};
125
126 static {
127
128 // The standard exclude filters contain simple, exclude pattern filters.
129 final List<Filter<File>> tmp = new ArrayList<Filter<File>>();
130 tmp.add(new PatternFileFilter(Arrays.asList(STANDARD_EXCLUDE_SUFFIXES), true));
131 tmp.add(new FileFilterAdapter(new FileFilter() {
132 @Override
133 public boolean accept(final File aFileOrDir) {
134
135 // Check sanity
136 if (aFileOrDir == null) {
137 return false;
138 }
139
140 final String name = aFileOrDir.getName();
141
142 // Ignore hidden files and CVS directories
143 return name.startsWith(".")
144 || (aFileOrDir.isDirectory() && name.equals("CVS"));
145
146 }
147 }));
148
149 // Make STANDARD_EXCLUDE_FILTERS be unmodifiable.
150 STANDARD_EXCLUDE_FILTERS = Collections.unmodifiableList(tmp);
151 }
152
153 /**
154 * The Plexus BuildContext is used to identify files or directories modified since last build,
155 * implying functionality used to define if java generation must be performed again.
156 */
157 @Component
158 private BuildContext buildContext;
159
160 /**
161 * The injected Maven project.
162 */
163 @Parameter(defaultValue = "${project}", readonly = true)
164 private MavenProject project;
165
166 /**
167 * Note that the execution parameter will be injected ONLY if this plugin is executed as part
168 * of a maven standard lifecycle - as opposed to directly invoked with a direct invocation.
169 * When firing this mojo directly (i.e. {@code mvn xjc:something} or {@code mvn schemagen:something}), the
170 * {@code execution} object will not be injected.
171 */
172 @Parameter(defaultValue = "${mojoExecution}", readonly = true)
173 private MojoExecution execution;
174
175 /**
176 * <p>The directory where the staleFile is found.
177 * The staleFile assists in determining if re-generation of JAXB build products is required.</p>
178 * <p>While it is permitted to re-define the staleFileDirectory, it is recommended to keep it
179 * below the <code>${project.build.directory}</code>, to ensure that JAXB code or XSD re-generation
180 * occurs after cleaning the project.</p>
181 *
182 * @since 2.0
183 */
184 @Parameter(defaultValue = "${project.build.directory}/jaxb2", readonly = true, required = true)
185 protected File staleFileDirectory;
186
187 /**
188 * <p>Defines the encoding used by XJC (for generating Java Source files) and schemagen (for generating XSDs).
189 * The corresponding argument parameter for XJC and SchemaGen is: {@code encoding}.</p>
190 * <p>The algorithm for finding the encoding to use is as follows
191 * (where the first non-null value found is used for encoding):
192 * <ol>
193 * <li>If the configuration property is explicitly given within the plugin's configuration, use that value.</li>
194 * <li>If the Maven property <code>project.build.sourceEncoding</code> is defined, use its value.</li>
195 * <li>Otherwise use the value from the system property <code>file.encoding</code>.</li>
196 * </ol>
197 * </p>
198 *
199 * @see #getEncoding(boolean)
200 * @since 2.0
201 */
202 @Parameter(defaultValue = "${project.build.sourceEncoding}")
203 private String encoding;
204
205 /**
206 * <p>A Locale definition to create and set the system (default) Locale when the XJB or SchemaGen tools executes.
207 * The Locale will be reset to its default value after the execution of XJC or SchemaGen is complete.</p>
208 * <p>The configuration parameter must be supplied on the form {@code language[,country[,variant]]},
209 * such as {@code sv,SE} or {@code fr}. Refer to
210 * {@code org.codehaus.mojo.jaxb2.shared.environment.locale.LocaleFacet.createFor(String, Log)} for further
211 * information.</p>
212 * <p><strong>Example</strong> (assigns french locale):</p>
213 * <pre>
214 * <code>
215 * <configuration>
216 * <locale>fr</locale>
217 * </configuration>
218 * </code>
219 * </pre>
220 *
221 * @see org.codehaus.mojo.jaxb2.shared.environment.locale.LocaleFacet#createFor(String, Log)
222 * @see Locale#getAvailableLocales()
223 * @since 2.2
224 */
225 @Parameter(required = false)
226 protected String locale;
227
228 /**
229 * <p>Defines a set of extra EnvironmentFacet instances which are used to further configure the
230 * ToolExecutionEnvironment used by this plugin to fire XJC or SchemaGen.</p>
231 * <p><em>Example:</em> If you implement the EnvironmentFacet interface in the class
232 * {@code org.acme.MyCoolEnvironmentFacetImplementation}, its {@code setup()} method is called before the
233 * XJC or SchemaGen tools are executed to setup some facet of their Execution environment. Correspondingly, the
234 * {@code restore()} method in your {@code org.acme.MyCoolEnvironmentFacetImplementation} class is invoked after
235 * the XJC or SchemaGen execution terminates.</p>
236 * <pre>
237 * <code>
238 * <configuration>
239 * ...
240 * <extraFacets>
241 * <extraFacet implementation="org.acme.MyCoolEnvironmentFacetImplementation" />
242 * </extraFacets>
243 * ...
244 * </configuration>
245 * </code>
246 * </pre>
247 *
248 * @see EnvironmentFacet
249 * @see org.codehaus.mojo.jaxb2.shared.environment.ToolExecutionEnvironment#add(EnvironmentFacet)
250 * @since 2.2
251 */
252 @Parameter(required = false)
253 protected List<EnvironmentFacet> extraFacets;
254
255 /**
256 * Adds the supplied Resource to the project using the appropriate scope (i.e. resource or testResource)
257 * depending on the exact implementation of this AbstractJaxbMojo.
258 *
259 * @param resource The resource to add.
260 */
261 protected abstract void addResource(final Resource resource);
262
263 /**
264 * The Plexus BuildContext is used to identify files or directories modified since last build,
265 * implying functionality used to define if java generation must be performed again.
266 *
267 * @return the active Plexus BuildContext.
268 */
269 protected final BuildContext getBuildContext() {
270 return getInjectedObject(buildContext, "buildContext");
271 }
272
273 /**
274 * @return The active MavenProject.
275 */
276 protected final MavenProject getProject() {
277 return getInjectedObject(project, "project");
278 }
279
280 /**
281 * @return The active MojoExecution.
282 */
283 public MojoExecution getExecution() {
284 return getInjectedObject(execution, "execution");
285 }
286
287 /**
288 * {@inheritDoc}
289 */
290 @Override
291 public final void execute() throws MojoExecutionException, MojoFailureException {
292
293 // 0) Get the log and its relevant level
294 final Log log = getLog();
295 final boolean isDebugEnabled = log.isDebugEnabled();
296 final boolean isInfoEnabled = log.isInfoEnabled();
297
298 // 1) Should we skip execution?
299 if (shouldExecutionBeSkipped()) {
300
301 if (isDebugEnabled) {
302 log.debug("Skipping execution, as instructed.");
303 }
304 return;
305 }
306
307 // 2) Printout relevant version information.
308 if (isDebugEnabled) {
309 logPluginAndJaxbDependencyInfo();
310 }
311
312 // 3) Are generated files stale?
313 if (isReGenerationRequired()) {
314
315 if (performExecution()) {
316
317 // As instructed by the performExecution() method, update
318 // the timestamp of the stale File.
319 updateStaleFileTimestamp();
320
321 // Hack to support M2E
322 buildContext.refresh(getOutputDirectory());
323
324 } else if (isInfoEnabled) {
325 log.info("Not updating staleFile timestamp as instructed.");
326 }
327 } else if (isInfoEnabled) {
328 log.info("No changes detected in schema or binding files - skipping JAXB generation.");
329 }
330
331 // 4) If the output directories exist, add them to the MavenProject's source directories
332 if (getOutputDirectory().exists() && getOutputDirectory().isDirectory()) {
333
334 final String canonicalPathToOutputDirectory = FileSystemUtilities.getCanonicalPath(getOutputDirectory());
335
336 if (log.isDebugEnabled()) {
337 log.debug("Adding existing JAXB outputDirectory [" + canonicalPathToOutputDirectory
338 + "] to Maven's sources.");
339 }
340
341 // Add the output Directory.
342 getProject().addCompileSourceRoot(canonicalPathToOutputDirectory);
343 }
344 }
345
346 /**
347 * Implement this method to check if this AbstractJaxbMojo should skip executing altogether.
348 *
349 * @return {@code true} to indicate that this AbstractJaxbMojo should bail out of its execute method.
350 */
351 protected abstract boolean shouldExecutionBeSkipped();
352
353 /**
354 * @return {@code true} to indicate that this AbstractJaxbMojo should be run since its generated files were
355 * either stale or not present, and {@code false} otherwise.
356 */
357 protected abstract boolean isReGenerationRequired();
358
359 /**
360 * <p>Implement this method to perform this Mojo's execution.
361 * This method will only be called if {@code !shouldExecutionBeSkipped() && isReGenerationRequired()}.</p>
362 *
363 * @return {@code true} if the timestamp of the stale file should be updated.
364 * @throws MojoExecutionException if an unexpected problem occurs.
365 * Throwing this exception causes a "BUILD ERROR" message to be displayed.
366 * @throws MojoFailureException if an expected problem (such as a compilation failure) occurs.
367 * Throwing this exception causes a "BUILD FAILURE" message to be displayed.
368 */
369 protected abstract boolean performExecution() throws MojoExecutionException, MojoFailureException;
370
371 /**
372 * Override this method to acquire a List holding all URLs to the sources which this
373 * AbstractJaxbMojo should use to produce its output (XSDs files for AbstractXsdGeneratorMojos and
374 * Java Source Code for AbstractJavaGeneratorMojos).
375 *
376 * @return A non-null List holding URLs to sources used by this AbstractJaxbMojo to produce its output.
377 */
378 protected abstract List<URL> getSources();
379
380 /**
381 * Retrieves the directory where the generated files should be written to.
382 *
383 * @return the directory where the generated files should be written to.
384 */
385 protected abstract File getOutputDirectory();
386
387 /**
388 * Retrieves the configured List of paths from which this AbstractJaxbMojo and its internal toolset
389 * (XJC or SchemaGen) should read bytecode classes.
390 *
391 * @return the configured List of paths from which this AbstractJaxbMojo and its internal toolset (XJC or
392 * SchemaGen) should read classes.
393 * @throws org.apache.maven.plugin.MojoExecutionException if the classpath could not be retrieved.
394 */
395 protected abstract List<String> getClasspath() throws MojoExecutionException;
396
397 /**
398 * Convenience method to invoke when some plugin configuration is incorrect.
399 * Will output the problem as a warning with some degree of log formatting.
400 *
401 * @param propertyName The name of the problematic property.
402 * @param description The problem description.
403 */
404 @SuppressWarnings("all")
405 protected void warnAboutIncorrectPluginConfiguration(final String propertyName, final String description) {
406
407 final StringBuilder builder = new StringBuilder();
408 builder.append("\n+=================== [Incorrect Plugin Configuration Detected]\n");
409 builder.append("|\n");
410 builder.append("| Property : " + propertyName + "\n");
411 builder.append("| Problem : " + description + "\n");
412 builder.append("|\n");
413 builder.append("+=================== [End Incorrect Plugin Configuration Detected]\n\n");
414 getLog().warn(builder.toString().replace("\n", NEWLINE));
415 }
416
417 /**
418 * @param arguments The final arguments to be passed to a JAXB tool (XJC or SchemaGen).
419 * @param toolName The name of the tool.
420 * @return the arguments, untouched.
421 */
422 protected final String[] logAndReturnToolArguments(final String[] arguments, final String toolName) {
423
424 // Check sanity
425 Validate.notNull(arguments, "arguments");
426
427 if (getLog().isDebugEnabled()) {
428
429 final StringBuilder argBuilder = new StringBuilder();
430 argBuilder.append("\n+=================== [" + arguments.length + " " + toolName + " Arguments]\n");
431 argBuilder.append("|\n");
432 for (int i = 0; i < arguments.length; i++) {
433 argBuilder.append("| [").append(i).append("]: ").append(arguments[i]).append("\n");
434 }
435 argBuilder.append("|\n");
436 argBuilder.append("+=================== [End " + arguments.length + " " + toolName + " Arguments]\n\n");
437 getLog().debug(argBuilder.toString().replace("\n", NEWLINE));
438 }
439
440 // All done.
441 return arguments;
442 }
443
444 /**
445 * Retrieves the last name part of the stale file.
446 * The full name of the stale file will be generated by pre-pending {@code "." + getExecution().getExecutionId()}
447 * before this staleFileName.
448 *
449 * @return The name of the stale file used by this AbstractJavaGeneratorMojo to detect staleness amongst its
450 * generated files.
451 */
452 protected abstract String getStaleFileName();
453
454 /**
455 * Acquires the staleFile for this execution
456 *
457 * @return the staleFile (used to define where) for this execution
458 */
459 protected final File getStaleFile() {
460 final String staleFileName = "."
461 + (getExecution() == null ? "nonExecutionJaxb" : getExecution().getExecutionId())
462 + "-" + getStaleFileName();
463 return new File(staleFileDirectory, staleFileName);
464 }
465
466 /**
467 * <p>The algorithm for finding the encoding to use is as follows (where the first non-null value found
468 * is used for encoding):</p>
469 * <ol>
470 * <li>If the configuration property is explicitly given within the plugin's configuration, use that value.</li>
471 * <li>If the Maven property <code>project.build.sourceEncoding</code> is defined, use its value.</li>
472 * <li>Otherwise use the value from the system property <code>file.encoding</code>.</li>
473 * </ol>
474 *
475 * @param warnIfPlatformEncoding Defines if a warning should be logged if encoding is not configured but
476 * the platform encoding (system property {@code file.encoding}) is used
477 * @return The encoding to be used by this AbstractJaxbMojo and its tools.
478 * @see #encoding
479 */
480 protected final String getEncoding(final boolean warnIfPlatformEncoding) {
481
482 // Harvest information
483 final boolean configuredEncoding = encoding != null;
484 final String fileEncoding = System.getProperty(SYSTEM_FILE_ENCODING_PROPERTY);
485 final String effectiveEncoding = configuredEncoding ? encoding : fileEncoding;
486
487 // Should we warn if using platform encoding (i.e. platform dependent)?
488 if (!configuredEncoding && warnIfPlatformEncoding) {
489 getLog().warn("Using platform encoding [" + effectiveEncoding + "], i.e. build is platform dependent!");
490 } else if (getLog().isDebugEnabled()) {
491 getLog().debug("Using " + (configuredEncoding ? "explicitly configured" : "system property")
492 + " encoding [" + effectiveEncoding + "]");
493 }
494
495 // All Done.
496 return effectiveEncoding;
497 }
498
499 /**
500 * Retrieves a File to the JAXB Episode (which is normally written during the XJC process).
501 * Moreover, ensures that the parent directory of that File is created, to enable writing the File.
502 *
503 * @param episodeFileName {@code null} to indicate that the standard episode file name ("sun-jaxb.episode")
504 * should be used, and otherwise a non-empty name which should be used
505 * as the episode file name.
506 * @return A non-null File where the JAXB episode file should be written.
507 * @throws MojoExecutionException if the parent directory of the episode file could not be created.
508 */
509 protected File getEpisodeFile(final String episodeFileName) throws MojoExecutionException {
510
511 // Get the execution ID
512 final String executionID = getExecution() != null && getExecution().getExecutionId() != null
513 ? getExecution().getExecutionId()
514 : null;
515
516 final String effectiveEpisodeFileName = episodeFileName == null
517 ? (executionID == null ? STANDARD_EPISODE_FILENAME : "episode_" + executionID)
518 : episodeFileName;
519 if (effectiveEpisodeFileName.isEmpty()) {
520 throw new MojoExecutionException("Cannot handle null or empty JAXB Episode filename. "
521 + "Check 'episodeFileName' configuration property.");
522 }
523
524 // Find or create the episode directory.
525 final Path episodePath;
526 final File generatedJaxbEpisodeDirectory;
527 try {
528 final Path path = Paths.get(getOutputDirectory().getAbsolutePath(), "META-INF", "JAXB");
529 episodePath = java.nio.file.Files.createDirectories(path);
530 generatedJaxbEpisodeDirectory = episodePath.toFile();
531
532 if (getLog().isInfoEnabled()) {
533 getLog().info("Created EpisodePath [" + episodePath.toString() + "]: " +
534 (generatedJaxbEpisodeDirectory.exists() && generatedJaxbEpisodeDirectory.isDirectory()));
535 }
536
537 } catch (IOException e) {
538 throw new MojoExecutionException("Could not create output directory.", e);
539 }
540
541 if (!generatedJaxbEpisodeDirectory.exists() || !generatedJaxbEpisodeDirectory.isDirectory()) {
542 throw new MojoExecutionException("Could not create directory [" + episodePath.toString() + "]");
543 }
544
545 // Add the (generated) outputDirectory to the Resources.
546 final Resource outputDirectoryResource = new Resource();
547 outputDirectoryResource.setDirectory(getOutputDirectory().getAbsolutePath());
548 this.addResource(outputDirectoryResource);
549
550 // Is there already an episode file here?
551 File episodeFile = new File(generatedJaxbEpisodeDirectory, effectiveEpisodeFileName + ".xjb");
552 final AtomicInteger index = new AtomicInteger(1);
553 while (episodeFile.exists()) {
554 episodeFile = new File(generatedJaxbEpisodeDirectory,
555 effectiveEpisodeFileName + "_" + index.getAndIncrement() + ".xjb");
556 }
557
558 // All Done.
559 return episodeFile;
560 }
561
562 //
563 // Private helpers
564 //
565
566 private void logPluginAndJaxbDependencyInfo() {
567
568 if (getLog().isDebugEnabled()) {
569 final StringBuilder builder = new StringBuilder();
570 builder.append("\n+=================== [Brief Plugin Build Dependency Information]\n");
571 builder.append("|\n");
572 builder.append("| Note: These dependencies pertain to what was used to build *the plugin*.\n");
573 builder.append("| Check project dependencies to see the ones used in *your build*.\n");
574 builder.append("|\n");
575
576 // Find the dependency and version information within the dependencies.properties file.
577 final SortedMap<String, String> versionMap = DependsFileParser.getVersionMap(OWN_ARTIFACT_ID);
578
579 builder.append("|\n");
580 builder.append("| Plugin's own information\n");
581 builder.append("| GroupId : " + versionMap.get(DependsFileParser.OWN_GROUPID_KEY) + "\n");
582 builder.append("| ArtifactID : " + versionMap.get(DependsFileParser.OWN_ARTIFACTID_KEY) + "\n");
583 builder.append("| Version : " + versionMap.get(DependsFileParser.OWN_VERSION_KEY) + "\n");
584 builder.append("| Buildtime : " + versionMap.get(DependsFileParser.BUILDTIME_KEY) + "\n");
585 builder.append("|\n");
586 builder.append("| Plugin's JAXB-related dependencies\n");
587 builder.append("|\n");
588
589 final SortedMap<String, DependencyInfo> diMap = DependsFileParser.createDependencyInfoMap(versionMap);
590
591 int dependencyIndex = 0;
592 for (Map.Entry<String, DependencyInfo> current : diMap.entrySet()) {
593
594 final String key = current.getKey().trim();
595 for (String currentRelevantGroupId : RELEVANT_GROUPIDS) {
596 if (key.startsWith(currentRelevantGroupId)) {
597
598 final DependencyInfo di = current.getValue();
599 builder.append("| " + (++dependencyIndex) + ") [" + di.getArtifactId() + "]\n");
600 builder.append("| GroupId : " + di.getGroupId() + "\n");
601 builder.append("| ArtifactID : " + di.getArtifactId() + "\n");
602 builder.append("| Version : " + di.getVersion() + "\n");
603 builder.append("| Scope : " + di.getScope() + "\n");
604 builder.append("| Type : " + di.getType() + "\n");
605 builder.append("|\n");
606 }
607 }
608 }
609
610 builder.append("+=================== [End Brief Plugin Build Dependency Information]\n\n");
611 getLog().debug(builder.toString().replace("\n", NEWLINE));
612 }
613 }
614
615 private <T> T getInjectedObject(final T objectOrNull, final String objectName) {
616
617 if (objectOrNull == null) {
618 getLog().error(
619 "Found null '" + objectName + "', implying that Maven @Component injection was not done properly.");
620 }
621
622 return objectOrNull;
623 }
624
625 private void updateStaleFileTimestamp() throws MojoExecutionException {
626
627 final File staleFile = getStaleFile();
628 if (!staleFile.exists()) {
629
630 // Ensure that the staleFileDirectory exists
631 FileSystemUtilities.createDirectory(staleFile.getParentFile(), false);
632
633 try {
634 staleFile.createNewFile();
635
636 if (getLog().isDebugEnabled()) {
637 getLog().debug("Created staleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]");
638 }
639 } catch (IOException e) {
640 throw new MojoExecutionException("Could not create staleFile.", e);
641 }
642
643 } else {
644 if (!staleFile.setLastModified(System.currentTimeMillis())) {
645 getLog().warn("Failed updating modification time of staleFile ["
646 + FileSystemUtilities.getCanonicalPath(staleFile) + "]");
647 }
648 }
649 }
650
651 /**
652 * Prints out the system properties to the Maven Log at Debug level.
653 */
654 protected void logSystemPropertiesAndBasedir() {
655 if (getLog().isDebugEnabled()) {
656
657 final StringBuilder builder = new StringBuilder();
658
659 builder.append("\n+=================== [System properties]\n");
660 builder.append("|\n");
661
662 // Sort the system properties
663 final SortedMap<String, Object> props = new TreeMap<String, Object>();
664 props.put("basedir", FileSystemUtilities.getCanonicalPath(getProject().getBasedir()));
665
666 for (Map.Entry<Object, Object> current : System.getProperties().entrySet()) {
667 props.put("" + current.getKey(), current.getValue());
668 }
669 for (Map.Entry<String, Object> current : props.entrySet()) {
670 builder.append("| [" + current.getKey() + "]: " + current.getValue() + "\n");
671 }
672
673 builder.append("|\n");
674 builder.append("+=================== [End System properties]\n");
675
676 // All done.
677 getLog().debug(builder.toString().replace("\n", NEWLINE));
678 }
679 }
680 }