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