1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.devzendo.xplp;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.OutputStream;
32 import java.util.HashSet;
33 import java.util.Properties;
34 import java.util.Set;
35
36 import org.apache.maven.artifact.Artifact;
37 import org.apache.maven.plugin.AbstractMojo;
38 import org.codehaus.plexus.util.cli.CommandLineException;
39 import org.codehaus.plexus.util.cli.CommandLineUtils;
40 import org.codehaus.plexus.util.cli.Commandline;
41 import org.codehaus.plexus.util.cli.CommandLineUtils.StringStreamConsumer;
42
43
44
45
46
47
48
49
50
51 public abstract class LauncherCreator {
52 private final AbstractMojo mMojo;
53 private final File mOutputDirectory;
54 private final String mMainClassName;
55 private final String mApplicationName;
56 private final String mLibraryDirectory;
57 private final Set<Artifact> mTransitiveArtifacts;
58 private final Set<File> mResourceDirectories;
59 private final Properties mParameterProperties;
60 private final String[] mSystemProperties;
61 private final String[] mVmArguments;
62 private final String[] mNarClassifierTypes;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 public LauncherCreator(final AbstractMojo mojo,
78 final File outputDirectory,
79 final String mainClassName,
80 final String applicationName,
81 final String libraryDirectory,
82 final Set<Artifact> transitiveArtifacts,
83 final Set<File> resourceDirectories,
84 final Properties parameterProperties,
85 final String[] systemProperties,
86 final String[] vmArguments,
87 final String[] narClassifierTypes) {
88 mMojo = mojo;
89 mOutputDirectory = outputDirectory;
90 mMainClassName = mainClassName;
91 mApplicationName = applicationName;
92 mLibraryDirectory = libraryDirectory;
93 mTransitiveArtifacts = transitiveArtifacts;
94 mResourceDirectories = resourceDirectories;
95 mParameterProperties = parameterProperties;
96 mSystemProperties = systemProperties;
97 mVmArguments = vmArguments;
98 mNarClassifierTypes = narClassifierTypes;
99 }
100
101
102
103
104 protected final AbstractMojo getMojo() {
105 return mMojo;
106 }
107
108
109
110
111 protected final Set<Artifact> getTransitiveArtifacts() {
112 return mTransitiveArtifacts;
113 }
114
115
116
117
118 protected final File getOutputDirectory() {
119 return mOutputDirectory;
120 }
121
122
123
124
125 protected final String getMainClassName() {
126 return mMainClassName;
127 }
128
129
130
131
132 protected final String getApplicationName() {
133 return mApplicationName;
134 }
135
136
137
138
139 protected final String getLibraryDirectory() {
140 return mLibraryDirectory;
141 }
142
143
144
145
146 protected final Set<File> getResourceDirectories() {
147 return mResourceDirectories;
148 }
149
150
151
152
153 protected final Properties getParameterProperties() {
154 return mParameterProperties;
155 }
156
157
158
159
160 protected final String[] getSystemProperties() {
161 return mSystemProperties;
162 }
163
164
165
166
167 protected final String[] getVmArguments() {
168 return mVmArguments;
169 }
170
171
172
173
174
175 public abstract void createLauncher() throws IOException;
176
177
178
179
180
181
182
183 protected final void copyPluginResource(final String resourceName, final File destinationFile) throws IOException {
184 final InputStream resourceAsStream = getPluginResourceAsStream(resourceName);
185 if (resourceAsStream == null) {
186 final String message = "Could not open resource " + resourceName;
187 mMojo.getLog().warn(message);
188 throw new IOException(message);
189 }
190
191 final OutputStream outputStream = createFileOutputStream(destinationFile);
192 final long bytesCopied = copyStream(resourceName, destinationFile.getAbsolutePath(),
193 resourceAsStream, outputStream);
194 mMojo.getLog().info("Created " + destinationFile.getAbsolutePath() + " [" + bytesCopied + " byte(s)]");
195 }
196
197
198
199
200
201
202
203 protected final void copyProjectResource(final String resourceName, final File destinationFile) throws IOException {
204 final InputStream resourceAsStream = getProjectResourceAsStream(resourceName);
205 final OutputStream outputStream = createFileOutputStream(destinationFile);
206 final long bytesCopied = copyStream(resourceName, destinationFile.getAbsolutePath(),
207 resourceAsStream, outputStream);
208 mMojo.getLog().info("Created " + destinationFile.getAbsolutePath() + " [" + bytesCopied + " byte(s)]");
209 }
210
211 private OutputStream createFileOutputStream(final File destinationFile) throws IOException {
212 try {
213 return new FileOutputStream(destinationFile);
214 } catch (final FileNotFoundException e) {
215 final String message = "Could not create destination file " + destinationFile.getAbsolutePath() + ": " + e.getMessage();
216 mMojo.getLog().warn(message);
217 throw new IOException(message);
218 }
219 }
220
221 private InputStream getProjectResourceAsStream(final String resourceName) throws IOException {
222 for (final File resourceDir : mResourceDirectories) {
223 try {
224 final InputStream resourceAsStream = new FileInputStream(new File(resourceDir, resourceName));
225 mMojo.getLog().debug("Located resource " + resourceName + " in directory " + resourceDir.getAbsolutePath());
226 return resourceAsStream;
227 } catch (final FileNotFoundException e) {
228 mMojo.getLog().debug("Resource " + resourceName + " not found in " + resourceDir.getAbsolutePath());
229 }
230 }
231 final String message = "Could not open resource " + resourceName;
232 mMojo.getLog().warn(message);
233 throw new IOException(message);
234 }
235
236 private InputStream getPluginResourceAsStream(final String resourceName) {
237 final InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName);
238 return resourceAsStream;
239 }
240
241
242
243
244
245
246
247
248 protected Set<String> getTransitiveJarOrNarArtifactFileNames(final Set<Artifact> transitiveArtifacts) {
249 final Set<String> jarNarArtifacts = new HashSet<String>();
250 for (final Artifact transitiveArtifact : transitiveArtifacts) {
251 if (transitiveArtifact.getScope().equals("compile")
252 && (transitiveArtifact.getType().equals("jar")
253 || transitiveArtifact.getType().equals("nar"))) {
254 final String artifactFileName = transitiveArtifact.getFile().getName().replaceFirst("\\.nar$", ".jar");
255 jarNarArtifacts.add(artifactFileName);
256 }
257 }
258 return jarNarArtifacts;
259 }
260
261
262
263
264
265
266
267
268 protected final void copyFile(final File sourceFile, final File destinationDirectory) throws IOException {
269 final String destinationFileName = sourceFile.getName();
270 copyFileWithRename(sourceFile, destinationDirectory,
271 destinationFileName);
272 }
273
274 private void copyFileWithRename(
275 final File sourceFile,
276 final File destinationDirectory,
277 final String destinationFileName) throws IOException {
278 InputStream inputStream;
279 try {
280 inputStream = new FileInputStream(sourceFile);
281 } catch (final IOException ioe) {
282 final String warning = "Could not open source file '" + sourceFile + "': " + ioe.getMessage();
283 mMojo.getLog().warn(warning);
284 throw new IOException(warning);
285 }
286 final File destinationFile = new File(destinationDirectory, destinationFileName);
287 OutputStream outputStream;
288 try {
289 outputStream = new FileOutputStream(destinationFile);
290 } catch (final FileNotFoundException e) {
291 final String message = "Could not create destination file " + destinationFile.getAbsolutePath() + ": " + e.getMessage();
292 mMojo.getLog().warn(message);
293 throw new IOException(message);
294 }
295 final long bytesCopied = copyStream(sourceFile.getAbsolutePath(), destinationFile.getAbsolutePath(),
296 inputStream, outputStream);
297 mMojo.getLog().info("Created "
298 + destinationDirectory.getAbsoluteFile()
299 + File.separatorChar + destinationFileName
300 + " from " + sourceFile.getAbsolutePath()
301 + " [" + bytesCopied + " byte(s) copied]");
302 }
303
304 private long copyStream(final String inName, final String outName,
305 final InputStream inputStream, final OutputStream outputStream)
306 throws IOException {
307 final int bufsize = 16384;
308 final byte[] buf = new byte[bufsize];
309 long totalRead = 0;
310 int nRead;
311 try {
312 while ((nRead = inputStream.read(buf, 0, bufsize)) != -1) {
313 outputStream.write(buf, 0, nRead);
314 totalRead += nRead;
315 }
316 return totalRead;
317 } catch (final IOException e) {
318 final String message = "Could not copy " + inName + " to "
319 + outName + ": " + e.getMessage();
320 mMojo.getLog().warn(message);
321 throw new IOException(message);
322 } finally {
323 try {
324 inputStream.close();
325 } catch (final IOException ioe) {
326 }
327 try {
328 outputStream.close();
329 } catch (final IOException ioe) {
330 }
331 }
332 }
333
334
335
336
337
338
339
340
341 protected final void copyTransitiveArtifacts(final File destinationDirectory) throws IOException {
342 getMojo().getLog().info("Copying transitive artifacts");
343 final Set<Artifact> transitiveArtifacts = getTransitiveArtifacts();
344 getMojo().getLog().info("There are " + transitiveArtifacts.size() + " transitive artifacts");
345 for (final Artifact artifact : transitiveArtifacts) {
346 if (artifact.getScope().equals("compile")) {
347 copyTransitiveArtifact(destinationDirectory, artifact);
348 } else {
349 getMojo().getLog().info("Not copying transitive artifact " + artifact + " since it is not a compile-scoped artifact");
350 }
351 }
352 }
353
354 private void copyTransitiveArtifact(
355 final File destinationDirectory,
356 final Artifact artifact) throws IOException {
357 if (!(artifact.getType().equals("jar") || artifact.getType().equals("nar"))) {
358 getMojo().getLog().info("Not copying transitive artifact " + artifact + " since it is not a jar or nar artifact");
359 return;
360 }
361
362 getMojo().getLog().info("Copying transitive artifact " + artifact);
363 final File artifactFile = artifact.getFile();
364 if (artifactFile.exists() && artifactFile.isFile()) {
365 if (artifact.getType().equals("nar")) {
366 copyTransitiveNarArtifact(artifact, destinationDirectory);
367 } else {
368 copyFile(artifactFile, destinationDirectory);
369 }
370 } else {
371 getMojo().getLog().warn("Not copying transitive artifact " + artifact + " since it either doesn't exist or is not a file");
372 getMojo().getLog().info("(Perhaps you're running this from an IDE, and this artifact is resolved as a project in your");
373 getMojo().getLog().info(" workspace and therefore on your classpath by the IDE?)");
374 }
375 }
376
377 private void copyTransitiveNarArtifact(
378 final Artifact artifact, final File destinationDirectory) throws IOException {
379 final File artifactFile = artifact.getFile();
380 final String destinationJarFileName = artifactFile.getName().replace(".nar", ".jar");
381 copyFileWithRename(artifactFile, destinationDirectory, destinationJarFileName);
382
383 for (final String narClassifierType : mNarClassifierTypes) {
384 final String[] split = narClassifierType.split(":");
385 copyNarClassifierType(artifact, destinationDirectory, split[0], split[1]);
386 }
387 }
388
389 private void copyNarClassifierType(
390 final Artifact artifact,
391 final File destinationDirectory,
392 final String classifier,
393 final String type) throws IOException {
394 getMojo().getLog().info("Copying unpacked NAR files for artifact: " + artifact);
395 final File unpackedNarDirectory = new File(mOutputDirectory, "nar/lib/" + classifier + "/" + type);
396 if (!unpackedNarDirectory.exists() || !unpackedNarDirectory.isDirectory()) {
397 getMojo().getLog().warn(
398 "NAR unpacked library directory " + unpackedNarDirectory.getAbsolutePath()
399 + " for classifier:type " + classifier + ":" + type + " does not exist or is not a directory");
400 getMojo().getLog().warn("Did you forget to use the nar-assembly goal?");
401 return;
402 }
403 final File[] unpackedNarFiles = unpackedNarDirectory.listFiles();
404 for (final File file : unpackedNarFiles) {
405 final String name = file.getName();
406 getMojo().getLog().info("Considering NAR unpacked file: " + file.getAbsolutePath());
407
408
409 if (name.equals("history.xml")) {
410 getMojo().getLog().info("Not copying " + name + " as it is NAR metadata");
411 continue;
412 }
413 final boolean isAnArtifactFile = name.contains(artifact.getArtifactId()) && name.contains(artifact.getVersion());
414 if (!isAnArtifactFile) {
415 getMojo().getLog().warn("Not copying " + name + " as it does not refer to the artifact " + artifact);
416 continue;
417 }
418 copyFile(file, destinationDirectory);
419 }
420 }
421
422
423
424
425
426
427
428 protected final void copyInterpolatedProjectResource(final String resourceName, final File outputFile) throws IOException {
429 final BufferedReader br = new BufferedReader(new InputStreamReader(getProjectResourceAsStream(resourceName)));
430 copyInterpolatedResource(resourceName, outputFile, br);
431 }
432
433
434
435
436
437
438
439 protected final void copyInterpolatedPluginResource(final String resourceName, final File outputFile) throws IOException {
440 final BufferedReader br = new BufferedReader(new InputStreamReader(getPluginResourceAsStream(resourceName)));
441 copyInterpolatedResource(resourceName, outputFile, br);
442 }
443
444 private void copyInterpolatedResource(
445 final String resourceName,
446 final File outputFile,
447 final BufferedReader br) throws IOException {
448 long bytesCopied = 0;
449 final PropertiesInterpolator interpolator = new PropertiesInterpolator(getParameterProperties());
450 try {
451 final String lineSeparator = System.getProperty("line.separator");
452 final FileWriter fw = createFileWriter(outputFile);
453 try {
454 while (true) {
455 final String line = br.readLine();
456 if (line == null) {
457 break;
458 }
459 try {
460 final String outLine = interpolator.interpolate(line);
461 fw.write(outLine);
462 bytesCopied += outLine.length();
463 if (!outLine.endsWith(lineSeparator)) {
464 fw.write(lineSeparator);
465 bytesCopied += lineSeparator.length();
466 }
467 } catch (final IllegalStateException e) {
468 final String message = "Cannot interpolate whilst processing " + resourceName + ": " + e.getMessage();
469 getMojo().getLog().warn(message);
470 throw new IOException(message);
471 }
472 }
473 } finally {
474 try {
475 fw.close();
476 } catch (final IOException e) {
477
478 }
479 }
480 } finally {
481 try {
482 br.close();
483 } catch (final IOException e) {
484
485 }
486 }
487 getMojo().getLog().info("Created "
488 + outputFile.getAbsolutePath()
489 + " [" + bytesCopied + " byte(s) copied]");
490
491 }
492
493 private FileWriter createFileWriter(final File outputFile) throws IOException {
494 try {
495 return new FileWriter(outputFile);
496 } catch (final IOException e) {
497 final String message = "Could not create destination file "
498 + outputFile.getAbsolutePath() + ": " + e.getMessage();
499 mMojo.getLog().warn(message);
500 throw new IOException(message);
501 }
502 }
503
504
505
506
507
508 protected final void makeExecutable(final File nonExecutableFile) {
509 getMojo().getLog().info("Making " + nonExecutableFile + " executable");
510 final Commandline cl = new Commandline("chmod");
511 cl.addArguments(new String [] {"a+x" , nonExecutableFile.getAbsolutePath()});
512 try {
513 final StringStreamConsumer output = new StringStreamConsumer();
514 final StringStreamConsumer error = new StringStreamConsumer();
515 final int returnValue = CommandLineUtils.executeCommandLine(cl, output, error);
516 if (returnValue != 0) {
517 getMojo().getLog().warn("chmod exit code is " + returnValue);
518 getMojo().getLog().warn("chmod output: " + output.getOutput());
519 getMojo().getLog().warn("chmod error output: " + error.getOutput());
520 }
521 } catch (final CommandLineException e) {
522 getMojo().getLog().warn("Couldn't run chmod: " + e.getMessage());
523 }
524 }
525 }