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