]> source.dussan.org Git - archiva.git/blob
592457d4300b9605f3daf6b54c84a61492d78164
[archiva.git] /
1 package org.apache.archiva.reports.consumers;
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 org.apache.archiva.checksum.ChecksumAlgorithm;
23 import org.apache.archiva.checksum.ChecksummedFile;
24 import org.apache.archiva.metadata.model.ArtifactMetadata;
25 import org.apache.archiva.metadata.repository.MetadataRepository;
26 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
27 import org.apache.archiva.metadata.repository.RepositorySession;
28 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
29 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
30 import org.apache.archiva.reports.RepositoryProblemFacet;
31 import org.apache.commons.collections.CollectionUtils;
32 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
33 import org.apache.maven.archiva.configuration.ConfigurationNames;
34 import org.apache.maven.archiva.configuration.FileTypes;
35 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
36 import org.apache.maven.archiva.consumers.AbstractMonitoredConsumer;
37 import org.apache.maven.archiva.consumers.ConsumerException;
38 import org.apache.maven.archiva.consumers.KnownRepositoryContentConsumer;
39 import org.codehaus.plexus.registry.Registry;
40 import org.codehaus.plexus.registry.RegistryListener;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.springframework.context.annotation.Scope;
44 import org.springframework.stereotype.Service;
45
46 import javax.annotation.PostConstruct;
47 import javax.inject.Inject;
48 import javax.inject.Named;
49 import java.io.File;
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.Date;
54 import java.util.List;
55
56 /**
57  * Search the database of known SHA1 Checksums for potential duplicate artifacts.
58  * <p/>
59  * TODO: no need for this to be a scanner - we can just query the database / content repository to get a full list
60  *
61  * @version $Id$
62  *          plexus.component role="org.apache.maven.archiva.consumers.KnownRepositoryContentConsumer"
63  *          role-hint="duplicate-artifacts"
64  *          instantiation-strategy="per-lookup"
65  */
66 @Service( "knownRepositoryContentConsumer#duplicate-artifacts" )
67 @Scope( "prototype" )
68 public class DuplicateArtifactsConsumer
69     extends AbstractMonitoredConsumer
70     implements KnownRepositoryContentConsumer, RegistryListener
71 {
72     private Logger log = LoggerFactory.getLogger( DuplicateArtifactsConsumer.class );
73
74     /**
75      * plexus.configuration default-value="duplicate-artifacts"
76      */
77     private String id = "duplicate-artifacts";
78
79     /**
80      * plexus.configuration default-value="Check for Duplicate Artifacts via SHA1 Checksums"
81      */
82     private String description = "Check for Duplicate Artifacts via SHA1 Checksums";
83
84     /**
85      * plexus.requirement
86      */
87     @Inject
88     private ArchivaConfiguration configuration;
89
90     /**
91      * plexus.requirement
92      */
93     @Inject
94     private FileTypes filetypes;
95
96     /**
97      * FIXME: can be of other types
98      * <p/>
99      * plexus.requirement
100      */
101     @Inject
102     private RepositorySessionFactory repositorySessionFactory;
103
104     private List<String> includes = new ArrayList<String>();
105
106     private File repositoryDir;
107
108     private String repoId;
109
110     /**
111      * FIXME: needs to be selected based on the repository in question
112      * <p/>
113      * plexus.requirement role-hint="maven2"
114      */
115     @Inject
116     @Named( value = "repositoryPathTranslator#maven2" )
117     private RepositoryPathTranslator pathTranslator;
118
119     private RepositorySession repositorySession;
120
121     public String getId()
122     {
123         return id;
124     }
125
126     public String getDescription()
127     {
128         return description;
129     }
130
131     public boolean isPermanent()
132     {
133         return false;
134     }
135
136     public List<String> getIncludes()
137     {
138         return includes;
139     }
140
141     public List<String> getExcludes()
142     {
143         return Collections.emptyList();
144     }
145
146     public void beginScan( ManagedRepositoryConfiguration repo, Date whenGathered )
147         throws ConsumerException
148     {
149         repoId = repo.getId();
150         this.repositoryDir = new File( repo.getLocation() );
151         repositorySession = repositorySessionFactory.createSession();
152     }
153
154     public void beginScan( ManagedRepositoryConfiguration repo, Date whenGathered, boolean executeOnEntireRepo )
155         throws ConsumerException
156     {
157         beginScan( repo, whenGathered );
158     }
159
160     public void processFile( String path )
161         throws ConsumerException
162     {
163         File artifactFile = new File( this.repositoryDir, path );
164
165         // TODO: would be quicker to somehow make sure it ran after the update database consumer, or as a part of that
166         //  perhaps could use an artifact context that is retained for all consumers? First in can set the SHA-1
167         //  alternatively this could come straight from the storage resolver, which could populate the artifact metadata
168         //  in the later parse call with the desired checksum and use that
169         String checksumSha1;
170         ChecksummedFile checksummedFile = new ChecksummedFile( artifactFile );
171         try
172         {
173             checksumSha1 = checksummedFile.calculateChecksum( ChecksumAlgorithm.SHA1 );
174         }
175         catch ( IOException e )
176         {
177             throw new ConsumerException( e.getMessage(), e );
178         }
179
180         MetadataRepository metadataRepository = repositorySession.getRepository();
181
182         List<ArtifactMetadata> results;
183         try
184         {
185             results = metadataRepository.getArtifactsByChecksum( repoId, checksumSha1 );
186         }
187         catch ( MetadataRepositoryException e )
188         {
189             throw new ConsumerException( e.getMessage(), e );
190         }
191
192         if ( CollectionUtils.isNotEmpty( results ) )
193         {
194             ArtifactMetadata originalArtifact;
195             try
196             {
197                 originalArtifact = pathTranslator.getArtifactForPath( repoId, path );
198             }
199             catch ( Exception e )
200             {
201                 log.warn( "Not reporting problem for invalid artifact in checksum check: " + e.getMessage() );
202                 return;
203             }
204
205             for ( ArtifactMetadata dupArtifact : results )
206             {
207                 String id = path.substring( path.lastIndexOf( "/" ) + 1 );
208                 if ( dupArtifact.getId().equals( id )
209                     && dupArtifact.getNamespace().equals( originalArtifact.getNamespace() )
210                     && dupArtifact.getProject().equals( originalArtifact.getProject() )
211                     && dupArtifact.getVersion().equals( originalArtifact.getVersion() ) )
212                 {
213                     // Skip reference to itself.
214                     if ( log.isDebugEnabled() )
215                     {
216                         log.debug( "Not counting duplicate for artifact " + dupArtifact + " for path " + path );
217                     }
218                     continue;
219                 }
220
221                 RepositoryProblemFacet problem = new RepositoryProblemFacet();
222                 problem.setRepositoryId( repoId );
223                 problem.setNamespace( originalArtifact.getNamespace() );
224                 problem.setProject( originalArtifact.getProject() );
225                 problem.setVersion( originalArtifact.getVersion() );
226                 problem.setId( id );
227                 // FIXME: need to get the right storage resolver for the repository the dupe artifact is in, it might be
228                 //       a different type
229                 // FIXME: we need the project version here, not the artifact version
230                 problem.setMessage( "Duplicate Artifact Detected: " + path + " <--> " + pathTranslator.toPath(
231                     dupArtifact.getNamespace(), dupArtifact.getProject(), dupArtifact.getVersion(),
232                     dupArtifact.getId() ) );
233                 problem.setProblem( "duplicate-artifact" );
234
235                 try
236                 {
237                     metadataRepository.addMetadataFacet( repoId, problem );
238                 }
239                 catch ( MetadataRepositoryException e )
240                 {
241                     throw new ConsumerException( e.getMessage(), e );
242                 }
243             }
244         }
245     }
246
247     public void processFile( String path, boolean executeOnEntireRepo )
248         throws ConsumerException
249     {
250         processFile( path );
251     }
252
253     public void completeScan()
254     {
255         repositorySession.close();
256     }
257
258     public void completeScan( boolean executeOnEntireRepo )
259     {
260         completeScan();
261     }
262
263     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
264     {
265         if ( ConfigurationNames.isRepositoryScanning( propertyName ) )
266         {
267             initIncludes();
268         }
269     }
270
271     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
272     {
273         /* do nothing */
274     }
275
276     private void initIncludes()
277     {
278         includes.clear();
279
280         includes.addAll( filetypes.getFileTypePatterns( FileTypes.ARTIFACTS ) );
281     }
282
283     @PostConstruct
284     public void initialize()
285     {
286         initIncludes();
287         configuration.addChangeListener( this );
288     }
289 }