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.io.FileReader;
21  import java.io.IOException;
22  import java.io.Reader;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  import java.util.Map;
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.factory.ArtifactFactory;
29  import org.apache.maven.artifact.repository.ArtifactRepository;
30  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
31  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
32  import org.apache.maven.artifact.resolver.ArtifactResolver;
33  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
34  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugin.logging.Log;
37  import org.apache.maven.project.MavenProject;
38  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
39  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
40  import org.apache.maven.shared.dependency.graph.DependencyNode;
41  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
42  import org.codehaus.mojo.nbm.model.Dependency;
43  import org.codehaus.mojo.nbm.model.NetBeansModule;
44  import org.codehaus.mojo.nbm.model.io.xpp3.NetBeansModuleXpp3Reader;
45  import org.codehaus.mojo.nbm.utils.ExamineManifest;
46  import org.codehaus.plexus.util.IOUtil;
47  
48  public abstract class AbstractNbmMojo
49      extends org.codehaus.mojo.nbm.utils.AbstractNetbeansMojo
50  {
51  
52      static boolean matchesLibrary( Artifact artifact, List<String> libraries, ExamineManifest depExaminator,
53          Log log, boolean useOsgiDependencies )
54      {
55          String artId = artifact.getArtifactId();
56          String grId = artifact.getGroupId();
57          String id = grId + ":" + artId;
58          boolean explicit = libraries.remove( id );
59          if ( explicit )
60          {
61              log.debug(
62                  id + " included as module library, explicitly declared in module descriptor." );
63              return explicit;
64          }
65          if ( Artifact.SCOPE_PROVIDED.equals( artifact.getScope() ) || Artifact.SCOPE_SYSTEM.equals(
66              artifact.getScope() ) )
67          {
68              log.debug(
69                  id + " omitted as module library, has scope 'provided/system'" );
70              return false;
71          }
72          if ( "nbm".equals( artifact.getType() ) )
73          {
74              return false;
75          }
76          if ( depExaminator.isNetBeansModule() || ( useOsgiDependencies && depExaminator.isOsgiBundle() ) )
77          {
78              //TODO I can see how someone might want to include an osgi bundle as library, not dependency.
79              // I guess it won't matter much in 6.9+, in older versions it could be a problem.
80              return false;
81          }
82          log.debug(
83              id + " included as module library, squeezed through all the filters." );
84          return true;
85      }
86  
87      static Dependency resolveNetBeansDependency( Artifact artifact, List<Dependency> deps,
88          ExamineManifest manifest, Log log )
89      {
90          String artId = artifact.getArtifactId();
91          String grId = artifact.getGroupId();
92          String id = grId + ":" + artId;
93          for ( Dependency dep : deps )
94          {
95              if ( id.equals( dep.getId() ) )
96              {
97                  if ( manifest.isNetBeansModule() )
98                  {
99                      return dep;
100                 }
101                 else
102                 {
103                     if ( dep.getExplicitValue() != null )
104                     {
105                         return dep;
106                     }
107                     log.warn(
108                         id + " declared as module dependency in descriptor, but not a NetBeans module" );
109                     return null;
110                 }
111             }
112         }
113         if ( "nbm".equals( artifact.getType() ) )
114         {
115             Dependency dep = new Dependency();
116             dep.setId( id );
117             dep.setType( "spec" );
118             log.debug( "Adding nbm module dependency - " + id );
119             return dep;
120         }
121         if ( manifest.isNetBeansModule() )
122         {
123             Dependency dep = new Dependency();
124             dep.setId( id );
125             dep.setType( "spec" );
126             log.debug( "Adding direct NetBeans module dependency - " + id );
127             return dep;
128         }
129         return null;
130     }
131 
132     protected final NetBeansModule readModuleDescriptor( File descriptor )
133         throws MojoExecutionException
134     {
135         if ( descriptor == null )
136         {
137             throw new MojoExecutionException(
138                 "The module descriptor has to be configured." );
139         }
140         if ( !descriptor.exists() )
141         {
142             throw new MojoExecutionException(
143                 "The module descriptor is missing: '" + descriptor + "'." );
144         }
145         Reader r = null;
146         try
147         {
148             r = new FileReader( descriptor );
149             NetBeansModuleXpp3Reader reader = new NetBeansModuleXpp3Reader();
150             NetBeansModule module = reader.read( r );
151             return module;
152         }
153         catch ( IOException exc )
154         {
155             throw new MojoExecutionException(
156                 "Error while reading module descriptor '" + descriptor + "'.",
157                 exc );
158         }
159         catch ( XmlPullParserException xml )
160         {
161             throw new MojoExecutionException(
162                 "Error while reading module descriptor '" + descriptor + "'.",
163                 xml );
164         }
165         finally
166         {
167             IOUtil.close( r );
168         }
169     }
170 
171     protected final NetBeansModule createDefaultDescriptor( MavenProject project, boolean log )
172     {
173 
174         if ( log )
175         {
176             getLog().info(
177                 "No Module Descriptor defined, trying to fallback to generated values:" );
178         }
179         NetBeansModule module = new NetBeansModule();
180         return module;
181     }
182 
183     static List<Artifact> getLibraryArtifacts( DependencyNode treeRoot, NetBeansModule module,
184                                                List<Artifact> runtimeArtifacts,
185                                                Map<Artifact, ExamineManifest> examinerCache, Log log,
186                                                boolean useOsgiDependencies )
187         throws MojoExecutionException
188     {
189         List<Artifact> include = new ArrayList<Artifact>();
190         if ( module != null )
191         {
192             List<String> librList = new ArrayList<String>();
193             if ( module.getLibraries() != null )
194             {
195                 librList.addAll( module.getLibraries() );
196             }
197             CollectLibrariesNodeVisitor visitor = new CollectLibrariesNodeVisitor( librList,
198                 runtimeArtifacts, examinerCache, log, treeRoot, useOsgiDependencies );
199             treeRoot.accept( visitor );
200             include.addAll( visitor.getArtifacts() );
201         }
202         return include;
203     }
204 
205     static List<ModuleWrapper> getModuleDependencyArtifacts( DependencyNode treeRoot, NetBeansModule module,
206                                                              Dependency[] customDependencies, MavenProject project,
207                                                              Map<Artifact, ExamineManifest> examinerCache,
208                                                              List<Artifact> libraryArtifacts, Log log,
209                                                              boolean useOsgiDependencies )
210         throws MojoExecutionException
211     {
212         List<Dependency> deps = new ArrayList<Dependency>();
213         if (customDependencies != null) {
214             deps.addAll( Arrays.asList( customDependencies ));
215         }
216         if (module != null && !module.getDependencies().isEmpty()) {
217             log.warn( "dependencies in module descriptor are deprecated, use the plugin's parameter moduleDependencies");
218             //we need to make sure a dependency is not twice there, module deps override the config (as is the case with other
219             //configurations)
220             for (Dependency d : module.getDependencies()) {
221                 Dependency found = null;
222                 for (Dependency d2 : deps) {
223                     if (d2.getId().equals(d.getId())) {
224                         found = d2;
225                         break;
226                     }
227                 }
228                 if (found != null) {
229                     deps.remove( found );
230                 }
231                 deps.add(d);
232             }
233         }
234         List<ModuleWrapper> include = new ArrayList<ModuleWrapper>();
235         
236             @SuppressWarnings( "unchecked" )
237             List<Artifact> artifacts = project.getCompileArtifacts();
238             for ( Artifact artifact : artifacts )
239             {
240                 if ( libraryArtifacts.contains( artifact ) )
241                 {
242                     continue;
243                 }
244                 ExamineManifest depExaminator = examinerCache.get( artifact );
245                 if ( depExaminator == null )
246                 {
247                     depExaminator = new ExamineManifest( log );
248                     depExaminator.setArtifactFile( artifact.getFile() );
249                     depExaminator.checkFile();
250                     examinerCache.put( artifact, depExaminator );
251                 }
252                 Dependency dep = resolveNetBeansDependency( artifact, deps, depExaminator, log );
253                 if ( dep != null )
254                 {
255                     ModuleWrapper wr = new ModuleWrapper();
256                     wr.dependency = dep;
257                     wr.artifact = artifact;
258                     wr.transitive = false;
259                     //only direct deps matter to us..
260                     if ( depExaminator.isNetBeansModule() && artifact.getDependencyTrail().size() > 2 )
261                     {
262                         log.debug(
263                             artifact.getId() + " omitted as NetBeans module dependency, not a direct one. Declare it in the pom for inclusion." );
264                         wr.transitive = true;
265 
266                     }
267                     include.add( wr );
268                 }
269                 else
270                 {
271                     if ( useOsgiDependencies && depExaminator.isOsgiBundle() )
272                     {
273                         ModuleWrapper wr = new ModuleWrapper();
274                         wr.osgi = true;
275                         String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
276                         for ( Dependency depe : deps )
277                         {
278                             if ( id.equals( depe.getId() ) )
279                             {
280                                 wr.dependency = depe;
281                             }
282                         }
283                         boolean print = false;
284                         if ( wr.dependency == null )
285                         {
286                             Dependency depe = new Dependency();
287                             depe.setId( id );
288                             depe.setType( "spec" );
289                             wr.dependency = depe;
290                             print = true;
291                         }
292 
293                         wr.artifact = artifact;
294                         wr.transitive = false;
295                         //only direct deps matter to us..
296                         if ( artifact.getDependencyTrail().size() > 2 )
297                         {
298                             log.debug(
299                                 artifact.getId() + " omitted as NetBeans module OSGi dependency, not a direct one. Declare it in the pom for inclusion." );
300                             wr.transitive = true;
301 
302                         }
303                         else
304                         {
305                             if ( print )
306                             {
307                                 log.info( "Adding OSGi bundle dependency - " + id );
308                             }
309                         }
310 
311                         include.add( wr );
312                     }
313                 }
314             }
315         return include;
316     }
317 
318     static class ModuleWrapper
319     {
320 
321         Dependency dependency;
322 
323         Artifact artifact;
324 
325         boolean transitive = true;
326         
327         boolean osgi = false;
328 
329     }
330 
331     //copied from dependency:tree mojo
332     protected DependencyNode createDependencyTree( MavenProject project, DependencyGraphBuilder dependencyGraphBuilder,
333                                                    String scope )
334         throws MojoExecutionException
335     {
336         ArtifactFilter artifactFilter = createResolvingArtifactFilter( scope );
337         try
338         {
339             return dependencyGraphBuilder.buildDependencyGraph( project, artifactFilter );
340         }
341         catch ( DependencyGraphBuilderException exception )
342         {
343             throw new MojoExecutionException( "Cannot build project dependency tree", exception );
344         }
345 
346     }
347 
348     //copied from dependency:tree mojo
349     /**
350      * Gets the artifact filter to use when resolving the dependency tree.
351      *
352      * @return the artifact filter
353      */
354     private ArtifactFilter createResolvingArtifactFilter( String scope )
355     {
356         ArtifactFilter filter;
357 
358         // filter scope
359         if ( scope != null )
360         {
361             getLog().debug( "+ Resolving dependency tree for scope '" + scope + "'" );
362 
363             filter = new ScopeArtifactFilter( scope );
364         }
365         else
366         {
367             filter = null;
368         }
369 
370         return filter;
371     }
372 
373     protected final ArtifactResult turnJarToNbmFile( Artifact art, ArtifactFactory artifactFactory,
374                                                      ArtifactResolver artifactResolver, MavenProject project,
375                                                      ArtifactRepository localRepository )
376         throws MojoExecutionException
377     {
378         if ( "jar".equals( art.getType() ) || "nbm".equals( art.getType() ) )
379         {
380             //TODO, it would be nice to have a check to see if the
381             // "to-be-created" module nbm artifact is actually already in the
382             // list of dependencies (as "nbm-file") or not..
383             // that would be a timesaver
384             ExamineManifest mnf = new ExamineManifest( getLog() );
385             File jar = art.getFile();
386             if ( !jar.isFile() )
387             {
388                 //MNBMODULE-210 with recent CoS changes in netbeans (7.4) jar will be file as we link open projects in the build
389                 // via WorkspaceReader. That's fine here, as all we need is to know if project is osgi or nbm module.
390                 // the nbm file has to be in local repository though.
391                 String path = localRepository.pathOf( art );
392                 File jar2 = new File(localRepository.getBasedir(), path.replace( "/", File.separator));
393                 File manifest = new File(jar, "META-INF/MANIFEST.MF" );
394                 
395                 if (! jar2.isFile() || !manifest.isFile() ) {
396                     getLog().warn( "MNBMODULE-131: need to at least run install phase on " + jar2 );
397                     return new ArtifactResult( null, null );
398                 }
399                 mnf.setManifestFile( manifest );
400             } else {
401                 mnf.setJarFile( jar );
402             }
403             mnf.checkFile();
404             if ( mnf.isNetBeansModule() )
405             {
406                 Artifact nbmArt = artifactFactory.createDependencyArtifact(
407                     art.getGroupId(),
408                     art.getArtifactId(),
409                     art.getVersionRange(),
410                     "nbm-file",
411                     art.getClassifier(),
412                     art.getScope() );
413                 try
414                 {
415                     artifactResolver.resolve( nbmArt, project.getRemoteArtifactRepositories(), localRepository );
416                 }
417 
418                 catch ( ArtifactResolutionException ex )
419                 {
420                     //shall be check before actually resolving from repos?
421                     checkReactor( art, nbmArt );
422                     if ( !nbmArt.isResolved() )
423                     {
424                         throw new MojoExecutionException( "Failed to retrieve the nbm file from repository", ex );
425                     }
426                 }
427                 catch ( ArtifactNotFoundException ex )
428                 {
429                     //shall be check before actually resolving from repos?
430                     checkReactor( art, nbmArt );
431                     if ( !nbmArt.isResolved() )
432                     {
433                         throw new MojoExecutionException( "Failed to retrieve the nbm file from repository", ex );
434                     }
435                 }
436                 return new ArtifactResult( nbmArt, mnf );
437             }
438             if ( mnf.isOsgiBundle() )
439             {
440                 return new ArtifactResult( null, mnf );
441             }
442         }
443         return new ArtifactResult( null, null );
444     }
445 
446     protected static final class ArtifactResult
447     {
448         private final Artifact converted;
449         private final ExamineManifest manifest;
450 
451         ArtifactResult( Artifact conv, ExamineManifest manifest )
452         {
453             converted = conv;
454             this.manifest = manifest;
455         }
456 
457         boolean hasConvertedArtifact()
458         {
459             return converted != null;
460         }
461 
462         Artifact getConvertedArtifact()
463         {
464             return converted;
465         }
466 
467         public boolean isOSGiBundle()
468         {
469             return manifest != null && manifest.isOsgiBundle();
470         }
471 
472         public ExamineManifest getExaminedManifest()
473         {
474             return manifest;
475         }
476     }
477 
478     private void checkReactor( Artifact art, Artifact nbmArt )
479     {
480         if ( art.getFile().getName().endsWith( ".jar" ) )
481         {
482             String name = art.getFile().getName();
483             name = name.substring( 0, name.length() - ".jar".length() ) + ".nbm";
484             File fl = new File( art.getFile().getParentFile(), name );
485             if ( fl.exists() )
486             {
487                 nbmArt.setFile( fl );
488                 nbmArt.setResolved( true );
489             }
490         }
491     }
492 
493 }