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
258
259 private JettyJspC jspc;
260
261
262
263
264
265
266
267
268
269 private boolean scanAllDirectories;
270
271
272 public void execute() throws MojoExecutionException, MojoFailureException
273 {
274 if (getLog().isDebugEnabled())
275 {
276
277 getLog().info("webAppSourceDirectory=" + webAppSourceDirectory);
278 getLog().info("generatedClasses=" + generatedClasses);
279 getLog().info("webXmlFragment=" + webXmlFragment);
280 getLog().info("webXml="+webXml);
281 getLog().info("insertionMarker="+ (insertionMarker == null || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker));
282 getLog().info("keepSources=" + keepSources);
283 getLog().info("mergeFragment=" + mergeFragment);
284 }
285 try
286 {
287 prepare();
288 compile();
289 cleanupSrcs();
290 mergeWebXml();
291 }
292 catch (Exception e)
293 {
294 throw new MojoExecutionException("Failure processing jsps", e);
295 }
296 }
297
298 public void compile() throws Exception
299 {
300 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
301
302
303 List<URL> webAppUrls = setUpWebAppClassPath();
304
305
306 Set<URL> pluginJars = getPluginJars();
307 Set<URL> providedJars = getProvidedScopeJars(pluginJars);
308
309
310
311 List<URL> sysUrls = new ArrayList<URL>();
312 sysUrls.addAll(providedJars);
313 URLClassLoader sysClassLoader = new URLClassLoader((URL[])sysUrls.toArray(new URL[0]), currentClassLoader);
314
315
316 URLClassLoader webAppClassLoader = new URLClassLoader((URL[]) webAppUrls.toArray(new URL[0]), sysClassLoader);
317 StringBuffer webAppClassPath = new StringBuffer();
318
319 for (int i = 0; i < webAppUrls.size(); i++)
320 {
321 if (getLog().isDebugEnabled())
322 getLog().debug("webappclassloader contains: " + webAppUrls.get(i));
323 webAppClassPath.append(new File(webAppUrls.get(i).toURI()).getCanonicalPath());
324 if (getLog().isDebugEnabled())
325 getLog().debug("added to classpath: " + ((URL) webAppUrls.get(i)).getFile());
326 if (i+1<webAppUrls.size())
327 webAppClassPath.append(System.getProperty("path.separator"));
328 }
329
330
331
332
333 URLClassLoader fakeWebAppClassLoader = new URLClassLoader(new URL[0], webAppClassLoader);
334 Thread.currentThread().setContextClassLoader(fakeWebAppClassLoader);
335
336 if (jspc == null)
337 jspc = new JettyJspC();
338
339
340 jspc.setWebXmlFragment(webXmlFragment);
341 jspc.setUriroot(webAppSourceDirectory);
342 jspc.setOutputDir(generatedClasses);
343 jspc.setClassLoader(fakeWebAppClassLoader);
344 jspc.setScanAllDirectories(scanAllDirectories);
345 jspc.setCompile(true);
346
347
348
349 String jspFiles = getJspFiles(webAppSourceDirectory);
350
351 try
352 {
353 if (jspFiles == null | jspFiles.equals(""))
354 {
355 getLog().info("No files selected to precompile");
356 }
357 else
358 {
359 getLog().info("Compiling "+jspFiles+" from includes="+includes+" excludes="+excludes);
360 jspc.setJspFiles(jspFiles);
361 jspc.execute();
362 }
363 }
364 finally
365 {
366
367 Thread.currentThread().setContextClassLoader(currentClassLoader);
368 }
369 }
370
371 private String getJspFiles(String webAppSourceDirectory)
372 throws Exception
373 {
374 List fileNames = FileUtils.getFileNames(new File(webAppSourceDirectory),includes, excludes, false);
375 return StringUtils.join(fileNames.toArray(new String[0]), ",");
376
377 }
378
379
380
381
382
383
384
385 public void cleanupSrcs() throws Exception
386 {
387
388 if (!keepSources)
389 {
390 File generatedClassesDir = new File(generatedClasses);
391
392 if(generatedClassesDir.exists() && generatedClassesDir.isDirectory())
393 {
394 delete(generatedClassesDir, new FileFilter()
395 {
396 public boolean accept(File f)
397 {
398 return f.isDirectory() || f.getName().endsWith(".java");
399 }
400 });
401 }
402 }
403 }
404
405 static void delete(File dir, FileFilter filter)
406 {
407 File[] files = dir.listFiles(filter);
408 if (files != null)
409 {
410 for(File f: files)
411 {
412 if(f.isDirectory())
413 delete(f, filter);
414 else
415 f.delete();
416 }
417 }
418 }
419
420
421
422
423
424
425
426
427
428
429
430
431 public void mergeWebXml() throws Exception
432 {
433 if (mergeFragment)
434 {
435
436 File webXml = getWebXmlFile();
437
438 if (!webXml.exists())
439 {
440 getLog().info(webXml.toString() + " does not exist, cannot merge with generated fragment");
441 return;
442 }
443
444 File fragmentWebXml = new File(webXmlFragment);
445 File mergedWebXml = new File(fragmentWebXml.getParentFile(), "web.xml");
446
447 try (BufferedReader webXmlReader = new BufferedReader(new FileReader(webXml));
448 PrintWriter mergedWebXmlWriter = new PrintWriter(new FileWriter(mergedWebXml)))
449 {
450
451 if (!fragmentWebXml.exists())
452 {
453 getLog().info("No fragment web.xml file generated");
454
455 IO.copy(webXmlReader, mergedWebXmlWriter);
456 }
457 else
458 {
459
460
461 boolean atInsertPoint = false;
462 boolean atEOF = false;
463 String marker = (insertionMarker == null
464 || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker);
465 while (!atInsertPoint && !atEOF)
466 {
467 String line = webXmlReader.readLine();
468 if (line == null)
469 atEOF = true;
470 else if (line.indexOf(marker) >= 0)
471 {
472 atInsertPoint = true;
473 }
474 else
475 {
476 mergedWebXmlWriter.println(line);
477 }
478 }
479
480 if (atEOF && !atInsertPoint)
481 throw new IllegalStateException("web.xml does not contain insertionMarker "+insertionMarker);
482
483
484 mergedWebXmlWriter.println("<context-param><param-name>"+PRECOMPILED_FLAG+"</param-name><param-value>true</param-value></context-param>");
485
486
487
488 try (BufferedReader fragmentWebXmlReader =
489 new BufferedReader(new FileReader(fragmentWebXml)))
490 {
491 IO.copy(fragmentWebXmlReader, mergedWebXmlWriter);
492
493
494 if (marker.equals(END_OF_WEBAPP))
495 mergedWebXmlWriter.println(END_OF_WEBAPP);
496
497
498 IO.copy(webXmlReader, mergedWebXmlWriter);
499 }
500 }
501 }
502 }
503 }
504
505 private void prepare() throws Exception
506 {
507
508
509 File generatedSourceDirectoryFile = new File(generatedClasses);
510 if (!generatedSourceDirectoryFile.exists())
511 generatedSourceDirectoryFile.mkdirs();
512 }
513
514
515
516
517
518
519
520
521
522
523 private List<URL> setUpWebAppClassPath() throws Exception
524 {
525
526 List<URL> urls = new ArrayList<URL>();
527 String classesDir = classesDirectory.getCanonicalPath();
528 classesDir = classesDir + (classesDir.endsWith(File.pathSeparator) ? "" : File.separator);
529 urls.add(Resource.toURL(new File(classesDir)));
530
531 if (getLog().isDebugEnabled())
532 getLog().debug("Adding to classpath classes dir: " + classesDir);
533
534
535 for (Iterator<Artifact> iter = project.getArtifacts().iterator(); iter.hasNext();)
536 {
537 Artifact artifact = (Artifact)iter.next();
538
539
540 if (!Artifact.SCOPE_TEST.equals(artifact.getScope()) && !Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
541 {
542 String filePath = artifact.getFile().getCanonicalPath();
543 if (getLog().isDebugEnabled())
544 getLog().debug("Adding to classpath dependency file: " + filePath);
545
546 urls.add(Resource.toURL(artifact.getFile()));
547 }
548 }
549 return urls;
550 }
551
552
553
554
555
556
557
558 private Set<URL> getPluginJars () throws MalformedURLException
559 {
560 HashSet<URL> pluginJars = new HashSet<>();
561 for (Iterator<Artifact> iter = pluginArtifacts.iterator(); iter.hasNext(); )
562 {
563 Artifact pluginArtifact = iter.next();
564 if ("jar".equalsIgnoreCase(pluginArtifact.getType()))
565 {
566 if (getLog().isDebugEnabled()) { getLog().debug("Adding plugin artifact "+pluginArtifact);}
567 pluginJars.add(pluginArtifact.getFile().toURI().toURL());
568 }
569 }
570
571 return pluginJars;
572 }
573
574
575
576
577
578
579
580
581 private Set<URL> getProvidedScopeJars (Set<URL> pluginJars) throws MalformedURLException
582 {
583 if (!useProvidedScope)
584 return Collections.emptySet();
585
586 HashSet<URL> providedJars = new HashSet<>();
587
588 for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
589 {
590 Artifact artifact = iter.next();
591 if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
592 {
593
594 URL jar = artifact.getFile().toURI().toURL();
595 if (!pluginJars.contains(jar))
596 {
597 providedJars.add(jar);
598 if (getLog().isDebugEnabled()) { getLog().debug("Adding provided artifact: "+artifact);}
599 }
600 else
601 {
602 if (getLog().isDebugEnabled()) { getLog().debug("Skipping provided artifact: "+artifact);}
603 }
604 }
605 }
606 return providedJars;
607 }
608
609
610
611 private File getWebXmlFile ()
612 throws IOException
613 {
614 File file = null;
615 File baseDir = project.getBasedir().getCanonicalFile();
616 File defaultWebAppSrcDir = new File (baseDir, "src/main/webapp").getCanonicalFile();
617 File webAppSrcDir = new File (webAppSourceDirectory).getCanonicalFile();
618 File defaultWebXml = new File (defaultWebAppSrcDir, "web.xml").getCanonicalFile();
619
620
621 File webXmlFile = new File (webXml).getCanonicalFile();
622 if (webXmlFile.compareTo(defaultWebXml) != 0)
623 {
624 file = new File (webXml);
625 return file;
626 }
627
628
629
630 file = new File (webAppSrcDir, "web.xml");
631 return file;
632 }
633 }