View Javadoc
1   package org.codehaus.mojo.gwt;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
23  import static org.apache.maven.artifact.Artifact.SCOPE_RUNTIME;
24  
25  import org.apache.commons.io.IOUtils;
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.artifact.ArtifactUtils;
28  import org.apache.maven.artifact.factory.ArtifactFactory;
29  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
30  import org.apache.maven.artifact.repository.ArtifactRepository;
31  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
32  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
33  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
34  import org.apache.maven.artifact.resolver.ArtifactResolver;
35  import org.apache.maven.artifact.versioning.ArtifactVersion;
36  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
37  import org.apache.maven.model.Dependency;
38  import org.apache.maven.model.Resource;
39  import org.apache.maven.plugin.AbstractMojo;
40  import org.apache.maven.plugin.MojoExecutionException;
41  import org.apache.maven.plugins.annotations.Component;
42  import org.apache.maven.plugins.annotations.Parameter;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.maven.project.MavenProjectBuilder;
45  import org.apache.maven.project.artifact.MavenMetadataSource;
46  import org.codehaus.plexus.util.StringUtils;
47  
48  import java.io.File;
49  import java.io.IOException;
50  import java.io.InputStream;
51  import java.net.MalformedURLException;
52  import java.net.URL;
53  import java.util.ArrayList;
54  import java.util.Collection;
55  import java.util.Collections;
56  import java.util.HashSet;
57  import java.util.List;
58  import java.util.Map;
59  import java.util.Properties;
60  import java.util.Set;
61  
62  /**
63   * Abstract Support class for all GWT-related operations.
64   * <p>
65   * Provide methods to build classpath for GWT SDK tools.
66   *
67   * @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
68   * @version $Id$
69   */
70  public abstract class AbstractGwtMojo
71      extends AbstractMojo
72  {
73      protected static final String GWT_USER = "com.google.gwt:gwt-user";
74  
75      protected static final String GWT_DEV = "com.google.gwt:gwt-dev";
76  
77      /** GWT artifacts groupId */
78      public static final String GWT_GROUP_ID = "com.google.gwt";
79  
80      // --- Some Maven tools ----------------------------------------------------
81  
82      @Parameter(defaultValue = "${plugin.artifactMap}", required = true, readonly = true)
83      private Map<String, Artifact> pluginArtifactMap;
84  
85      @Component
86      private MavenProjectBuilder projectBuilder;
87  
88      @Component
89      protected ArtifactResolver resolver;
90  
91      @Component
92      protected ArtifactFactory artifactFactory;
93  
94      @Component
95      protected ClasspathBuilder classpathBuilder;
96  
97      // --- Some MavenSession related structures --------------------------------
98  
99      @Parameter(defaultValue = "${localRepository}", required = true, readonly = true)
100     protected ArtifactRepository localRepository;
101 
102     @Parameter(defaultValue = "${project.pluginArtifactRepositories}", required = true, readonly = true)
103     protected List<ArtifactRepository> remoteRepositories;
104 
105     @Component
106     protected ArtifactMetadataSource artifactMetadataSource;
107 
108     /**
109      * The maven project descriptor
110      */
111     @Parameter(defaultValue = "${project}", required = true, readonly = true)
112     private MavenProject project;
113 
114     // --- Plugin parameters ---------------------------------------------------
115 
116     /**
117      * Folder where generated-source will be created (automatically added to compile classpath).
118      */
119     @Parameter(defaultValue = "${project.build.directory}/generated-sources/gwt", required = true)
120     private File generateDirectory;
121 
122     /**
123      * Location on filesystem where GWT will write output files (-out option to GWTCompiler).
124      */
125     @Parameter(property = "gwt.war", defaultValue="${project.build.directory}/${project.build.finalName}", alias = "outputDirectory")
126     private File webappDirectory;
127 
128     /**
129      * Prefix to prepend to module names inside {@code webappDirectory} or in URLs in DevMode.
130      * <p>
131      * Could also be seen as a suffix to {@code webappDirectory}.
132      */
133     @Parameter(property = "gwt.modulePathPrefix")
134     protected String modulePathPrefix;
135 
136     /**
137      * Location of the web application static resources (same as maven-war-plugin parameter)
138      */
139     @Parameter(defaultValue="${basedir}/src/main/webapp")
140     protected File warSourceDirectory;
141 
142     /**
143      * Select the place where GWT application is built. In <code>inplace</code> mode, the warSourceDirectory is used to
144      * match the same use case of the {@link war:inplace
145      * http://maven.apache.org/plugins/maven-war-plugin/inplace-mojo.html} goal.
146      */
147     @Parameter(defaultValue = "false", property = "gwt.inplace")
148     private boolean inplace;
149 
150     /**
151      * The forked command line will use gwt sdk jars first in classpath.
152      * see issue http://code.google.com/p/google-web-toolkit/issues/detail?id=5290
153      *
154      * @since 2.1.0-1
155      * @deprecated tweak your dependencies and/or split your project with a client-only module
156      */
157     @Deprecated
158     @Parameter(defaultValue = "false", property = "gwt.gwtSdkFirstInClasspath")
159     protected boolean gwtSdkFirstInClasspath;
160 
161     public File getOutputDirectory()
162     {
163         File out = inplace ? warSourceDirectory : webappDirectory;
164         if ( !StringUtils.isBlank( modulePathPrefix ) ) 
165         {
166             out = new File(out, modulePathPrefix);
167         }
168         return out;
169     }
170 
171     /**
172      * Add classpath elements to a classpath URL set
173      *
174      * @param elements the initial URL set
175      * @param urls the urls to add
176      * @param startPosition the position to insert URLS
177      * @return full classpath URL set
178      * @throws MojoExecutionException some error occured
179      */
180     protected int addClasspathElements( Collection<?> elements, URL[] urls, int startPosition )
181         throws MojoExecutionException
182     {
183         for ( Object object : elements )
184         {
185             try
186             {
187                 if ( object instanceof Artifact )
188                 {
189                     urls[startPosition] = ( (Artifact) object ).getFile().toURI().toURL();
190                 }
191                 else if ( object instanceof Resource )
192                 {
193                     urls[startPosition] = new File( ( (Resource) object ).getDirectory() ).toURI().toURL();
194                 }
195                 else
196                 {
197                     urls[startPosition] = new File( (String) object ).toURI().toURL();
198                 }
199             }
200             catch ( MalformedURLException e )
201             {
202                 throw new MojoExecutionException(
203                                                   "Failed to convert original classpath element " + object + " to URL.",
204                                                   e );
205             }
206             startPosition++;
207         }
208         return startPosition;
209     }
210 
211 
212     /**
213      * Build the GWT classpath for the specified scope
214      *
215      * @param scope Artifact.SCOPE_COMPILE or Artifact.SCOPE_TEST
216      * @return a collection of dependencies as Files for the specified scope.
217      * @throws MojoExecutionException if classPath building failed
218      */
219     public Collection<File> getClasspath( String scope )
220         throws MojoExecutionException
221     {
222         try
223         {
224             Collection<File> files = classpathBuilder.buildClasspathList( getProject(), scope, getProjectArtifacts(), isGenerator() );
225 
226             if ( getLog().isDebugEnabled() )
227             {
228                 getLog().debug( "GWT SDK execution classpath :" );
229                 for ( File f : files )
230                 {
231                     getLog().debug( "   " + f.getAbsolutePath() );
232                 }
233             }
234             return files;
235         }
236         catch ( ClasspathBuilderException e )
237         {
238             throw new MojoExecutionException( e.getMessage(), e );
239         }
240     }
241 
242 
243     /**
244      * Whether to use processed resources and compiled classes ({@code false}), or raw resources ({@code true }).
245      */
246     protected boolean isGenerator() {
247         return false;
248     }
249 
250     protected Collection<File> getGwtDevJar() throws MojoExecutionException
251     {
252         return getJarFiles( GWT_DEV, true );
253     }
254 
255     protected Collection<File> getGwtUserJar() throws MojoExecutionException
256     {
257         return getJarFiles( GWT_USER, true );
258     }
259 
260     protected Collection<File> getJarFiles(String artifactId, boolean detectProjectDependencies) throws MojoExecutionException
261     {
262         checkGwtUserVersion();
263 
264         if (detectProjectDependencies) for (Artifact artifact : project.getArtifacts()) {
265             String dependencyKey = ArtifactUtils.versionlessKey(artifact.getGroupId(), artifact.getArtifactId());
266             // if the project contains this GWT lib do not returns any jar, maven resolution should do that
267             if (dependencyKey.equals(artifactId)) return Collections.emptyList();
268         }
269 
270         Artifact rootArtifact = pluginArtifactMap.get( artifactId );
271 
272         ArtifactResolutionResult result;
273         try
274         {
275             // Code shamelessly copied from exec-maven-plugin.
276             MavenProject rootProject =
277                             this.projectBuilder.buildFromRepository( rootArtifact, this.remoteRepositories,
278                                                                      this.localRepository );
279             List<Dependency> dependencies = rootProject.getDependencies();
280             Set<Artifact> dependencyArtifacts =
281                             MavenMetadataSource.createArtifacts( artifactFactory, dependencies, null, null, null );
282             dependencyArtifacts.add( rootProject.getArtifact() );
283             result = resolver.resolveTransitively( dependencyArtifacts, rootArtifact,
284                                                    Collections.EMPTY_MAP, localRepository,
285                                                    remoteRepositories, artifactMetadataSource,
286                                                    null, Collections.EMPTY_LIST);
287         }
288         catch (Exception e)
289         {
290             throw new MojoExecutionException( "Failed to resolve artifact", e);
291         }
292 
293         Collection<Artifact> resolved = result.getArtifacts();
294         Collection<File> files = new ArrayList<File>(resolved.size() + 1 );
295         files.add( rootArtifact.getFile() );
296         for ( Artifact artifact : resolved )
297         {
298             files.add( artifact.getFile() );
299         }
300 
301         return files;
302     }
303 
304     /**
305      * Check gwt-user dependency matches plugin version
306      */
307     private void checkGwtUserVersion() throws MojoExecutionException
308     {
309         InputStream inputStream = Thread.currentThread().getContextClassLoader()
310             .getResourceAsStream( "org/codehaus/mojo/gwt/mojoGwtVersion.properties" );
311         Properties properties = new Properties();
312         try
313         {
314             properties.load( inputStream );
315 
316         }
317         catch (IOException e)
318         {
319             throw new MojoExecutionException( "Failed to load plugin properties", e );
320         }
321         finally
322         {
323             IOUtils.closeQuietly( inputStream );
324         }
325 
326         Artifact gwtUser = project.getArtifactMap().get( GWT_USER );
327         if (gwtUser != null)
328         {
329             String mojoGwtVersion = properties.getProperty( "gwt.version" );
330             //ComparableVersion with an up2date maven version
331             ArtifactVersion mojoGwtArtifactVersion = new DefaultArtifactVersion( mojoGwtVersion );
332             ArtifactVersion userGwtArtifactVersion = new DefaultArtifactVersion( gwtUser.getVersion() );
333             if ( userGwtArtifactVersion.compareTo( mojoGwtArtifactVersion ) < 0 )
334             {
335                 getLog().warn( "Your project declares dependency on gwt-user " + gwtUser.getVersion()
336                                    + ". This plugin is designed for at least gwt version " + mojoGwtVersion );
337             }
338         }
339     }
340 
341     protected Artifact resolve( String groupId, String artifactId, String version, String type, String classifier )
342         throws MojoExecutionException
343     {
344         // return project.getArtifactMap().get( groupId + ":" + artifactId );
345 
346         Artifact artifact = artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, type, classifier );
347         try
348         {
349             resolver.resolve(artifact, remoteRepositories, localRepository);
350         }
351         catch ( ArtifactNotFoundException e )
352         {
353             throw new MojoExecutionException( "artifact not found - " + e.getMessage(), e );
354         }
355         catch ( ArtifactResolutionException e )
356         {
357             throw new MojoExecutionException( "artifact resolver problem - " + e.getMessage(), e );
358         }
359         return artifact;
360     }
361 
362     /**
363      * @param path file to add to the project compile directories
364      */
365     protected void addCompileSourceRoot( File path )
366     {
367         getProject().addCompileSourceRoot( path.getAbsolutePath() );
368     }
369 
370     /**
371      * @return the project
372      */
373     public MavenProject getProject()
374     {
375         return project;
376     }
377 
378 
379     public ArtifactRepository getLocalRepository()
380     {
381         return this.localRepository;
382     }
383 
384     public List<ArtifactRepository> getRemoteRepositories()
385     {
386         return this.remoteRepositories;
387     }
388 
389     protected File setupGenerateDirectory() {
390         if ( !generateDirectory.exists() )
391         {
392             getLog().debug( "Creating target directory " + generateDirectory.getAbsolutePath() );
393             generateDirectory.mkdirs();
394         }
395         getLog().debug( "Add compile source root " + generateDirectory.getAbsolutePath() );
396         addCompileSourceRoot( generateDirectory );
397         return generateDirectory;
398     }
399 
400     public File getGenerateDirectory()
401     {
402         if ( !generateDirectory.exists() )
403         {
404             getLog().debug( "Creating target directory " + generateDirectory.getAbsolutePath() );
405             generateDirectory.mkdirs();
406         }
407         return generateDirectory;
408     }
409 
410     public Set<Artifact> getProjectArtifacts()
411     {
412         return project.getArtifacts();
413     }
414 
415     public Set<Artifact> getProjectRuntimeArtifacts()
416     {
417         Set<Artifact> artifacts = new HashSet<Artifact>();
418         for (Artifact projectArtifact : project.getArtifacts() )
419         {
420             String scope = projectArtifact.getScope();
421             if ( SCOPE_RUNTIME.equals( scope )
422               || SCOPE_COMPILE.equals( scope ) )
423             {
424                 artifacts.add( projectArtifact );
425             }
426 
427         }
428         return artifacts;
429     }
430 
431 
432 }