View Javadoc

1   /* ==========================================================================
2    * Copyright 2003-2004 Mevenide Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   * =========================================================================
16   */
17  package org.codehaus.mojo.nbm;
18  
19  import java.io.File;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Set;
23  import java.util.regex.Matcher;
24  import java.util.regex.Pattern;
25  import org.apache.maven.artifact.Artifact;
26  import org.apache.maven.artifact.factory.ArtifactFactory;
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
29  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
30  import org.apache.maven.artifact.resolver.ArtifactResolver;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.MojoFailureException;
33  import org.apache.maven.plugin.logging.Log;
34  import org.apache.maven.plugins.annotations.Component;
35  import org.apache.maven.plugins.annotations.LifecyclePhase;
36  import org.apache.maven.plugins.annotations.Mojo;
37  import org.apache.maven.plugins.annotations.Parameter;
38  import org.apache.maven.plugins.annotations.ResolutionScope;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.project.MavenProjectHelper;
41  import org.apache.tools.ant.BuildException;
42  import org.apache.tools.ant.Project;
43  import org.apache.tools.ant.taskdefs.Copy;
44  import org.apache.tools.ant.types.FileSet;
45  import org.codehaus.plexus.PlexusConstants;
46  import org.codehaus.plexus.PlexusContainer;
47  import org.codehaus.plexus.archiver.gzip.GZipArchiver;
48  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
49  import org.codehaus.plexus.context.Context;
50  import org.codehaus.plexus.context.ContextException;
51  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
52  import org.netbeans.nbbuild.MakeUpdateDesc;
53  
54  /**
55   * Create the NetBeans auto update site definition.
56   * @author <a href="mailto:mkleint@codehaus.org">Milos Kleint</a>
57   *
58   */
59  @Mojo(name="autoupdate", 
60          defaultPhase= LifecyclePhase.PACKAGE, 
61          aggregator=true, 
62          requiresDependencyResolution= ResolutionScope.RUNTIME )
63  public class CreateUpdateSiteMojo
64          extends AbstractNbmMojo
65          implements Contextualizable
66  {
67  
68      /**
69       * output directory.
70       */
71      @Parameter(required=true, defaultValue="${project.build.directory}")
72      protected File outputDirectory;
73      /**
74       * autoupdate site xml file name.
75       */
76      @Parameter( defaultValue="updates.xml", property="maven.nbm.updatesitexml")
77      protected String fileName;
78      /**
79       * A custom distribution base for the nbms in the update site.
80       * If NOT defined, the update site will use a simple relative URL, which is generally what you want.
81       * Defining it as "auto" will pick up the distribution URL from each NBM, which is generally wrong. 
82       * See <code>distributionUrl</code> in nbm mojo for what url will be used in that case.
83       * <p/>
84       * The value is either a direct http protocol based URL that points to
85       * the location under which all nbm files are located, or
86       * <p/>
87       * allows to create an update site based on maven repository content.
88       * The resulting autoupdate site document can be uploaded as tar.gz to repository as well
89       * as attached artifact to the 'nbm-application' project.
90       * <br/>
91       * Format: id::layout::url same as in maven-deploy-plugin
92       * <br/>
93       * with the 'default' and 'legacy' layouts. (maven2 vs maven1 layout)
94       * <br/>
95       * If the value doesn't contain :: characters,
96       * it's assumed to be the flat structure and the value is just the URL.
97       * 
98       * @since 3.0 it's also possible to add remote repository as base
99       */
100     @Parameter(defaultValue=".", property="maven.nbm.customDistBase")
101     private String distBase;
102 
103     /**
104      * The Maven Project.
105      *
106      */
107     @Parameter(required=true, readonly=true, property="project")
108     private MavenProject project;
109 
110     /**
111      * If the executed project is a reactor project, this will contains the full list of projects in the reactor.
112      */
113     @Parameter(required=true, readonly=true, defaultValue="${reactorProjects}")
114     private List reactorProjects;
115     
116     /**
117      * List of Ant style patterns on artifact GA (groupID:artifactID) that should be included in the update site.
118      * Eg. org.netbeans.* matches all artifacts with any groupID starting with 'org.netbeans.',
119      * org.*:api will match any artifact with artifactId of 'api' and groupId starting with 'org.'
120      * @since 3.14
121      */
122     @Parameter
123     private List<String> updateSiteIncludes;
124     
125 
126     // <editor-fold defaultstate="collapsed" desc="Component parameters">
127 
128     @Component
129     private ArtifactFactory artifactFactory;
130     /**
131      * Contextualized.
132      */
133     private PlexusContainer container;
134     /**
135      * Used for attaching the artifact in the project
136      *
137      */
138     @Component
139     private MavenProjectHelper projectHelper;
140 
141     @Component
142     private ArtifactResolver artifactResolver;
143 
144     /**
145      * Local maven repository.
146      *
147      */
148     @Parameter(readonly=true, required=true, defaultValue="${localRepository}")
149     protected ArtifactRepository localRepository;
150 
151     // </editor-fold>
152 
153     public void execute()
154         throws MojoExecutionException, MojoFailureException
155     {
156         Project antProject = registerNbmAntTasks();
157         File nbmBuildDirFile = new File( outputDirectory, "netbeans_site" );
158         if ( !nbmBuildDirFile.exists() )
159         {
160             nbmBuildDirFile.mkdirs();
161         }
162 
163         boolean isRepository = false;
164         if ( "auto".equals( distBase ) )
165         {
166             distBase = null;
167         }
168         ArtifactRepository distRepository = getDeploymentRepository( distBase, container, getLog() );
169         String oldDistBase = null;
170         if ( distRepository != null )
171         {
172             isRepository = true;
173         }
174         else
175         {
176             if ( distBase != null && !distBase.contains( "::" ) )
177             {
178                 oldDistBase = distBase;
179             }
180         }
181 
182         if ( "nbm-application".equals( project.getPackaging() ) )
183         {
184             @SuppressWarnings( "unchecked" )
185             Set<Artifact> artifacts = project.getArtifacts();
186             for ( Artifact art : artifacts )
187             {
188                 if (!matchesIncludes(art)) {
189                     continue;
190                 }
191                 ArtifactResult res =
192                     turnJarToNbmFile( art, artifactFactory, artifactResolver, project, localRepository );
193                 if ( res.hasConvertedArtifact() )
194                 {
195                     art = res.getConvertedArtifact();
196                 }
197 
198                 if ( art.getType().equals( "nbm-file" ) )
199                 {
200                     Copy copyTask = (Copy) antProject.createTask( "copy" );
201                     copyTask.setOverwrite( true );
202                     copyTask.setFile( art.getFile() );
203                     if ( !isRepository )
204                     {
205                         copyTask.setFlatten( true );
206                         copyTask.setTodir( nbmBuildDirFile );
207                     }
208                     else
209                     {
210                         String path = distRepository.pathOf( art );
211                         File f = new File( nbmBuildDirFile, path.replace( '/', File.separatorChar ) );
212                         copyTask.setTofile( f );
213                     }
214                     try
215                     {
216                         copyTask.execute();
217                     }
218                     catch ( BuildException ex )
219                     {
220                         throw new MojoExecutionException( "Cannot merge nbm files into autoupdate site", ex );
221                     }
222 
223                 }
224                 if ( res.isOSGiBundle() )
225                 {
226                     // TODO check for bundles
227                 }
228             }
229             getLog().info( "Created NetBeans module cluster(s) at " + nbmBuildDirFile.getAbsoluteFile() );
230 
231         }
232         else if ( reactorProjects != null && reactorProjects.size() > 0 )
233         {
234 
235             Iterator it = reactorProjects.iterator();
236             while ( it.hasNext() )
237             {
238                 MavenProject proj = (MavenProject) it.next();
239                 //TODO how to figure where the the buildDir/nbm directory is
240                 File moduleDir = proj.getFile().getParentFile();
241                 if ( moduleDir != null && moduleDir.exists() )
242                 {
243                     Copy copyTask = (Copy) antProject.createTask( "copy" );
244                     if ( !isRepository )
245                     {
246                         FileSet fs = new FileSet();
247                         fs.setDir( moduleDir );
248                         fs.createInclude().setName( "target/*.nbm" );
249                         copyTask.addFileset( fs );
250                         copyTask.setOverwrite( true );
251                         copyTask.setFlatten( true );
252                         copyTask.setTodir( nbmBuildDirFile );
253                     }
254                     else
255                     {
256                         File target = new File( moduleDir, "target" );
257                         boolean has = false;
258                         File[] fls = target.listFiles();
259                         if ( fls != null )
260                         {
261                             for ( File fl : fls )
262                             {
263                                 if ( fl.getName().endsWith( ".nbm" ) )
264                                 {
265                                     copyTask.setFile( fl );
266                                     has = true;
267                                     break;
268                                 }
269                             }
270                         }
271                         if ( !has )
272                         {
273                             continue;
274                         }
275                         Artifact art =
276                             artifactFactory.createArtifact( proj.getGroupId(), proj.getArtifactId(), proj.getVersion(),
277                                                             null, "nbm-file" );
278                         String path = distRepository.pathOf( art );
279                         File f = new File( nbmBuildDirFile, path.replace( '/', File.separatorChar ) );
280                         copyTask.setTofile( f );
281                     }
282                     try
283                     {
284                         copyTask.execute();
285                     }
286                     catch ( BuildException ex )
287                     {
288                         throw new MojoExecutionException( "Cannot merge nbm files into autoupdate site", ex );
289                     }
290                 }
291             }
292         }
293         else
294         {
295             throw new MojoExecutionException(
296                     "This goal only makes sense on reactor projects or project with 'nbm-application' packaging." );
297 
298         }
299         MakeUpdateDesc descTask = (MakeUpdateDesc) antProject.createTask( "updatedist" );
300         File xmlFile = new File( nbmBuildDirFile, fileName );
301         descTask.setDesc( xmlFile );
302         if ( oldDistBase != null )
303         {
304             descTask.setDistBase( oldDistBase );
305         }
306         if ( distRepository != null )
307         {
308             descTask.setDistBase( distRepository.getUrl() );
309         }
310         FileSet fs = new FileSet();
311         fs.setDir( nbmBuildDirFile );
312         fs.createInclude().setName( "**/*.nbm" );
313         descTask.addFileset( fs );
314         try
315         {
316             descTask.execute();
317         }
318         catch ( BuildException ex )
319         {
320             throw new MojoExecutionException( "Cannot create autoupdate site xml file", ex );
321         }
322         getLog().info( "Generated autoupdate site content at " + nbmBuildDirFile.getAbsolutePath() );
323 
324         try
325         {
326             GZipArchiver gz = new GZipArchiver();
327             gz.addFile( xmlFile, fileName );
328             File gzipped = new File( nbmBuildDirFile, fileName + ".gz" );
329             gz.setDestFile( gzipped );
330             gz.createArchive();
331             if ( "nbm-application".equals( project.getPackaging() ) )
332             {
333                 projectHelper.attachArtifact( project, "xml.gz", "updatesite", gzipped );
334             }
335         }
336         catch ( Exception ex )
337         {
338             throw new MojoExecutionException( "Cannot create gzipped version of the update site xml file.", ex );
339         }
340 
341     }
342 
343     private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile( "(.+)::(.+)::(.+)" );
344 
345     static ArtifactRepository getDeploymentRepository( String distBase, PlexusContainer container, Log log )
346         throws MojoExecutionException, MojoFailureException
347     {
348 
349         ArtifactRepository repo = null;
350 
351         if ( distBase != null )
352         {
353 
354             Matcher matcher = ALT_REPO_SYNTAX_PATTERN.matcher( distBase );
355 
356             if ( !matcher.matches() )
357             {
358                 if ( !distBase.contains( "::" ) )
359                 {
360                     //backward compatibility gag.
361                     return null;
362                 }
363                 throw new MojoFailureException( distBase,
364                         "Invalid syntax for repository.",
365                         "Invalid syntax for alternative repository. Use \"id::layout::url\"." );
366             }
367             else
368             {
369                 String id = matcher.group( 1 ).trim();
370                 String layout = matcher.group( 2 ).trim();
371                 String url = matcher.group( 3 ).trim();
372 
373                 ArtifactRepositoryLayout repoLayout;
374                 try
375                 {
376                     repoLayout = (ArtifactRepositoryLayout) container.lookup( ArtifactRepositoryLayout.ROLE, layout );
377                 }
378                 catch ( ComponentLookupException e )
379                 {
380                     throw new MojoExecutionException( "Cannot find repository layout: " + layout, e );
381                 }
382 
383                 repo = new DefaultArtifactRepository( id, url, repoLayout );
384             }
385         }
386         return repo;
387     }
388 
389     public void contextualize( Context context )
390         throws ContextException
391     {
392         this.container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
393     }
394 
395     private boolean matchesIncludes( Artifact art )
396     {
397         if (updateSiteIncludes != null) {
398             String s = art.getGroupId() + ":" + art.getArtifactId();
399             for (String p : updateSiteIncludes) {
400                 //TODO optimize and only do once per execution.
401                 p = p.replace(".", "\\.").replace( "*", ".*");
402                 Pattern patt = Pattern.compile( p );
403                 if (patt.matcher( s).matches()) {
404                     return true;
405                 }
406             }
407             return false;    
408         }
409         return true;
410     }
411 }