1 package org.codehaus.gmavenplus.mojo;
2
3 import org.apache.maven.plugins.annotations.Parameter;
4 import org.apache.maven.shared.model.fileset.FileSet;
5 import org.apache.maven.shared.model.fileset.util.FileSetManager;
6 import org.codehaus.gmavenplus.javaparser.LanguageLevel;
7 import org.codehaus.gmavenplus.model.IncludeClasspath;
8 import org.codehaus.gmavenplus.model.Link;
9 import org.codehaus.gmavenplus.model.Scopes;
10 import org.codehaus.gmavenplus.model.internal.Version;
11 import org.codehaus.gmavenplus.util.FileUtils;
12
13 import org.codehaus.gmavenplus.model.GroovyDocConfiguration;
14 import org.codehaus.gmavenplus.util.GroovyCompiler;
15
16 import java.io.BufferedReader;
17 import java.io.BufferedWriter;
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.OutputStreamWriter;
22 import java.lang.reflect.InvocationTargetException;
23 import java.net.MalformedURLException;
24 import java.nio.file.Files;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Properties;
28
29
30
31
32
33
34
35 public abstract class AbstractGroovyDocMojo extends AbstractGroovySourcesMojo {
36
37
38
39
40 protected static final Version GROOVY_1_6_0_RC1 = new Version(1, 6, 0, "RC-1");
41
42
43
44
45 protected static final Version GROOVY_1_5_8 = new Version(1, 5, 8);
46
47
48
49
50 @Parameter(defaultValue = "Groovy Documentation")
51 protected String windowTitle;
52
53
54
55
56 @Parameter(defaultValue = "Groovy Documentation")
57 protected String docTitle;
58
59
60
61
62 @Parameter(defaultValue = "Groovy Documentation")
63 protected String footer;
64
65
66
67
68 @Parameter
69 protected LanguageLevel languageLevel;
70
71
72
73
74 @Parameter(defaultValue = "Groovy Documentation")
75 protected String header;
76
77
78
79
80 @Parameter(defaultValue = "true")
81 protected boolean displayAuthor;
82
83
84
85
86 @Parameter
87 protected File overviewFile;
88
89
90
91
92 @Parameter
93 protected File stylesheetFile;
94
95
96
97
98 @Parameter(defaultValue = "${project.build.sourceEncoding}")
99 protected String stylesheetEncoding;
100
101
102
103
104
105
106
107
108
109
110 @Parameter(defaultValue = "private")
111 protected String scope;
112
113
114
115
116
117
118 @Parameter
119 protected List<Link> links;
120
121
122
123
124
125
126 @Parameter(property = "skipGroovydoc", defaultValue = "false")
127 protected boolean skipGroovyDoc;
128
129
130
131
132
133
134
135
136
137
138
139
140 @Parameter(defaultValue = "PROJECT_ONLY")
141 protected IncludeClasspath includeClasspath;
142
143
144
145
146
147
148 @Parameter
149 protected String[] defaultDocTemplates = null;
150
151
152
153
154
155
156 @Parameter
157 protected String[] defaultPackageTemplates = null;
158
159
160
161
162
163
164 @Parameter
165 protected String[] defaultClassTemplates = null;
166
167
168
169
170
171
172
173 @Parameter
174 protected String groovyDocToolClass = null;
175
176
177
178
179
180
181
182 @Parameter
183 protected String outputToolClass = null;
184
185
186
187
188
189
190
191 @Parameter
192 protected String fileOutputToolClass = null;
193
194
195
196
197
198
199
200 @Parameter
201 protected String resourceManagerClass = null;
202
203
204
205
206
207
208
209 @Parameter
210 protected String classpathResourceManagerClass = null;
211
212
213
214
215
216
217
218
219 @Parameter
220 protected String linkArgumentClass = null;
221
222
223
224
225
226
227 @Parameter(defaultValue = "false")
228 protected boolean attachGroovyDocAnnotation;
229
230
231
232
233 @javax.inject.Inject
234 protected org.apache.maven.toolchain.ToolchainManager toolchainManager;
235
236
237
238
239 @Parameter(defaultValue = "${session}", readonly = true, required = true)
240 protected org.apache.maven.execution.MavenSession session;
241
242
243
244
245
246
247 @Parameter(property = "fork", defaultValue = "false")
248 protected boolean fork;
249
250
251
252
253
254
255
256
257
258
259
260
261
262 protected synchronized void doGroovyDocGeneration(final FileSet[] sourceDirectories, final List<?> classpath, final File outputDirectory) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, MalformedURLException {
263 if (skipGroovyDoc) {
264 getLog().info("Skipping generation of GroovyDoc because ${skipGroovydoc} was set to true.");
265 return;
266 }
267
268 if (sourceDirectories == null || sourceDirectories.length == 0) {
269 getLog().info("No source directories specified for GroovyDoc generation. Skipping.");
270 return;
271 }
272
273 GroovyDocConfiguration configuration = new GroovyDocConfiguration(sourceDirectories, classpath, outputDirectory);
274 configuration.setIncludeClasspath(includeClasspath);
275 configuration.setDocProperties(setupProperties());
276 configuration.setLinks(links);
277
278 configuration.setAttachGroovyDocAnnotation(attachGroovyDocAnnotation);
279 configuration.setDefaultDocTemplates(defaultDocTemplates);
280 configuration.setDefaultPackageTemplates(defaultPackageTemplates);
281 configuration.setDefaultClassTemplates(defaultClassTemplates);
282 configuration.setGroovyDocToolClass(groovyDocToolClass);
283 configuration.setOutputToolClass(outputToolClass);
284 configuration.setFileOutputToolClass(fileOutputToolClass);
285 configuration.setResourceManagerClass(resourceManagerClass);
286 configuration.setClasspathResourceManagerClass(classpathResourceManagerClass);
287 configuration.setLinkArgumentClass(linkArgumentClass);
288 configuration.setWindowTitle(windowTitle);
289 configuration.setDocTitle(docTitle);
290 configuration.setFooter(footer);
291 configuration.setHeader(header);
292 configuration.setDisplayAuthor(displayAuthor);
293 configuration.setDisplayAuthor(displayAuthor);
294 configuration.setOverviewFile(overviewFile);
295 configuration.setLanguageLevel(languageLevel != null ? languageLevel.toString() : null);
296
297 configuration.setScope(scope);
298
299 org.apache.maven.toolchain.Toolchain toolchain = toolchainManager.getToolchainFromBuildContext("jdk", session);
300 if (toolchain != null) {
301 getLog().info("Toolchain in gmavenplus-plugin: " + toolchain);
302 performForkedGroovyDocGeneration(configuration, toolchain.findTool("java"));
303 } else if (fork) {
304 String javaExecutable = getJavaExecutable();
305 getLog().info("Forking GroovyDoc generation using " + javaExecutable);
306 performForkedGroovyDocGeneration(configuration, javaExecutable);
307 } else {
308 getLog().info("Performing in-process GroovyDoc generation");
309 performInProcessGroovyDocGeneration(configuration);
310 }
311
312
313 if (stylesheetFile != null) {
314 copyStylesheet(outputDirectory);
315 }
316 }
317
318 protected void performInProcessGroovyDocGeneration(GroovyDocConfiguration configuration) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, MalformedURLException {
319 setupClassWrangler(configuration.getClasspath(), includeClasspath);
320 classWrangler.logGroovyVersion(mojoExecution.getMojoDescriptor().getGoal());
321 logPluginClasspath();
322
323 if (!groovyVersionSupportsAction()) {
324 getLog().error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support GroovyDoc. The minimum version of Groovy required is " + minGroovyVersion + ". Skipping GroovyDoc generation.");
325 return;
326 }
327 if (groovyIs(GROOVY_1_6_0_RC1) || groovyIs(GROOVY_1_5_8)) {
328
329 getLog().warn("Groovy " + GROOVY_1_5_8 + " and " + GROOVY_1_6_0_RC1 + " are blacklisted from the supported GroovyDoc versions because of their dependency on Ant. Skipping GroovyDoc generation.");
330 return;
331 }
332
333 GroovyCompiler compiler = new GroovyCompiler(classWrangler, getLog());
334 compiler.generateGroovyDoc(configuration);
335 }
336
337 protected void performForkedGroovyDocGeneration(GroovyDocConfiguration configuration, String javaExecutable) throws InvocationTargetException {
338 try {
339
340 File configFile = File.createTempFile("groovy-doc-config", ".ser");
341 configFile.deleteOnExit();
342 try (java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(java.nio.file.Files.newOutputStream(configFile.toPath()))) {
343 oos.writeObject(configuration);
344 }
345
346
347 String forkClasspath = buildForkClasspath();
348
349 List<String> command = new ArrayList<>();
350 command.add(javaExecutable);
351 command.add("-cp");
352 command.add(forkClasspath);
353 command.add("org.codehaus.gmavenplus.util.ForkedGroovyCompiler");
354 command.add(configFile.getAbsolutePath());
355
356 ProcessBuilder pb = new ProcessBuilder(command);
357 pb.inheritIO();
358 Process process = pb.start();
359 int exitCode = process.waitFor();
360
361 if (exitCode != 0) {
362 throw new InvocationTargetException(new RuntimeException("Forked GroovyDoc generation failed with exit code " + exitCode));
363 }
364 } catch (IOException | InterruptedException e) {
365 throw new InvocationTargetException(e);
366 }
367 }
368
369 protected String buildForkClasspath() {
370 StringBuilder cp = new StringBuilder();
371
372 cp.append(pluginDescriptor.getPluginArtifact().getFile().getAbsolutePath());
373
374
375 for (org.apache.maven.artifact.Artifact artifact : pluginDescriptor.getArtifacts()) {
376 cp.append(File.pathSeparator);
377 cp.append(artifact.getFile().getAbsolutePath());
378 }
379
380
381 try {
382 Class<?> logClass = org.apache.maven.plugin.logging.Log.class;
383 java.security.CodeSource codeSource = logClass.getProtectionDomain().getCodeSource();
384 if (codeSource != null) {
385 String logJar = new File(codeSource.getLocation().toURI()).getAbsolutePath();
386 cp.append(File.pathSeparator).append(logJar);
387 }
388 } catch (Exception e) {
389 getLog().warn("Could not find maven-plugin-api jar to add to fork classpath", e);
390 }
391
392 return cp.toString();
393 }
394
395
396
397
398
399
400 protected Properties setupProperties() {
401 Properties properties = new Properties();
402 properties.setProperty("windowTitle", windowTitle);
403 properties.setProperty("docTitle", docTitle);
404 properties.setProperty("footer", footer);
405 properties.setProperty("header", header);
406 properties.setProperty("author", Boolean.toString(displayAuthor));
407 properties.setProperty("overviewFile", overviewFile != null ? overviewFile.getAbsolutePath() : "");
408 try {
409 Scopes scopeVal = Scopes.valueOf(scope.toUpperCase());
410 if (scopeVal.equals(Scopes.PUBLIC)) {
411 properties.setProperty("publicScope", "true");
412 } else if (scopeVal.equals(Scopes.PROTECTED)) {
413 properties.setProperty("protectedScope", "true");
414 } else if (scopeVal.equals(Scopes.PACKAGE)) {
415 properties.setProperty("packageScope", "true");
416 } else if (scopeVal.equals(Scopes.PRIVATE)) {
417 properties.setProperty("privateScope", "true");
418 }
419 } catch (IllegalArgumentException e) {
420 getLog().warn("Scope (" + scope + ") was not recognized. Skipping argument.");
421 }
422
423 return properties;
424 }
425
426
427
428
429
430
431
432 protected void copyStylesheet(final File outputDirectory) {
433 getLog().info("Using stylesheet from " + stylesheetFile.getAbsolutePath() + ".");
434 try {
435 BufferedReader bufferedReader = null;
436 BufferedWriter bufferedWriter = null;
437 try {
438 if (stylesheetEncoding != null) {
439 bufferedReader = new BufferedReader(new InputStreamReader(Files.newInputStream(stylesheetFile.toPath()), stylesheetEncoding));
440 } else {
441 bufferedReader = new BufferedReader(new InputStreamReader(Files.newInputStream(stylesheetFile.toPath())));
442 }
443 StringBuilder css = new StringBuilder();
444 String line;
445 while ((line = bufferedReader.readLine()) != null) {
446 css.append(line).append("\n");
447 }
448 File outfile = new File(outputDirectory, "stylesheet.css");
449 if (stylesheetEncoding != null) {
450 bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(outfile.toPath()), stylesheetEncoding));
451 } else {
452 bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(outfile.toPath())));
453 }
454 bufferedWriter.write(css.toString());
455 } finally {
456 FileUtils.closeQuietly(bufferedReader);
457 FileUtils.closeQuietly(bufferedWriter);
458 }
459 } catch (IOException e) {
460 getLog().warn("Unable to copy specified stylesheet (" + stylesheetFile.getAbsolutePath() + ").");
461 }
462 }
463
464 }