1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.jspc.plugin;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileFilter;
24 import java.io.FileReader;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.net.MalformedURLException;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Set;
37
38 import org.apache.jasper.JspC;
39 import org.apache.jasper.servlet.JspCServletContext;
40 import org.apache.jasper.servlet.TldScanner;
41 import org.apache.maven.artifact.Artifact;
42 import org.apache.maven.plugin.AbstractMojo;
43 import org.apache.maven.plugin.MojoExecutionException;
44 import org.apache.maven.plugin.MojoFailureException;
45 import org.apache.maven.project.MavenProject;
46 import org.apache.tomcat.JarScanner;
47 import org.apache.tomcat.util.scan.StandardJarScanner;
48 import org.codehaus.plexus.util.FileUtils;
49 import org.codehaus.plexus.util.StringUtils;
50 import org.eclipse.jetty.util.IO;
51 import org.eclipse.jetty.util.resource.Resource;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public class JspcMojo extends AbstractMojo
73 {
74 public static final String END_OF_WEBAPP = "</web-app>";
75 public static final String PRECOMPILED_FLAG = "org.eclipse.jetty.jsp.precompiled";
76
77
78
79
80
81
82
83
84
85
86 public static class JettyJspC extends JspC
87 {
88
89 private boolean scanAll;
90
91 public void setClassLoader (ClassLoader loader)
92 {
93 this.loader = loader;
94 }
95
96 public void setScanAllDirectories (boolean scanAll)
97 {
98 this.scanAll = scanAll;
99 }
100
101 public boolean getScanAllDirectories ()
102 {
103 return this.scanAll;
104 }
105
106
107 @Override
108 protected TldScanner newTldScanner(JspCServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal)
109 {
110 if (context != null && context.getAttribute(JarScanner.class.getName()) == null)
111 {
112 StandardJarScanner jarScanner = new StandardJarScanner();
113 jarScanner.setScanAllDirectories(getScanAllDirectories());
114 context.setAttribute(JarScanner.class.getName(), jarScanner);
115 }
116
117 return super.newTldScanner(context, namespaceAware, validate, blockExternal);
118 }
119 }
120
121
122
123
124
125
126
127
128
129 private boolean useProvidedScope;
130
131
132
133
134
135
136
137
138 private Set projectArtifacts;
139
140
141
142
143
144
145
146
147
148 private MavenProject project;
149
150
151
152
153
154
155
156
157
158 private List pluginArtifacts;
159
160
161
162
163
164
165
166
167 private String webXmlFragment;
168
169
170
171
172
173
174
175
176
177
178 private String insertionMarker;
179
180
181
182
183
184
185
186
187 private boolean mergeFragment;
188
189
190
191
192
193
194 private String generatedClasses;
195
196
197
198
199
200
201
202 private boolean keepSources;
203
204
205
206
207
208
209
210
211 private String webAppSourceDirectory;
212
213
214
215
216
217
218
219 private String webXml;
220
221
222
223
224
225
226
227
228 private String includes;
229
230
231
232
233
234
235 private String excludes;
236
237
238
239
240
241
242 private File classesDirectory;
243
244
245
246
247
248
249
250 private String tldJarNamePatterns;
251
252
253
254
255
256
257 private String sourceVersion;
258
259
260
261
262
263
264 private String targetVersion;
265
266
267
268
269
270
271
272 private JettyJspC jspc;
273
274
275
276
277
278
279
280
281
282 private boolean scanAllDirectories;
283
284
285 public void execute() throws MojoExecutionException, MojoFailureException
286 {
287 if (getLog().isDebugEnabled())
288 {
289
290 getLog().info("webAppSourceDirectory=" + webAppSourceDirectory);
291 getLog().info("generatedClasses=" + generatedClasses);
292 getLog().info("webXmlFragment=" + webXmlFragment);
293 getLog().info("webXml="+webXml);
294 getLog().info("insertionMarker="+ (insertionMarker == null || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker));
295 getLog().info("keepSources=" + keepSources);
296 getLog().info("mergeFragment=" + mergeFragment);
297 if (sourceVersion != null)
298 getLog().info("sourceVersion="+sourceVersion);
299 if (targetVersion != null)
300 getLog().info("targetVersion="+targetVersion);
301 }
302 try
303 {
304 prepare();
305 compile();
306 cleanupSrcs();
307 mergeWebXml();
308 }
309 catch (Exception e)
310 {
311 throw new MojoExecutionException("Failure processing jsps", e);
312 }
313 }
314
315 public void compile() throws Exception
316 {
317 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
318
319
320 List<URL> webAppUrls = setUpWebAppClassPath();
321
322
323 Set<URL> pluginJars = getPluginJars();
324 Set<URL> providedJars = getProvidedScopeJars(pluginJars);
325
326
327
328 List<URL> sysUrls = new ArrayList<URL>();
329 sysUrls.addAll(providedJars);
330 URLClassLoader sysClassLoader = new URLClassLoader((URL[])sysUrls.toArray(new URL[0]), currentClassLoader);
331
332
333 URLClassLoader webAppClassLoader = new URLClassLoader((URL[]) webAppUrls.toArray(new URL[0]), sysClassLoader);
334 StringBuffer webAppClassPath = new StringBuffer();
335
336 for (int i = 0; i < webAppUrls.size(); i++)
337 {
338 if (getLog().isDebugEnabled())
339 getLog().debug("webappclassloader contains: " + webAppUrls.get(i));
340 webAppClassPath.append(new File(webAppUrls.get(i).toURI()).getCanonicalPath());
341 if (getLog().isDebugEnabled())
342 getLog().debug("added to classpath: " + ((URL) webAppUrls.get(i)).getFile());
343 if (i+1<webAppUrls.size())
344 webAppClassPath.append(System.getProperty("path.separator"));
345 }
346
347
348
349
350 URLClassLoader fakeWebAppClassLoader = new URLClassLoader(new URL[0], webAppClassLoader);
351 Thread.currentThread().setContextClassLoader(fakeWebAppClassLoader);
352
353 if (jspc == null)
354 jspc = new JettyJspC();
355
356
357 jspc.setWebXmlFragment(webXmlFragment);
358 jspc.setUriroot(webAppSourceDirectory);
359 jspc.setOutputDir(generatedClasses);
360 jspc.setClassLoader(fakeWebAppClassLoader);
361 jspc.setScanAllDirectories(scanAllDirectories);
362 jspc.setCompile(true);
363 if (sourceVersion != null)
364 jspc.setCompilerSourceVM(sourceVersion);
365 if (targetVersion != null)
366 jspc.setCompilerTargetVM(targetVersion);
367
368
369
370 String jspFiles = getJspFiles(webAppSourceDirectory);
371
372 try
373 {
374 if (jspFiles == null | jspFiles.equals(""))
375 {
376 getLog().info("No files selected to precompile");
377 }
378 else
379 {
380 getLog().info("Compiling "+jspFiles+" from includes="+includes+" excludes="+excludes);
381 jspc.setJspFiles(jspFiles);
382 jspc.execute();
383 }
384 }
385 finally
386 {
387
388 Thread.currentThread().setContextClassLoader(currentClassLoader);
389 }
390 }
391
392 private String getJspFiles(String webAppSourceDirectory)
393 throws Exception
394 {
395 List fileNames = FileUtils.getFileNames(new File(webAppSourceDirectory),includes, excludes, false);
396 return StringUtils.join(fileNames.toArray(new String[0]), ",");
397
398 }
399
400
401
402
403
404
405
406 public void cleanupSrcs() throws Exception
407 {
408
409 if (!keepSources)
410 {
411 File generatedClassesDir = new File(generatedClasses);
412
413 if(generatedClassesDir.exists() && generatedClassesDir.isDirectory())
414 {
415 delete(generatedClassesDir, new FileFilter()
416 {
417 public boolean accept(File f)
418 {
419 return f.isDirectory() || f.getName().endsWith(".java");
420 }
421 });
422 }
423 }
424 }
425
426 static void delete(File dir, FileFilter filter)
427 {
428 File[] files = dir.listFiles(filter);
429 if (files != null)
430 {
431 for(File f: files)
432 {
433 if(f.isDirectory())
434 delete(f, filter);
435 else
436 f.delete();
437 }
438 }
439 }
440
441
442
443
444
445
446
447
448
449
450
451
452 public void mergeWebXml() throws Exception
453 {
454 if (mergeFragment)
455 {
456
457 File webXml = getWebXmlFile();
458
459 if (!webXml.exists())
460 {
461 getLog().info(webXml.toString() + " does not exist, cannot merge with generated fragment");
462 return;
463 }
464
465 File fragmentWebXml = new File(webXmlFragment);
466 File mergedWebXml = new File(fragmentWebXml.getParentFile(), "web.xml");
467
468 try (BufferedReader webXmlReader = new BufferedReader(new FileReader(webXml));
469 PrintWriter mergedWebXmlWriter = new PrintWriter(new FileWriter(mergedWebXml)))
470 {
471
472 if (!fragmentWebXml.exists())
473 {
474 getLog().info("No fragment web.xml file generated");
475
476 IO.copy(webXmlReader, mergedWebXmlWriter);
477 }
478 else
479 {
480
481
482 boolean atInsertPoint = false;
483 boolean atEOF = false;
484 String marker = (insertionMarker == null
485 || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker);
486 while (!atInsertPoint && !atEOF)
487 {
488 String line = webXmlReader.readLine();
489 if (line == null)
490 atEOF = true;
491 else if (line.indexOf(marker) >= 0)
492 {
493 atInsertPoint = true;
494 }
495 else
496 {
497 mergedWebXmlWriter.println(line);
498 }
499 }
500
501 if (atEOF && !atInsertPoint)
502 throw new IllegalStateException("web.xml does not contain insertionMarker "+insertionMarker);
503
504
505 mergedWebXmlWriter.println("<context-param><param-name>"+PRECOMPILED_FLAG+"</param-name><param-value>true</param-value></context-param>");
506
507
508
509 try (BufferedReader fragmentWebXmlReader =
510 new BufferedReader(new FileReader(fragmentWebXml)))
511 {
512 IO.copy(fragmentWebXmlReader, mergedWebXmlWriter);
513
514
515 if (marker.equals(END_OF_WEBAPP))
516 mergedWebXmlWriter.println(END_OF_WEBAPP);
517
518
519 IO.copy(webXmlReader, mergedWebXmlWriter);
520 }
521 }
522 }
523 }
524 }
525
526 private void prepare() throws Exception
527 {
528
529
530 File generatedSourceDirectoryFile = new File(generatedClasses);
531 if (!generatedSourceDirectoryFile.exists())
532 generatedSourceDirectoryFile.mkdirs();
533 }
534
535
536
537
538
539
540
541
542
543
544 private List<URL> setUpWebAppClassPath() throws Exception
545 {
546
547 List<URL> urls = new ArrayList<URL>();
548 String classesDir = classesDirectory.getCanonicalPath();
549 classesDir = classesDir + (classesDir.endsWith(File.pathSeparator) ? "" : File.separator);
550 urls.add(Resource.toURL(new File(classesDir)));
551
552 if (getLog().isDebugEnabled())
553 getLog().debug("Adding to classpath classes dir: " + classesDir);
554
555
556 for (Iterator<Artifact> iter = project.getArtifacts().iterator(); iter.hasNext();)
557 {
558 Artifact artifact = (Artifact)iter.next();
559
560
561 if (!Artifact.SCOPE_TEST.equals(artifact.getScope()) && !Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
562 {
563 String filePath = artifact.getFile().getCanonicalPath();
564 if (getLog().isDebugEnabled())
565 getLog().debug("Adding to classpath dependency file: " + filePath);
566
567 urls.add(Resource.toURL(artifact.getFile()));
568 }
569 }
570 return urls;
571 }
572
573
574
575
576
577
578
579 private Set<URL> getPluginJars () throws MalformedURLException
580 {
581 HashSet<URL> pluginJars = new HashSet<>();
582 for (Iterator<Artifact> iter = pluginArtifacts.iterator(); iter.hasNext(); )
583 {
584 Artifact pluginArtifact = iter.next();
585 if ("jar".equalsIgnoreCase(pluginArtifact.getType()))
586 {
587 if (getLog().isDebugEnabled()) { getLog().debug("Adding plugin artifact "+pluginArtifact);}
588 pluginJars.add(pluginArtifact.getFile().toURI().toURL());
589 }
590 }
591
592 return pluginJars;
593 }
594
595
596
597
598
599
600
601
602 private Set<URL> getProvidedScopeJars (Set<URL> pluginJars) throws MalformedURLException
603 {
604 if (!useProvidedScope)
605 return Collections.emptySet();
606
607 HashSet<URL> providedJars = new HashSet<>();
608
609 for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
610 {
611 Artifact artifact = iter.next();
612 if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
613 {
614
615 URL jar = artifact.getFile().toURI().toURL();
616 if (!pluginJars.contains(jar))
617 {
618 providedJars.add(jar);
619 if (getLog().isDebugEnabled()) { getLog().debug("Adding provided artifact: "+artifact);}
620 }
621 else
622 {
623 if (getLog().isDebugEnabled()) { getLog().debug("Skipping provided artifact: "+artifact);}
624 }
625 }
626 }
627 return providedJars;
628 }
629
630
631
632 private File getWebXmlFile ()
633 throws IOException
634 {
635 File file = null;
636 File baseDir = project.getBasedir().getCanonicalFile();
637 File defaultWebAppSrcDir = new File (baseDir, "src/main/webapp").getCanonicalFile();
638 File webAppSrcDir = new File (webAppSourceDirectory).getCanonicalFile();
639 File defaultWebXml = new File (defaultWebAppSrcDir, "web.xml").getCanonicalFile();
640
641
642 File webXmlFile = new File (webXml).getCanonicalFile();
643 if (webXmlFile.compareTo(defaultWebXml) != 0)
644 {
645 file = new File (webXml);
646 return file;
647 }
648
649
650
651 file = new File (webAppSrcDir, "web.xml");
652 return file;
653 }
654 }