1
2
3
4
5 package com.qulice.maven;
6
7 import com.google.common.base.Function;
8 import com.google.common.base.Predicate;
9 import com.google.common.base.Predicates;
10 import com.google.common.collect.Collections2;
11 import com.google.common.collect.Iterables;
12 import com.jcabi.log.Logger;
13 import com.qulice.spi.Binary;
14 import java.io.File;
15 import java.net.MalformedURLException;
16 import java.net.URI;
17 import java.net.URL;
18 import java.net.URLClassLoader;
19 import java.nio.charset.Charset;
20 import java.security.PrivilegedAction;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Properties;
25 import javax.annotation.Nullable;
26 import org.apache.commons.io.FileUtils;
27 import org.apache.commons.io.FilenameUtils;
28 import org.apache.commons.io.filefilter.DirectoryFileFilter;
29 import org.apache.commons.io.filefilter.IOFileFilter;
30 import org.apache.commons.io.filefilter.WildcardFileFilter;
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.artifact.DependencyResolutionRequiredException;
33 import org.apache.maven.model.Build;
34 import org.apache.maven.model.Resource;
35 import org.apache.maven.project.MavenProject;
36 import org.codehaus.plexus.context.Context;
37
38
39
40
41
42 @SuppressWarnings("PMD.GodClass")
43 public final class DefaultMavenEnvironment implements MavenEnvironment {
44
45
46
47
48 private MavenProject iproject;
49
50
51
52
53 private Context icontext;
54
55
56
57
58 private final Properties iproperties;
59
60
61
62
63 private MojoExecutor exectr;
64
65
66
67
68 private final Collection<String> exc;
69
70
71
72
73 private final Collection<String> assertion;
74
75
76
77
78 private String charset;
79
80
81
82
83 public DefaultMavenEnvironment() {
84 this.iproperties = new Properties();
85 this.exc = new ArrayList<>(0);
86 this.assertion = new ArrayList<>(0);
87 this.charset = "UTF-8";
88 }
89
90 @Override
91 public String param(final String name, final String value) {
92 String ret = this.iproperties.getProperty(name);
93 if (ret == null) {
94 ret = value;
95 }
96 return ret;
97 }
98
99 @Override
100 public File basedir() {
101 return this.iproject.getBasedir();
102 }
103
104 @Override
105 public File tempdir() {
106 return new File(this.iproject.getBuild().getOutputDirectory());
107 }
108
109 @Override
110 public File outdir() {
111 return new File(this.iproject.getBuild().getOutputDirectory());
112 }
113
114 @Override
115 @SuppressWarnings("deprecation")
116 public Collection<String> classpath() {
117 final Collection<String> paths = new ArrayList<>(0);
118 final String blank = "%20";
119 final String whitespace = " ";
120 try {
121 for (final String name
122 : this.iproject.getRuntimeClasspathElements()) {
123 paths.add(
124 name.replace(
125 File.separatorChar, '/'
126 ).replaceAll(whitespace, blank)
127 );
128 }
129 for (final Artifact artifact
130 : this.iproject.getDependencyArtifacts()) {
131 if (artifact.getFile() != null) {
132 paths.add(
133 artifact.getFile().getAbsolutePath()
134 .replace(File.separatorChar, '/')
135 .replaceAll(whitespace, blank)
136 );
137 }
138 }
139 } catch (final DependencyResolutionRequiredException ex) {
140 throw new IllegalStateException("Failed to read classpath", ex);
141 }
142 return paths;
143 }
144
145 @Override
146 public ClassLoader classloader() {
147 final List<URL> urls = new ArrayList<>(0);
148 for (final String path : this.classpath()) {
149 try {
150 urls.add(
151 URI.create(String.format("file:///%s", path)).toURL()
152 );
153 } catch (final MalformedURLException ex) {
154 throw new IllegalStateException("Failed to build URL", ex);
155 }
156 }
157 final URLClassLoader loader =
158 new DefaultMavenEnvironment.PrivilegedClassLoader(urls).run();
159 for (final URL url : loader.getURLs()) {
160 Logger.debug(this, "Classpath: %s", url);
161 }
162 return loader;
163 }
164
165 @Override
166 public MavenProject project() {
167 return this.iproject;
168 }
169
170 @Override
171 public Properties properties() {
172 return this.iproperties;
173 }
174
175 @Override
176 public Context context() {
177 return this.icontext;
178 }
179
180 @Override
181 public Properties config() {
182 return this.iproperties;
183 }
184
185 @Override
186 public MojoExecutor executor() {
187 return this.exectr;
188 }
189
190 @Override
191 public Collection<String> asserts() {
192 return this.assertion;
193 }
194
195 @Override
196 public Collection<File> files(final String pattern) {
197 final Collection<File> files = new ArrayList<>(0);
198 final IOFileFilter filter = WildcardFileFilter.builder().setWildcards(pattern).get();
199 for (final File sources : this.sources()) {
200 if (sources.exists()) {
201 for (final File found : FileUtils.listFiles(
202 sources,
203 filter,
204 DirectoryFileFilter.INSTANCE
205 )) {
206 if (new Binary(found).yes()) {
207 Logger.debug(
208 this,
209 "Skipping binary file %s",
210 found
211 );
212 } else {
213 files.add(found);
214 }
215 }
216 }
217 }
218 return files;
219 }
220
221 @Override
222 public boolean exclude(final String check, final String name) {
223 return Iterables.any(
224 this.excludes(check),
225 new DefaultMavenEnvironment.PathPredicate(name)
226 );
227 }
228
229 @Override
230 public Collection<String> excludes(final String checker) {
231 return Collections2.filter(
232 Collections2.transform(
233 this.exc,
234 new DefaultMavenEnvironment.CheckerExcludes(checker)
235 ),
236 Predicates.notNull()
237 );
238 }
239
240
241
242
243
244 public void setProject(final MavenProject proj) {
245 this.iproject = proj;
246 }
247
248
249
250
251
252 public void setContext(final Context ctx) {
253 this.icontext = ctx;
254 }
255
256
257
258
259
260 public void setMojoExecutor(final MojoExecutor exec) {
261 this.exectr = exec;
262 }
263
264
265
266
267
268
269 public void setProperty(final String name, final String value) {
270 this.iproperties.setProperty(name, value);
271 }
272
273
274
275
276
277 public void setExcludes(final Collection<String> exprs) {
278 this.exc.clear();
279 this.exc.addAll(exprs);
280 }
281
282
283
284
285
286 public void setAssertion(final Collection<String> ass) {
287 this.assertion.clear();
288 this.assertion.addAll(ass);
289 }
290
291
292
293
294
295 public void setEncoding(final String encoding) {
296 this.charset = encoding;
297 }
298
299 @Override
300 public Charset encoding() {
301 if (this.charset == null || this.charset.isEmpty()) {
302 this.charset = "UTF-8";
303 }
304 return Charset.forName(this.charset);
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322 private Collection<File> sources() {
323 final Collection<File> dirs = new ArrayList<>(0);
324 final Build build = this.iproject.getBuild();
325 final File output = this.buildDirectory(build);
326 this.addRoots(dirs, this.iproject.getCompileSourceRoots(), output);
327 this.addRoots(dirs, this.iproject.getTestCompileSourceRoots(), output);
328 if (build != null) {
329 this.addResources(dirs, build.getResources(), output);
330 this.addResources(dirs, build.getTestResources(), output);
331 }
332 if (dirs.isEmpty()) {
333 dirs.add(new File(this.basedir(), "src"));
334 }
335 return dirs;
336 }
337
338
339
340
341
342
343 @Nullable
344 private File buildDirectory(@Nullable final Build build) {
345 File dir = null;
346 if (build != null && build.getDirectory() != null) {
347 dir = this.resolve(build.getDirectory());
348 }
349 return dir;
350 }
351
352
353
354
355
356
357
358 private void addRoots(final Collection<File> dirs,
359 final List<String> roots, @Nullable final File output) {
360 if (roots != null) {
361 for (final String root : roots) {
362 final File resolved = this.resolve(root);
363 if (DefaultMavenEnvironment.outside(resolved, output)) {
364 dirs.add(resolved);
365 } else {
366 Logger.debug(
367 this,
368 "Skipping generated source root %s under %s",
369 resolved, output
370 );
371 }
372 }
373 }
374 }
375
376
377
378
379
380
381
382 private void addResources(final Collection<File> dirs,
383 final List<Resource> resources, @Nullable final File output) {
384 if (resources != null) {
385 for (final Resource res : resources) {
386 final File resolved = this.resolve(res.getDirectory());
387 if (DefaultMavenEnvironment.outside(resolved, output)) {
388 dirs.add(resolved);
389 } else {
390 Logger.debug(
391 this,
392 "Skipping generated resource directory %s under %s",
393 resolved, output
394 );
395 }
396 }
397 }
398 }
399
400
401
402
403
404
405
406 private static boolean outside(final File file,
407 @Nullable final File parent) {
408 boolean answer = true;
409 if (parent != null) {
410 final String head = FilenameUtils.normalize(
411 parent.getAbsolutePath(), true
412 );
413 final String tail = FilenameUtils.normalize(
414 file.getAbsolutePath(), true
415 );
416 if (head != null && tail != null
417 && (tail.equals(head) || tail.startsWith(head.concat("/")))) {
418 answer = false;
419 }
420 }
421 return answer;
422 }
423
424
425
426
427
428
429 private File resolve(final String path) {
430 final File file = new File(path);
431 final File resolved;
432 if (file.isAbsolute()) {
433 resolved = file;
434 } else {
435 resolved = new File(this.basedir(), path);
436 }
437 return resolved;
438 }
439
440
441
442
443
444 private static final class PrivilegedClassLoader implements
445 PrivilegedAction<URLClassLoader> {
446
447
448
449
450 private final List<URL> urls;
451
452
453
454
455
456 private PrivilegedClassLoader(final List<URL> urls) {
457 this.urls = urls;
458 }
459
460 @Override
461 public URLClassLoader run() {
462 return new URLClassLoader(
463 this.urls.toArray(new URL[0]),
464 Thread.currentThread().getContextClassLoader()
465 );
466 }
467 }
468
469
470
471
472
473 private static class PathPredicate implements Predicate<String> {
474
475
476
477
478 private final String name;
479
480
481
482
483
484 PathPredicate(final String name) {
485 this.name = name;
486 }
487
488 @Override
489 public boolean apply(@Nullable final String input) {
490 return input != null
491 && FilenameUtils.normalize(this.name, true).matches(input);
492 }
493 }
494
495
496
497
498
499
500
501
502 private static class CheckerExcludes implements Function<String, String> {
503
504
505
506
507 private static final String ALL = "*";
508
509
510
511
512 private final String checker;
513
514
515
516
517
518 CheckerExcludes(final String checker) {
519 this.checker = checker;
520 }
521
522 @Nullable
523 @Override
524 public String apply(@Nullable final String input) {
525 String result = null;
526 if (input != null) {
527 final String[] exclude = input.split(":", 2);
528 final String check = exclude[0];
529 final boolean appropriate = CheckerExcludes.ALL.equals(check)
530 || this.checker.equals(check);
531 if (appropriate && exclude.length > 1) {
532 result = exclude[1];
533 }
534 }
535 return result;
536 }
537 }
538 }