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.*;
20  import java.lang.reflect.Field;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Hashtable;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Properties;
30  import java.util.jar.Attributes;
31  import java.util.jar.JarEntry;
32  import java.util.jar.JarInputStream;
33  import java.util.jar.JarOutputStream;
34  import java.util.jar.Manifest;
35  import java.util.logging.Level;
36  import java.util.logging.Logger;
37  import org.apache.maven.artifact.Artifact;
38  import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
39  //import org.apache.maven.artifact.factory.ArtifactFactory;
40  import org.apache.maven.execution.MavenSession;
41  import org.apache.maven.model.Resource;
42  import org.apache.maven.plugin.MojoExecutionException;
43  import org.apache.maven.plugin.MojoFailureException;
44  import org.apache.maven.plugins.annotations.Component;
45  import org.apache.maven.plugins.annotations.Parameter;
46  import org.apache.maven.shared.filtering.MavenFilteringException;
47  import org.codehaus.mojo.nbm.model.NbmResource;
48  import org.apache.maven.project.MavenProject;
49  import org.apache.maven.shared.filtering.MavenResourcesExecution;
50  import org.apache.maven.shared.filtering.MavenResourcesFiltering;
51  import org.apache.tools.ant.BuildException;
52  import org.apache.tools.ant.Project;
53  import org.apache.tools.ant.taskdefs.Copy;
54  import org.apache.tools.ant.taskdefs.Jar;
55  import org.apache.tools.ant.types.FileSet;
56  import org.apache.tools.ant.types.Path;
57  import org.apache.tools.ant.types.PatternSet;
58  import org.apache.tools.ant.util.FileUtils;
59  import org.netbeans.nbbuild.CreateModuleXML;
60  import org.netbeans.nbbuild.MakeListOfNBM;
61  import org.codehaus.mojo.nbm.model.NetBeansModule;
62  import org.codehaus.mojo.nbm.utils.ExamineManifest;
63  import org.codehaus.plexus.util.ReaderFactory;
64  import org.codehaus.plexus.util.StringUtils;
65  import org.netbeans.nbbuild.JHIndexer;
66  
67  /**
68   * Create the NetBeans module directory structure, a prerequisite for nbm creation and cluster creation.
69   * <p/>
70   *
71   * @author <a href="mailto:mkleint@codehaus.org">Milos Kleint</a>
72   *
73   */
74  public abstract class CreateNetBeansFileStructure
75          extends AbstractNbmMojo
76  {
77  
78      /**
79       * NetBeans module assembly build directory.
80       * directory where the the NetBeans jar and nbm file get constructed.
81       */
82      @Parameter(defaultValue="${project.build.directory}/nbm", property="maven.nbm.buildDir")
83      protected File nbmBuildDir;
84      /**
85       * Build directory
86       */
87      @Parameter(required=true, readonly=true, property="project.build.directory")
88      protected File buildDir;
89      /**
90       * Name of the jar packaged by the jar:jar plugin
91       */
92      @Parameter(alias="jarname", property="project.build.finalName")
93      protected String finalName;
94      /**
95       * a NetBeans module descriptor containing dependency information and more..
96       * @deprecated all content from the module descriptor can be defined as plugin configuration now, will be removed in 4.0 entirely
97       */
98      @Parameter(defaultValue="${basedir}/src/main/nbm/module.xml")
99      protected File descriptor;
100     /**
101      * NetBeans module's cluster. Replaces the cluster element in module descriptor.
102      *
103      */
104     @Parameter(required=true, defaultValue="extra")
105     protected String cluster;
106     /**
107      * The location of JavaHelp sources for the project. The documentation
108      * itself is expected to be in the directory structure based on codenamebase of the module.
109      * eg. if your codenamebase is "org.netbeans.modules.apisupport", then the actual docs
110      * files shall go to ${basedir}/src/main/javahelp/org/netbeans/modules/apisupport/docs.
111      * @deprecated Obsolete as of NetBeans 7.0 with &#64;HelpSetRegistration.
112      * @since 2.7
113      */
114     @Deprecated
115     @Parameter(defaultValue="${basedir}/src/main/javahelp")
116     protected File nbmJavahelpSource;
117 
118     @Parameter(required=true, readonly=true, property="project")
119     protected MavenProject project;
120 
121     /**
122      * A list of additional resources to include in the NBM file.
123      * (Not in the module JAR; see <code>InstalledFileLocator</code> for retrieval.)
124      * Supersedes similarly-named configuration in the module descriptor file.
125      * <p>For example, to include native libraries:</p>
126      *
127      <pre>
128             &lt;nbmResource&gt;
129             &nbsp;&nbsp;&lt;directory&gt;src/main/libs&lt;/directory&gt;
130             &nbsp;&nbsp;&lt;targetPath&gt;modules/lib&lt;/targetPath&gt;
131             &nbsp;&nbsp;&lt;includes&gt;
132             &nbsp;&nbsp;&nbsp;&nbsp;&lt;include&gt;*.dll&lt;/include&gt;
133             &nbsp;&nbsp;&nbsp;&nbsp;&lt;include&gt;*.so&lt;/include&gt;
134             &nbsp;&nbsp;&lt;/includes&gt;
135             &lt;/nbmResource&gt;
136      </pre>
137      *
138      * @since 3.2
139      */
140     @Parameter
141     protected Resource[] nbmResources;
142 
143     /**
144      * The character encoding scheme to be applied when filtering nbm resources.
145      *
146      * @since 3.2
147      */
148     @Parameter(property="encoding", defaultValue="${project.build.sourceEncoding}")
149     
150     protected String encoding;
151     
152     /**
153      * Deployment type of the module, allowed values are <code>normal</code>,<code>eager</code>,<code>autoload</code>,
154      * <code>disabled</code>.
155      * <p>
156      * <code>autoload</code> - Such a module is
157      * automatically enabled when some other module requires it and
158      * automatically disabled otherwise.</p>
159      *                     <p><code>eager</code> - This module type gets
160      * automatically enabled when all it's dependencies are
161      * satisfied. Disabled otherwise.</p>
162      *                     <p><code>normal</code> - This is the default
163      * value. This kind of module is enabled/disabled manually by
164      * the user. It installs enabled.</p>
165      *                     <p><code>disabled</code> - This kind of module is enabled/disabled manually by
166      * the user. It installs disabled. Since 3.11</p>
167      *
168      * For details, see <a href="http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#enablement">Netbeans Module system docs</a>
169      *
170      * Since 3.14, for autoload and eager modules, we automatically set AutoUpdate-Show-In-Client manifest entry to false, if not defined already otherwise in the manifest.
171      * See issue <a href="http://jira.codehaus.org/browse/MNBMODULE-194">MNBMODULE-194</a>
172      *
173      * 
174      * @since 3.8
175      */ 
176     @Parameter(defaultValue="normal")
177     protected String moduleType;
178     
179     /**
180      * codename base of the module, uniquely identifying the module within the NetBeans runtime. usually the package name equivalent.
181      * Can include the major release version.
182      * See <a href="http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#how-manifest"> NetBeans Module system docs</a>
183      * @since 3.8
184      */
185     @Parameter(defaultValue="${project.groupId}.${project.artifactId}")
186     private String codeNameBase;
187     
188     /**
189      * list of groupId:artifactId pairs describing libraries that go into the nbm file and will only include the .external reference in the nbm
190      * instead of the actual binary. See <a href="http://netbeans.org/bugzilla/show_bug.cgi?id=195041">NetBeans issue #195041</a> for details.
191      * Please note that the scheme will only work for artifacts present in central repository but no effort is made at build time to enforce that.
192      * Additionally at runtime when installing the module, the user has to be online and be capable of reaching central using maven. 
193      * You have been warned.
194      * @since 3.8
195      */ 
196     @Parameter
197     private List<String> externals;
198 
199 
200     @Component
201     protected MavenResourcesFiltering mavenResourcesFiltering;
202 
203     @Parameter(property="session", readonly=true, required=true)
204     protected MavenSession session;
205 
206 
207     //items used by the CreateNBMMojo.
208     protected Project antProject;
209     protected NetBeansModule module;
210     protected File clusterDir;
211     protected String moduleJarName;
212 
213     public void execute()
214             throws MojoExecutionException, MojoFailureException
215     {
216         antProject = registerNbmAntTasks();
217         if ( descriptor != null && descriptor.exists() )
218         {
219             module = readModuleDescriptor( descriptor );
220         } else
221         {
222             module = createDefaultDescriptor( project, false );
223         }
224         //same moduleType related code in NetBeansManifestUpdateMojo.java
225         String type = moduleType;
226         if ("normal".equals(type) && module.getModuleType() != null) {
227             type = module.getModuleType();
228             getLog().warn( "moduleType in module descriptor is deprecated, use the plugin's parameter moduleType");
229         }
230         if (!"normal".equals(type) && !"autoload".equals(type) && !"eager".equals(type) && !"disabled".equals(type)) {
231             getLog().error( "Only 'normal,autoload,eager,disabled' are allowed values in the moduleType parameter");
232         }
233         boolean autoload = "autoload".equals( type );
234         boolean eager = "eager".equals( type );
235         boolean disabled = "disabled".equals( type );
236         // 1. initialization
237         String moduleName = codeNameBase;
238         if (module.getCodeNameBase() != null) {
239             moduleName = module.getCodeNameBase();
240             getLog().warn( "codeNameBase in module descriptor is deprecated, use the plugin's parameter codeNameBase");
241         }
242         moduleName = NetBeansManifestUpdateMojo.stripVersionFromCodebaseName( moduleName.replaceAll( "-", "." ) );
243         moduleJarName = moduleName.replace( '.', '-' );
244         if ( "extra".equals( cluster ) && module.getCluster() != null )
245         {
246             getLog().warn(
247                     "Parameter cluster in module descriptor is deprecated, use the plugin configuration element." );
248             cluster = module.getCluster();
249         }
250         File jarFile = new File( buildDir, finalName + ".jar" );
251         clusterDir = new File( nbmBuildDir, "netbeans" + File.separator + cluster );
252         File moduleJarLocation = new File( clusterDir, "modules" );
253         moduleJarLocation.mkdirs();
254 
255         //2. create nbm resources
256         File moduleFile = new File( moduleJarLocation, moduleJarName + ".jar" );
257 
258         try
259         {
260             boolean needPlainCopy = false;
261             InputStream is = new FileInputStream( jarFile );
262             try
263             {
264                 JarInputStream jis = new JarInputStream( is );
265                 Manifest m = jis.getManifest();
266                 Attributes a = m.getMainAttributes();
267                 String classPath = ( String ) a.remove( new Attributes.Name( "X-Class-Path" ) );
268                 if ( classPath == null )
269                 {
270                     needPlainCopy = true;
271                 }
272                 else // MNBMODULE-133
273                 {
274                     getLog().info( "Copying module JAR to " + moduleJarLocation + " with manifest updates" );
275                     a.putValue( "Class-Path", classPath );
276                     a.remove( new Attributes.Name( "Maven-Class-Path" ) );
277                     OutputStream os = new FileOutputStream( moduleFile );
278                     try
279                     {
280                         JarOutputStream jos = new JarOutputStream( os, m );
281                         JarEntry entry;
282                         while ( ( entry = jis.getNextJarEntry() ) != null )
283                         {
284                             JarEntry entry2 = new JarEntry( entry );
285                             jos.putNextEntry( entry2 );
286                             int c;
287                             while ( ( c = jis.read() ) != -1 )
288                             {
289                                 jos.write( c );
290                             }
291                             jos.closeEntry();
292                         }
293                         jos.finish();
294                         jos.close();
295                     }
296                     finally
297                     {
298                         os.close();
299                     }
300                 }
301             }
302             finally
303             {
304                 is.close();
305             }
306             if ( needPlainCopy )
307             {
308                 getLog().info( "Copying module JAR to " + moduleJarLocation );
309                 FileUtils.getFileUtils().copyFile( jarFile, moduleFile, null, true, false );
310             }
311         }
312         catch ( IOException x )
313         {
314             throw new MojoExecutionException( "Cannot copy module jar", x );
315         }
316 
317         ExamineManifest modExaminator = new ExamineManifest( getLog() );
318         modExaminator.setJarFile( moduleFile );
319         modExaminator.checkFile();
320         String classpathValue = modExaminator.getClasspath();
321 
322         if ( module != null )
323         {
324             // copy libraries to the designated place..
325             @SuppressWarnings("unchecked")
326             List<Artifact> artifacts = project.getRuntimeArtifacts();
327             for ( Artifact artifact : artifacts )
328             {
329                 File source = artifact.getFile();
330                 
331                 String path = NetBeansManifestUpdateMojo.artifactToClassPathEntry( artifact, codeNameBase );
332                 
333                 if ( classpathValue.contains( path ) )
334                 {
335                     File target = new File( moduleJarLocation, path );
336 
337                     File targetDir = target.getParentFile();
338                     targetDir.mkdirs();
339 
340                     try
341                     {
342                         FileUtils.getFileUtils().copyFile( source, target, null, true, false );
343                         if ( externals != null && externals.contains(artifact.getGroupId() + ":" + artifact.getArtifactId())) // MNBMODULE-138
344                         {
345                             String name = target.getName();
346                             getLog().info( "Using *.external replacement for " + name );
347                             PrintWriter external = new PrintWriter( new File( targetDir, name + ".external" ), "UTF-8" );
348                             try
349                             {
350                                 writeExternal( external, artifact );
351                             }
352                             finally
353                             {
354                                 external.close();
355                             }
356                         }
357                     }
358                     catch ( IOException ex )
359                     {
360                         getLog().error( "Cannot copy library jar" );
361                         throw new MojoExecutionException( "Cannot copy library jar", ex );
362                     }
363                 }
364             }
365             if ( nbmResources != null )
366             {
367                 copyNbmResources();
368             }
369             copyDeprecatedNbmResources();
370         }
371 
372         //javahelp stuff.
373         if ( nbmJavahelpSource.exists() )
374         {
375             getLog().warn( "src/main/javahelp/ deprecated; use @HelpSetRegistration instead" );
376             File javahelp_target = new File( buildDir, "javahelp" );
377             String javahelpbase = moduleJarName.replace( '-', File.separatorChar ) + File.separator + "docs";
378             String javahelpSearch = "JavaHelpSearch";
379             File b = new File( javahelp_target, javahelpbase );
380             File p = new File( b, javahelpSearch );
381             p.mkdirs();
382             Copy cp = (Copy) antProject.createTask( "copy" );
383             cp.setTodir( javahelp_target );
384             FileSet set = new FileSet();
385             set.setDir( nbmJavahelpSource );
386             cp.addFileset( set );
387             cp.execute();
388             getLog().info( "Generating JavaHelp Index..." );
389 
390             JHIndexer jhTask = (JHIndexer) antProject.createTask( "jhindexer" );
391             jhTask.setBasedir( b );
392             jhTask.setDb( p );
393             jhTask.setIncludes( "**/*.html" );
394             jhTask.setExcludes( javahelpSearch );
395             Path path = new Path( antProject );
396             jhTask.setClassPath( path );
397             clearStaticFieldsInJavaHelpIndexer();
398             try
399             {
400                 jhTask.execute();
401             }
402             catch ( BuildException e )
403             {
404                 getLog().error( "Cannot generate JavaHelp index." );
405                 throw new MojoExecutionException( e.getMessage(), e );
406             }
407             File helpJarLocation = new File( clusterDir, "modules/docs" );
408             helpJarLocation.mkdirs();
409             Jar jar = (Jar) antProject.createTask( "jar" );
410             jar.setDestFile( new File( helpJarLocation, moduleJarName + ".jar" ) );
411             set = new FileSet();
412             set.setDir( javahelp_target );
413             jar.addFileset( set );
414             jar.execute();
415         }
416 
417         File configDir = new File( clusterDir, "config" + File.separator + "Modules" );
418         configDir.mkdirs();
419         CreateModuleXML moduleXmlTask = (CreateModuleXML) antProject.createTask( "createmodulexml" );
420         moduleXmlTask.setXmldir( configDir );
421         FileSet fs = new FileSet();
422         fs.setDir( clusterDir );
423         fs.setIncludes( "modules" + File.separator + moduleJarName + ".jar" );
424         if ( autoload )
425         {
426             moduleXmlTask.addAutoload( fs );
427         }
428         else if ( eager )
429         {
430             moduleXmlTask.addEager( fs );
431         }
432         else if ( disabled )
433         {
434             moduleXmlTask.addDisabled( fs );
435         }
436         else
437         {
438             moduleXmlTask.addEnabled( fs );
439         }
440         try
441         {
442             moduleXmlTask.execute();
443         }
444         catch ( BuildException e )
445         {
446             getLog().error( "Cannot generate config file." );
447             throw new MojoExecutionException( e.getMessage(), e );
448         }
449         MakeListOfNBM makeTask = (MakeListOfNBM) antProject.createTask( "genlist" );
450         antProject.setNewProperty( "module.name", finalName );
451         antProject.setProperty( "cluster.dir", cluster );
452         FileSet set = makeTask.createFileSet();
453         set.setDir( clusterDir );
454         PatternSet pattern = set.createPatternSet();
455         pattern.setIncludes( "**" );
456         makeTask.setModule( "modules" + File.separator + moduleJarName + ".jar" );
457         makeTask.setOutputfiledir( clusterDir );
458         try
459         {
460             makeTask.execute();
461         }
462         catch ( BuildException e )
463         {
464             getLog().error( "Cannot Generate nbm list" );
465             throw new MojoExecutionException( e.getMessage(), e );
466         }
467 
468     }
469 
470     private void copyDeprecatedNbmResources()
471         throws BuildException, MojoExecutionException
472     {
473         // copy additional resources..
474         List<NbmResource> ress = module.getNbmResources();
475         if ( ress.size() > 0 )
476         {
477             getLog().warn( "NBM resources defined in module descriptor are deprecated. Please configure NBM resources in plugin configuration." );
478             Copy cp = (Copy) antProject.createTask( "copy" );
479             cp.setTodir( clusterDir );
480             HashMap<File, Collection<FileSet>> customPaths = new HashMap<File, Collection<FileSet>>();
481             boolean hasStandard = false;
482             for ( NbmResource res : ress )
483             {
484                 if ( res.getBaseDirectory() != null )
485                 {
486                     File base = new File( project.getBasedir(), res.getBaseDirectory() );
487                     FileSet set = new FileSet();
488                     set.setDir( base );
489                     for ( String inc : res.getIncludes() )
490                     {
491                         set.createInclude().setName( inc );
492                     }
493                     for ( String exc : res.getExcludes() )
494                     {
495                         set.createExclude().setName( exc );
496                     }
497 
498                     if ( res.getRelativeClusterPath() != null )
499                     {
500                         File path = new File( clusterDir, res.getRelativeClusterPath() );
501                         Collection<FileSet> col = customPaths.get( path );
502                         if ( col == null )
503                         {
504                             col = new ArrayList<FileSet>();
505                             customPaths.put( path, col );
506                         }
507                         col.add( set );
508                     }
509                     else
510                     {
511                         cp.addFileset( set );
512                         hasStandard = true;
513                     }
514                 }
515             }
516             try
517             {
518                 if ( hasStandard )
519                 {
520                     cp.execute();
521                 }
522                 if ( customPaths.size() > 0 )
523                 {
524                     for ( Map.Entry<File, Collection<FileSet>> ent : customPaths.entrySet() )
525                     {
526                         cp = (Copy) antProject.createTask( "copy" );
527                         cp.setTodir( ent.getKey() );
528                         for ( FileSet set : ent.getValue() )
529                         {
530                             cp.addFileset( set );
531                         }
532                         cp.execute();
533                     }
534                 }
535             }
536             catch ( BuildException e )
537             {
538                 getLog().error( "Cannot copy additional resources into the nbm file" );
539                 throw new MojoExecutionException( e.getMessage(), e );
540             }
541         }
542     }
543 
544     // repeated invokation of the javahelp indexer (possibly via multiple classloaders)
545     // is causing trouble, residue from previous invokations seems to cause errors
546     // this is a nasty workaround for the problem.
547     // alternatively we could try invoking the indexer from a separate jvm i guess,
548     // ut that's more work.
549     private void clearStaticFieldsInJavaHelpIndexer() // MNBMODULE-51 hack
550     {
551         try
552         {
553             Class clazz = Class.forName( "com.sun.java.help.search.Indexer" );
554             Field fld = clazz.getDeclaredField( "kitRegistry" );
555             fld.setAccessible( true );
556             Hashtable hash = (Hashtable) fld.get( null );
557             hash.clear();
558 
559             clazz = Class.forName( "com.sun.java.help.search.HTMLIndexerKit" );
560             fld = clazz.getDeclaredField( "defaultParser" );
561             fld.setAccessible( true );
562             fld.set( null, null);
563 
564             fld = clazz.getDeclaredField( "defaultCallback" );
565             fld.setAccessible( true );
566             fld.set( null, null);
567 
568         }
569         catch ( IllegalArgumentException ex )
570         {
571             Logger.getLogger( CreateNetBeansFileStructure.class.getName() ).log( Level.SEVERE, null, ex );
572         }
573         catch ( IllegalAccessException ex )
574         {
575             Logger.getLogger( CreateNetBeansFileStructure.class.getName() ).log( Level.SEVERE, null, ex );
576         }
577         catch ( NoSuchFieldException ex )
578         {
579             Logger.getLogger( CreateNetBeansFileStructure.class.getName() ).log( Level.SEVERE, null, ex );
580         }
581         catch ( SecurityException ex )
582         {
583             Logger.getLogger( CreateNetBeansFileStructure.class.getName() ).log( Level.SEVERE, null, ex );
584         }
585         catch ( ClassNotFoundException ex )
586         {
587             Logger.getLogger( CreateNetBeansFileStructure.class.getName() ).log( Level.SEVERE, null, ex );
588         }
589     }
590 
591     private void copyNbmResources()
592         throws MojoExecutionException
593     {
594         try
595         {
596             if ( StringUtils.isEmpty( encoding ) && isFilteringEnabled( nbmResources ) )
597             {
598                 getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
599                                    + ", i.e. build is platform dependent!" );
600             }
601             MavenResourcesExecution mavenResourcesExecution =
602                 new MavenResourcesExecution( Arrays.asList( nbmResources ), clusterDir, project, encoding,
603                                              Collections.EMPTY_LIST, Collections.EMPTY_LIST, session );
604             mavenResourcesExecution.setEscapeWindowsPaths( true );
605             mavenResourcesFiltering.filterResources( mavenResourcesExecution );
606         }
607         catch ( MavenFilteringException ex )
608         {
609             throw new MojoExecutionException( ex.getMessage(), ex );
610         }
611     }
612 
613     /**
614      * Determines whether filtering has been enabled for any resource.
615      *
616      * @param resources The set of resources to check for filtering.
617      * @return <code>true</code> if at least one resource uses filtering, <code>false</code> otherwise.
618      */
619     private boolean isFilteringEnabled( Resource[] resources )
620     {
621         for ( Resource resource : resources )
622         {
623             if ( resource.isFiltering() )
624             {
625                 return true;
626             }
627         }
628         return false;
629     }
630 
631     static void writeExternal( PrintWriter w, Artifact artifact )
632         throws IOException
633     {
634         w.write( "CRC:" );
635         File file = artifact.getFile();
636         w.write( Long.toString( CreateClusterAppMojo.crcForFile( file ).getValue() ) );
637         w.write( "\nSIZE:" );
638         w.write( Long.toString( file.length() ) );
639         w.write( "\nURL:m2:/" );
640         w.write( artifact.getGroupId() );
641         w.write( ':' );
642         w.write( artifact.getArtifactId() );
643         w.write( ':' );
644         w.write( artifact.getVersion() );
645         w.write( ':' );
646         w.write( artifact.getType() );
647         if ( artifact.getClassifier() != null )
648         {
649             w.write( ':' );
650             w.write( artifact.getClassifier() );
651         }
652         w.write( "\nURL:" );
653         // artifact.repository is null, so cannot use its url, and anyway might be a mirror
654         w.write( /* M3: RepositorySystem.DEFAULT_REMOTE_REPO_URL + '/' */ "http://repo.maven.apache.org/maven2/" target="alexandria_uri">http://repo.maven.apache.org/maven2/" );
655         w.write( new DefaultRepositoryLayout().pathOf( artifact ) );
656         w.write( '\n' );
657         w.flush();
658     }
659 
660 }