]> source.dussan.org Git - archiva.git/blob
6102c333d0f60f38039d38f0182c3a26e90cb1de
[archiva.git] /
1 package org.apache.archiva.consumers.dependencytree;
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 java.io.File;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.lang.reflect.Field;
26 import java.net.MalformedURLException;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
30
31 import org.apache.commons.io.IOUtils;
32 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
33 import org.apache.maven.archiva.consumers.AbstractMonitoredConsumer;
34 import org.apache.maven.archiva.consumers.ConsumerException;
35 import org.apache.maven.archiva.consumers.KnownRepositoryContentConsumer;
36 import org.apache.maven.artifact.Artifact;
37 import org.apache.maven.artifact.factory.ArtifactFactory;
38 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
39 import org.apache.maven.artifact.repository.ArtifactRepository;
40 import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
41 import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
42 import org.apache.maven.artifact.resolver.ArtifactCollector;
43 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
44 import org.apache.maven.project.DefaultMavenProjectBuilder;
45 import org.apache.maven.project.MavenProject;
46 import org.apache.maven.project.MavenProjectBuilder;
47 import org.apache.maven.project.ProjectBuildingException;
48 import org.apache.maven.shared.dependency.tree.DependencyNode;
49 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
50 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
51 import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
52 import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
53 import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
54 import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
55 import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
56 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
57 import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
58 import org.dom4j.Document;
59 import org.dom4j.DocumentHelper;
60 import org.dom4j.Element;
61 import org.dom4j.io.OutputFormat;
62 import org.dom4j.io.XMLWriter;
63
64 /**
65  * @plexus.component role="org.apache.maven.archiva.consumers.KnownRepositoryContentConsumer"
66  *                   role-hint="dependency-tree-generator" instantiation-strategy="per-lookup"
67  */
68 public class DependencyTreeGeneratorConsumer
69     extends AbstractMonitoredConsumer
70     implements KnownRepositoryContentConsumer
71 {
72     /** @plexus.configuration */
73     private File generatedRepositoryLocation;
74
75     /** @plexus.configuration */
76     private File localRepository;
77
78     /** @plexus.requirement */
79     private DependencyTreeBuilder dependencyTreeBuilder;
80
81     /** @plexus.requirement */
82     private ArtifactFactory artifactFactory;
83
84     /** @plexus.requirement role-hint="maven" */
85     private ArtifactMetadataSource artifactMetadataSource;
86
87     /** @plexus.requirement */
88     private ArtifactCollector artifactCollector;
89
90     /** @plexus.requirement */
91     private MavenProjectBuilder projectBuilder;
92
93     /** @plexus.requirement */
94     private ArtifactRepositoryFactory artifactRepositoryFactory;
95
96     private String repositoryLocation;
97
98     private final DefaultRepositoryLayout layout = new DefaultRepositoryLayout();
99
100     private ArtifactRepository localArtifactRepository;
101
102     private Field rawProjectCacheField;
103
104     private Field processedProjectCacheField;
105
106     private List<String> includes = Collections.singletonList( "**/*.pom" );
107
108     public String getDescription()
109     {
110         return "Generate dependency tree metadata for tracking changes across algorithms";
111     }
112
113     public String getId()
114     {
115         return "dependency-tree-generator";
116     }
117
118     public boolean isPermanent()
119     {
120         return false;
121     }
122
123     public void setGeneratedRepositoryLocation( File generatedRepositoryLocation )
124     {
125         this.generatedRepositoryLocation = generatedRepositoryLocation;
126     }
127
128     public void beginScan( ManagedRepositoryConfiguration repository )
129         throws ConsumerException
130     {
131         repositoryLocation = repository.getLocation();
132
133         if ( generatedRepositoryLocation == null )
134         {
135             generatedRepositoryLocation = new File( repositoryLocation );
136         }
137
138         if ( localRepository == null )
139         {
140             // This is a bit crappy, it would be better to operate entirely within
141             // the base repository, but would need to adjust maven-artifact
142             localRepository = new File( System.getProperty( "user.home" ), ".m2/repository" );
143             try
144             {
145                 localArtifactRepository =
146                     artifactRepositoryFactory.createArtifactRepository( "local",
147                                                                         localRepository.toURL().toExternalForm(),
148                                                                         layout, null, null );
149             }
150             catch ( MalformedURLException e )
151             {
152                 throw new ConsumerException( e.getMessage(), e );
153             }
154         }
155     }
156
157     public void completeScan()
158     {
159     }
160
161     public List getExcludes()
162     {
163         return null;
164     }
165
166     public List getIncludes()
167     {
168         return includes;
169     }
170
171     public void processFile( String path )
172         throws ConsumerException
173     {
174         MavenProject project;
175         try
176         {
177             project = projectBuilder.build( new File( repositoryLocation, path ), localArtifactRepository, null, false );
178
179             // manually flush out the cache for memory concerns and more accurate building
180             flushProjectCache( projectBuilder );
181         }
182         catch ( ProjectBuildingException e )
183         {
184             throw new ConsumerException( e.getMessage(), e );
185         }
186
187         DependencyNode rootNode;
188         try
189         {
190             // TODO: do this for different values of new ScopeArtifactFilter( scope )
191             ArtifactFilter artifactFilter = null;
192
193             rootNode =
194                 dependencyTreeBuilder.buildDependencyTree( project, localArtifactRepository, artifactFactory,
195                                                            artifactMetadataSource, artifactFilter, artifactCollector );
196         }
197         catch ( DependencyTreeBuilderException e )
198         {
199             throw new ConsumerException( e.getMessage(), e );
200         }
201
202         Document document = DocumentHelper.createDocument();
203         DependencyNodeVisitor visitor = new XmlSerializingDependencyNodeVisitor( document );
204
205         // TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
206         visitor = new BuildingDependencyNodeVisitor( visitor );
207
208         CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
209         DependencyNodeVisitor firstPassVisitor =
210             new FilteringDependencyNodeVisitor( collectingVisitor, StateDependencyNodeFilter.INCLUDED );
211         rootNode.accept( firstPassVisitor );
212
213         DependencyNodeFilter secondPassFilter = new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
214         visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
215
216         rootNode.accept( visitor );
217
218         FileWriter writer = null;
219         try
220         {
221             Artifact artifact =
222                 artifactFactory.createProjectArtifact( project.getGroupId(), project.getArtifactId(),
223                                                        project.getVersion() );
224
225             String p = layout.pathOf( artifact );
226             if ( !p.equals( path ) )
227             {
228                 throw new ConsumerException( "Bad path: " + p + "; should be: " + path );
229             }
230
231             File generatedFile = new File( generatedRepositoryLocation, p + ".xml" );
232             generatedFile.getParentFile().mkdirs();
233             writer = new FileWriter( generatedFile );
234             OutputFormat format = OutputFormat.createPrettyPrint();
235             XMLWriter w = new XMLWriter( writer, format );
236             w.write( document );
237         }
238         catch ( IOException e )
239         {
240             throw new ConsumerException( e.getMessage(), e );
241         }
242         finally
243         {
244             IOUtils.closeQuietly( writer );
245         }
246     }
247
248     private void flushProjectCache( MavenProjectBuilder projectBuilder )
249     {
250         try
251         {
252             if ( rawProjectCacheField == null )
253             {
254                 rawProjectCacheField = DefaultMavenProjectBuilder.class.getDeclaredField( "rawProjectCache" );
255                 rawProjectCacheField.setAccessible( true );
256             }
257
258             if ( processedProjectCacheField == null )
259             {
260                 processedProjectCacheField =
261                     DefaultMavenProjectBuilder.class.getDeclaredField( "processedProjectCache" );
262                 processedProjectCacheField.setAccessible( true );
263             }
264
265             rawProjectCacheField.set( projectBuilder, new HashMap() );
266
267             processedProjectCacheField.set( projectBuilder, new HashMap() );
268         }
269         catch ( Exception e )
270         {
271             throw new RuntimeException( e );
272         }
273     }
274
275     private static class XmlSerializingDependencyNodeVisitor
276         implements DependencyNodeVisitor
277     {
278         private Element xmlNode;
279
280         public XmlSerializingDependencyNodeVisitor( Document document )
281         {
282             xmlNode = document.addElement( "tree" );
283         }
284
285         // DependencyNodeVisitor methods ------------------------------------------
286
287         /*
288          * @see org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor#visit(org.apache.maven.shared.dependency.tree.DependencyNode)
289          */
290         public boolean visit( DependencyNode node )
291         {
292             Element dependency = xmlNode.addElement( "dependency" );
293
294             Artifact artifact = node.getArtifact();
295             dependency.addElement( "groupId" ).setText( artifact.getGroupId() );
296             dependency.addElement( "artifactId" ).setText( artifact.getArtifactId() );
297             dependency.addElement( "type" ).setText( artifact.getType() );
298             dependency.addElement( "version" ).setText( artifact.getVersion() );
299             if ( artifact.getScope() != null )
300             {
301                 dependency.addElement( "scope" ).setText( artifact.getScope() );
302             }
303
304             xmlNode = dependency.addElement( "dependencies" );
305
306             return true;
307         }
308
309         /*
310          * @see org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor#endVisit(org.apache.maven.shared.dependency.tree.DependencyNode)
311          */
312         public boolean endVisit( DependencyNode node )
313         {
314             Element e = xmlNode.getParent();
315
316             if ( !xmlNode.hasContent() )
317             {
318                 e.remove( xmlNode );
319             }
320
321             xmlNode = e.getParent();
322
323             return true;
324         }
325     }
326
327     public void setIncludes( List<String> includes )
328     {
329         this.includes = includes;
330     }
331 }