1 package org.codehaus.mojo.gwt.shell;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.regex.Pattern;
30
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.apache.maven.plugins.annotations.Component;
35 import org.apache.maven.plugins.annotations.Execute;
36 import org.apache.maven.plugins.annotations.LifecyclePhase;
37 import org.apache.maven.plugins.annotations.Mojo;
38 import org.apache.maven.plugins.annotations.Parameter;
39 import org.apache.maven.plugins.annotations.ResolutionScope;
40 import org.apache.maven.project.MavenProject;
41 import org.codehaus.mojo.gwt.utils.GwtModuleReaderException;
42 import org.codehaus.plexus.archiver.ArchiverException;
43 import org.codehaus.plexus.archiver.UnArchiver;
44 import org.codehaus.plexus.archiver.manager.ArchiverManager;
45 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
46 import org.codehaus.plexus.util.FileUtils;
47 import org.codehaus.plexus.util.DirectoryScanner;
48
49
50
51
52
53
54
55
56 @Mojo(name = "run", requiresDirectInvocation = true, requiresDependencyResolution = ResolutionScope.TEST)
57 @Execute(phase = LifecyclePhase.PROCESS_CLASSES, goal = "war:exploded")
58 public class RunMojo
59 extends AbstractGwtWebMojo
60 {
61
62
63
64 @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}")
65
66 private File hostedWebapp;
67
68
69
70
71 @Parameter(defaultValue = "${executedProject}")
72 private MavenProject executedProject;
73
74
75
76
77
78
79
80
81
82
83
84
85 @Parameter(property = "runTarget", required = true)
86 private String runTarget;
87
88
89
90
91 @Parameter
92 @SuppressWarnings("unused")
93 private int runTimeOut;
94
95
96
97
98 @Parameter(defaultValue = "8888", property = "gwt.port")
99 private int port;
100
101
102
103
104 @Parameter(defaultValue = "9997", property = "gwt.codeServerPort")
105 private int codeServerPort;
106
107
108
109
110 @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true)
111 private File buildOutputDirectory;
112
113
114
115
116
117
118 @Parameter(defaultValue = "false", property = "gwt.noserver")
119 private boolean noServer;
120
121
122
123
124 @Parameter(property = "gwt.server")
125 private String server;
126
127
128
129
130
131
132 @Parameter
133 private Map<String, String> systemProperties;
134
135
136
137
138
139
140
141
142
143 @Parameter(defaultValue = "false", property = "gwt.copyWebapp")
144 private boolean copyWebapp;
145
146
147
148
149
150
151
152
153
154
155 @Parameter(defaultValue = "1.3.8", property = "gwt.appEngineVersion")
156 private String appEngineVersion;
157
158
159
160
161
162
163
164
165
166 @Parameter
167 private List<String> runClasspathExcludes;
168
169
170
171
172
173
174
175
176 @Parameter(defaultValue = "${project.build.directory}/appengine-sdk/", property = "gwt.appEngineHome")
177 private File appEngineHome;
178
179
180
181
182
183
184 @Parameter(defaultValue = "com.google.appengine", property = "gwt.appEngineGroupId")
185 private String appEngineGroupId;
186
187
188
189
190
191
192 @Parameter(defaultValue = "appengine-java-sdk", property = "gwt.appEngineArtifactId")
193 private String appEngineArtifactId;
194
195
196
197
198
199 @Component
200 protected ArchiverManager archiverManager;
201
202
203
204
205
206
207
208 @Parameter(property = "gwt.bindAddress")
209 private String bindAddress;
210
211
212
213
214
215
216 @Parameter(defaultValue = "true", property = "gwt.cacheGeneratorResults")
217 private boolean cacheGeneratorResults;
218
219
220
221
222
223
224 @Parameter
225 private File workDir;
226
227
228
229
230
231
232 @Parameter
233 private File logDir;
234
235
236
237
238
239
240
241
242 @Parameter(defaultValue = "auto", property = "maven.compiler.source")
243 private String sourceLevel;
244
245
246
247
248
249
250 @Parameter(defaultValue = "true", property = "gwt.superDevMode")
251 private boolean superDevMode;
252
253
254
255
256
257
258 @Parameter(alias = "compilePerFile", defaultValue = "true", property = "gwt.compiler.incremental")
259 private boolean incremental;
260
261
262
263
264
265
266 @Parameter(alias = "generateJsInteropExports", defaultValue = "false", property = "gwt.compiler.generateJsInteropExports")
267 private boolean generateJsInteropExports;
268
269
270
271
272
273
274
275
276 @Parameter(defaultValue = "NONE", property = "gwt.compiler.methodNameDisplayMode")
277 private String methodNameDisplayMode;
278
279
280
281
282 public String getStartupUrl()
283 throws MojoExecutionException
284 {
285 if ( noServer )
286 {
287 return runTarget;
288 }
289
290 int dash = runTarget.indexOf( '/' );
291 if ( dash > 0 )
292 {
293 String prefix = runTarget.substring( 0, dash );
294
295
296 String[] modules = getModules();
297 for ( String module : modules )
298 {
299 if ( prefix.equals( module ) )
300 {
301 try
302 {
303 return readModule( module ).getPath() + '/' + runTarget.substring( dash + 1 );
304 }
305 catch ( GwtModuleReaderException e )
306 {
307 throw new MojoExecutionException( e.getMessage(), e );
308 }
309 }
310 }
311 }
312 return runTarget;
313 }
314
315 public void doExecute( )
316 throws MojoExecutionException, MojoFailureException
317 {
318 JavaCommand cmd = createJavaCommand()
319 .setMainClass( "com.google.gwt.dev.DevMode" );
320
321 if ( gwtSdkFirstInClasspath )
322 {
323 cmd.addToClasspath( getGwtUserJar() )
324 .addToClasspath( getGwtDevJar() );
325 }
326
327 cmd.addToClasspath( getClasspath( Artifact.SCOPE_RUNTIME ) );
328 addCompileSourceArtifacts( cmd );
329 addArgumentDeploy(cmd);
330 addArgumentGen( cmd );
331 addPersistentUnitCache(cmd);
332
333 if ( !gwtSdkFirstInClasspath )
334 {
335 cmd.addToClasspath( getGwtUserJar() )
336 .addToClasspath( getGwtDevJar() );
337 }
338
339 cmd.arg( "-war", hostedWebapp.getAbsolutePath() )
340 .arg( "-logLevel", getLogLevel() )
341 .arg( "-port", Integer.toString( getPort() ) )
342 .arg( "-codeServerPort" , Integer.toString( codeServerPort ))
343 .arg( "-startupUrl", getStartupUrl() )
344 .arg( noServer, "-nostartServer" )
345 .arg( !cacheGeneratorResults, "-XnocacheGeneratorResults" )
346 .arg( !superDevMode, "-nosuperDevMode" )
347 .arg( !incremental, "-noincremental" )
348 .arg( generateJsInteropExports, "-generateJsInteropExports" )
349 .arg( "-sourceLevel", sourceLevel );
350
351 if ( style != null && style.length() > 0 )
352 {
353 cmd.arg( "-style", style );
354 }
355
356 if ( methodNameDisplayMode != null && methodNameDisplayMode.length() > 0 && !methodNameDisplayMode.equals( "NONE" ))
357 {
358 cmd.arg( "-XmethodNameDisplayMode", methodNameDisplayMode );
359 }
360
361 if ( workDir != null )
362 {
363 cmd.arg( "-workDir", workDir.getAbsolutePath() );
364 }
365 if ( logDir != null )
366 {
367 cmd.arg( "-logdir", logDir.getAbsolutePath() );
368 }
369
370 if ( server != null )
371 {
372 cmd.arg( "-server", server );
373 }
374
375 if ( systemProperties != null && !systemProperties.isEmpty() )
376 {
377 for ( String key : systemProperties.keySet() )
378 {
379 String value = systemProperties.get( key );
380 if ( value != null )
381 {
382 getLog().debug( " " + key + "=" + value );
383 cmd.systemProperty( key, value );
384 }
385 else
386 {
387 getLog().debug( "skip sysProps " + key + " with empty value" );
388 }
389 }
390 }
391
392 if ( bindAddress != null && bindAddress.length() > 0 )
393 {
394 cmd.arg( "-bindAddress" ).arg( bindAddress );
395 }
396
397 if ( modulePathPrefix != null && !modulePathPrefix.isEmpty() )
398 {
399 cmd.arg( "-modulePathPrefix" ).arg( modulePathPrefix );
400 }
401
402 if ( !noServer )
403 {
404 setupExplodedWar();
405 }
406 else
407 {
408 getLog().info( "noServer is set! Skipping exploding war file..." );
409 }
410
411 for ( String module : getModules() )
412 {
413 cmd.arg( module );
414 }
415
416 try
417 {
418 cmd.execute();
419 }
420 catch ( JavaCommandException e )
421 {
422 throw new MojoExecutionException( e.getMessage(), e );
423 }
424 }
425
426 @Override
427 protected void postProcessClassPath( Collection<File> classPath )
428 {
429 boolean isAppEngine = "com.google.appengine.tools.development.gwt.AppEngineLauncher".equals( server );
430 List<Pattern> patternsToExclude = new ArrayList<Pattern>();
431 if ( runClasspathExcludes != null && !runClasspathExcludes.isEmpty() )
432 {
433 for ( String runClasspathExclude : runClasspathExcludes )
434 {
435 patternsToExclude.add( Pattern.compile( runClasspathExclude ) );
436 }
437 }
438 Iterator<File> it = classPath.iterator();
439 while ( it.hasNext() )
440 {
441 String name = it.next().getName();
442 if ( !patternsToExclude.isEmpty() )
443 {
444 for ( Pattern pattern : patternsToExclude )
445 {
446 if ( pattern.matcher( name ).find() )
447 {
448 getLog().info( "remove jar " + name + " from system classpath" );
449 it.remove();
450 continue;
451 }
452 }
453 }
454
455 }
456
457 if ( isAppEngine )
458 {
459 File appEngineToolsApi = new File( appEngineHome, "/lib/appengine-tools-api.jar" );
460 File appEngineLocalRuntime = new File( appEngineHome, "/lib/impl/appengine-local-runtime.jar" );
461 File appEngineAgent = new File( appEngineHome, "/lib/agent/appengine-agent.jar" );
462 if ( appEngineHome.exists() && appEngineToolsApi.exists() && appEngineLocalRuntime.exists()
463 && appEngineAgent.exists() )
464 {
465 classPath.add( appEngineToolsApi );
466 classPath.add( appEngineLocalRuntime );
467 classPath.add( appEngineAgent );
468 }
469 else
470 {
471 try
472 {
473 if ( !appEngineHome.exists() )
474 {
475 appEngineHome.mkdirs();
476
477 Artifact appEngineSdk =
478 resolve( appEngineGroupId, appEngineArtifactId, appEngineVersion, "zip", "" );
479
480 UnArchiver unArchiver = archiverManager.getUnArchiver( appEngineSdk.getFile() );
481 unArchiver.setSourceFile( appEngineSdk.getFile() );
482 unArchiver.setDestDirectory( appEngineHome );
483 getLog().info( "extract appengine " + appEngineVersion + " sdk to " + appEngineHome.getPath() );
484 unArchiver.extract();
485 }
486 else
487 {
488 getLog().info( "use existing appengine sdk from " + appEngineHome.getPath() );
489 }
490 appEngineToolsApi =
491 new File( appEngineHome, "appengine-java-sdk-" + appEngineVersion
492 + "/lib/appengine-tools-api.jar" );
493 if ( !appEngineToolsApi.exists() )
494 {
495 throw new RuntimeException( appEngineToolsApi.getPath() + " not exists" );
496 }
497 classPath.add( appEngineToolsApi );
498 getLog().debug( "add " + appEngineToolsApi.getPath() + " to the classpath" );
499
500 appEngineLocalRuntime =
501 new File( appEngineHome, "appengine-java-sdk-" + appEngineVersion
502 + "/lib/impl/appengine-local-runtime.jar" );
503 if ( !appEngineLocalRuntime.exists() )
504 {
505 throw new RuntimeException( appEngineLocalRuntime.getPath() + " not exists" );
506 }
507 classPath.add( appEngineLocalRuntime );
508 getLog().debug( "add " + appEngineLocalRuntime.getPath() + " to the classpath" );
509
510 appEngineAgent =
511 new File( appEngineHome, "appengine-java-sdk-" + appEngineVersion
512 + "/lib/agent/appengine-agent.jar" );
513 classPath.add( appEngineAgent );
514 getLog().debug( "add " + appEngineAgent.getPath() + " to the classpath" );
515 }
516 catch ( MojoExecutionException e )
517 {
518
519 throw new RuntimeException( e.getMessage(), e );
520 }
521 catch ( ArchiverException e )
522 {
523
524 throw new RuntimeException( e.getMessage(), e );
525 }
526 catch ( NoSuchArchiverException e )
527 {
528
529 throw new RuntimeException( e.getMessage(), e );
530 }
531 }
532 }
533 }
534
535
536
537
538
539
540
541
542 private void copyDirectoryStructureIfModified(File sourceDir, File destDir)
543 throws IOException {
544
545 DirectoryScanner scanner = new DirectoryScanner();
546 scanner.setBasedir( sourceDir );
547 scanner.addDefaultExcludes();
548 scanner.scan();
549
550
551
552
553 destDir.mkdirs();
554 String[] includedDirs = scanner.getIncludedDirectories();
555 for ( int i = 0; i < includedDirs.length; ++i ) {
556 File clonedDir = new File( destDir, includedDirs[i] );
557 clonedDir.mkdirs();
558 }
559
560 String[] includedFiles = scanner.getIncludedFiles();
561 for ( int i = 0; i < includedFiles.length; ++i ) {
562 File sourceFile = new File(sourceDir, includedFiles[i]);
563 File destFile = new File(destDir, includedFiles[i]);
564 FileUtils.copyFileIfModified(sourceFile, destFile);
565 }
566 }
567
568 private void setupExplodedWar()
569 throws MojoExecutionException
570 {
571 getLog().info( "create exploded Jetty webapp in " + hostedWebapp );
572
573 if ( copyWebapp && !warSourceDirectory.getAbsolutePath().equals( hostedWebapp.getAbsolutePath() ) )
574 {
575 try
576 {
577
578
579 copyDirectoryStructureIfModified(warSourceDirectory, hostedWebapp);
580 }
581 catch ( IOException e )
582 {
583 throw new MojoExecutionException( "Failed to copy warSourceDirectory to " + hostedWebapp, e );
584 }
585 }
586
587 File classes = new File( hostedWebapp, "WEB-INF/classes" );
588 classes.mkdirs();
589
590 if ( !buildOutputDirectory.getAbsolutePath().equals( classes.getAbsolutePath() ) )
591 {
592 getLog().warn( "Your POM <build><outputdirectory> does not match your "
593 + "hosted webapp WEB-INF/classes folder for GWT Hosted browser to see your classes." );
594 try
595 {
596 FileUtils.copyDirectoryStructure( buildOutputDirectory, classes );
597 }
598 catch ( IOException e )
599 {
600 throw new MojoExecutionException( "Failed to copy classes to " + classes , e );
601 }
602 }
603
604 File lib = new File( hostedWebapp, "WEB-INF/lib" );
605 lib.mkdirs();
606
607 Collection<Artifact> artifacts = getProjectRuntimeArtifacts();
608 for ( Artifact artifact : artifacts )
609 {
610 try
611 {
612
613 if ( ! artifact.getFile().isDirectory() )
614 {
615 FileUtils.copyFileToDirectory( artifact.getFile(), lib );
616 }
617 }
618 catch ( IOException e )
619 {
620 throw new MojoExecutionException( "Failed to copy runtime dependency " + artifact, e );
621 }
622 }
623 }
624
625 public int getPort()
626 {
627 return this.port;
628 }
629
630
631
632
633 public void setRunTimeOut( int runTimeOut )
634 {
635 setTimeOut( runTimeOut );
636 }
637
638 public void setExecutedProject( MavenProject executedProject )
639 {
640 this.executedProject = executedProject;
641 }
642
643 @Override
644 public MavenProject getProject()
645 {
646 return executedProject;
647 }
648 }