]> source.dussan.org Git - archiva.git/blob
559798a621e9ef26efb56863a8920f5619583296
[archiva.git] /
1 package org.apache.archiva.metadata.repository;
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.metadata.model.ArtifactMetadata;
23 import org.apache.archiva.metadata.model.ProjectMetadata;
24 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
25 import org.apache.archiva.metadata.model.ProjectVersionReference;
26 import org.apache.archiva.metadata.repository.filter.ExcludesFilter;
27 import org.apache.archiva.metadata.repository.storage.ReadMetadataRequest;
28 import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
29 import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException;
30 import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException;
31 import org.apache.archiva.metadata.repository.storage.RepositoryStorageRuntimeException;
32 import org.apache.archiva.redback.components.cache.Cache;
33 import org.apache.archiva.repository.events.RepositoryListener;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.stereotype.Service;
38
39 import javax.inject.Inject;
40 import javax.inject.Named;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.List;
44
45 /**
46  * Default implementation of the metadata resolver API. At present it will handle updating the content repository
47  * from new or changed information in the model and artifacts from the repository storage.
48  * <p/>
49  * This is a singleton component to allow an alternate implementation to be provided. It is intended to be the same
50  * system-wide for the whole content repository instead of on a per-managed-repository basis. Therefore, the session is
51  * passed in as an argument to obtain any necessary resources, rather than the class being instantiated within the
52  * session in the context of a single managed repository's resolution needs.
53  * <p/>
54  * Note that the caller is responsible for the session, such as closing and saving (which is implied by the resolver
55  * being obtained from within the session). The {@link RepositorySession#markDirty()} method is used as a hint to ensure
56  * that the session knows we've made changes at close. We cannot ensure the changes will be persisted if the caller
57  * chooses to revert first. This is preferable to storing the metadata immediately - a separate session would require
58  * having a bi-directional link with the session factory, and saving the existing session might save other changes
59  * unknowingly by the caller.
60  * <p/>
61  */
62 @Service("metadataResolver#default")
63 public class DefaultMetadataResolver
64     implements MetadataResolver
65 {
66
67     private Logger log = LoggerFactory.getLogger( DefaultMetadataResolver.class );
68
69     /**
70      * FIXME: this needs to be configurable based on storage type - and could also be instantiated per repo. Change to a
71      * factory, and perhaps retrieve from the session. We should avoid creating one per request, however.
72      * <p/>
73      * TODO: Also need to accommodate availability of proxy module
74      * ... could be a different type since we need methods to modify the storage metadata, which would also allow more
75      * appropriate methods to pass in the already determined repository configuration, for example, instead of the ID
76      */
77     @Inject
78     @Named(value = "repositoryStorage#maven2")
79     private RepositoryStorage repositoryStorage;
80
81     @Inject
82     @Autowired(required = false)
83     private List<RepositoryListener> listeners;
84
85     /**
86      * Cache used for namespaces
87      */
88     @Inject
89     @Named( value = "cache#namespaces" )
90     private Cache<String, Collection<String>> namespacesCache;
91
92     @Override
93     public ProjectVersionMetadata resolveProjectVersion( RepositorySession session, String repoId, String namespace,
94                                                          String projectId, String projectVersion )
95         throws MetadataResolutionException
96     {
97         MetadataRepository metadataRepository = session.getRepository();
98
99         ProjectVersionMetadata metadata =
100             metadataRepository.getProjectVersion( repoId, namespace, projectId, projectVersion );
101         // TODO: do we want to detect changes as well by comparing timestamps? isProjectVersionNewerThan(updated)
102         //       in such cases we might also remove/update stale metadata, including adjusting plugin-based facets
103         //       This would also be better than checking for completeness - we can then refresh only when fixed (though
104         //       sometimes this has an additional dependency - such as a parent - requesting the user to force an update
105         //       may then work here and be more efficient than always trying again)
106         if ( metadata == null || metadata.isIncomplete() )
107         {
108             try
109             {
110                 ReadMetadataRequest readMetadataRequest =
111                     new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
112                         projectId ).projectVersion( projectVersion ).browsingRequest( true );
113                 metadata = repositoryStorage.readProjectVersionMetadata( readMetadataRequest );
114
115                 log.debug( "Resolved project version metadata from storage: {}", metadata );
116
117                 // FIXME: make this a more generic post-processing that plugins can take advantage of
118                 //       eg. maven projects should be able to process parent here
119                 if ( !metadata.getDependencies().isEmpty() )
120                 {
121                     ProjectVersionReference ref = new ProjectVersionReference();
122                     ref.setNamespace( namespace );
123                     ref.setProjectId( projectId );
124                     ref.setProjectVersion( projectVersion );
125                     ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
126                 }
127                 try
128                 {
129                     for ( RepositoryListener listener : listeners )
130                     {
131                         listener.addArtifact( session, repoId, namespace, projectId, metadata );
132                     }
133                     metadataRepository.updateProjectVersion( repoId, namespace, projectId, metadata );
134                 }
135                 catch ( MetadataRepositoryException e )
136                 {
137                     log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
138                 }
139
140                 session.markDirty();
141             }
142             catch ( RepositoryStorageMetadataInvalidException e )
143             {
144                 for ( RepositoryListener listener : listeners )
145                 {
146                     listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
147                 }
148                 throw new MetadataResolutionException( e.getMessage(), e );
149             }
150             catch ( RepositoryStorageMetadataNotFoundException e )
151             {
152                 for ( RepositoryListener listener : listeners )
153                 {
154                     listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
155                 }
156                 // no need to rethrow - return null
157             }
158             catch ( RepositoryStorageRuntimeException e )
159             {
160                 for ( RepositoryListener listener : listeners )
161                 {
162                     listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
163                 }
164                 throw new MetadataResolutionException( e.getMessage(), e );
165             }
166
167         }
168         return metadata;
169     }
170
171     @Override
172     public Collection<ProjectVersionReference> resolveProjectReferences( RepositorySession session, String repoId,
173                                                                          String namespace, String projectId,
174                                                                          String projectVersion )
175         throws MetadataResolutionException
176     {
177         // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario?
178         // not passed to the storage mechanism as resolving references would require iterating all artifacts
179         MetadataRepository metadataRepository = session.getRepository();
180         return metadataRepository.getProjectReferences( repoId, namespace, projectId, projectVersion );
181     }
182
183     @Override
184     public Collection<String> resolveRootNamespaces( RepositorySession session, String repoId )
185         throws MetadataResolutionException
186     {
187         try
188         {
189
190             Collection<String> namespaces = namespacesCache.get( repoId );
191             if ( namespaces != null )
192             {
193                 return namespaces;
194             }
195
196             MetadataRepository metadataRepository = session.getRepository();
197             namespaces = metadataRepository.getRootNamespaces( repoId );
198             Collection<String> storageNamespaces =
199                 repositoryStorage.listRootNamespaces( repoId, new ExcludesFilter<String>( namespaces ) );
200             if ( storageNamespaces != null && !storageNamespaces.isEmpty() )
201             {
202
203                 log.debug( "Resolved root namespaces from storage: {}", storageNamespaces );
204
205                 for ( String n : storageNamespaces )
206                 {
207                     try
208                     {
209                         metadataRepository.updateNamespace( repoId, n );
210                         // just invalidate cache entry
211                         String cacheKey = repoId + "-" + n;
212                         namespacesCache.remove( cacheKey );
213                     }
214                     catch ( MetadataRepositoryException e )
215                     {
216                         log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
217                     }
218                 }
219                 session.markDirty();
220
221                 namespaces = new ArrayList<>( namespaces );
222                 namespaces.addAll( storageNamespaces );
223             }
224
225             namespacesCache.put( repoId, namespaces );
226
227             return namespaces;
228         }
229         catch ( RepositoryStorageRuntimeException e )
230         {
231             throw new MetadataResolutionException( e.getMessage(), e );
232         }
233     }
234
235     @Override
236     public Collection<String> resolveNamespaces( RepositorySession session, String repoId, String namespace )
237         throws MetadataResolutionException
238     {
239         try
240         {
241             MetadataRepository metadataRepository = session.getRepository();
242             String cacheKey = repoId + "-" + namespace;
243             Collection<String> namespaces = namespacesCache.get( cacheKey );
244             if ( namespaces == null )
245             {
246                 namespaces = metadataRepository.getNamespaces( repoId, namespace );
247                 namespacesCache.put( cacheKey, namespaces );
248             }
249             Collection<String> exclusions = new ArrayList<>( namespaces );
250             exclusions.addAll( metadataRepository.getProjects( repoId, namespace ) );
251             Collection<String> storageNamespaces =
252                 repositoryStorage.listNamespaces( repoId, namespace, new ExcludesFilter<String>( exclusions ) );
253             if ( storageNamespaces != null && !storageNamespaces.isEmpty() )
254             {
255
256                 log.debug( "Resolved namespaces from storage: {}", storageNamespaces );
257
258                 for ( String n : storageNamespaces )
259                 {
260                     try
261                     {
262                         metadataRepository.updateNamespace( repoId, namespace + "." + n );
263                         // just invalidate cache entry
264                         cacheKey = repoId + "-" + namespace + "." + n;
265                         namespacesCache.remove( cacheKey );
266                     }
267                     catch ( MetadataRepositoryException e )
268                     {
269                         log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
270                     }
271                 }
272                 session.markDirty();
273
274                 namespaces = new ArrayList<>( namespaces );
275                 namespaces.addAll( storageNamespaces );
276             }
277             return namespaces;
278         }
279         catch ( RepositoryStorageRuntimeException e )
280         {
281             throw new MetadataResolutionException( e.getMessage(), e );
282         }
283     }
284
285     @Override
286     public Collection<String> resolveProjects( RepositorySession session, String repoId, String namespace )
287         throws MetadataResolutionException
288     {
289         try
290         {
291             MetadataRepository metadataRepository = session.getRepository();
292             Collection<String> projects = metadataRepository.getProjects( repoId, namespace );
293             Collection<String> exclusions = new ArrayList<>( projects );
294
295             String cacheKey = repoId + "-" + namespace;
296             Collection<String> namespaces = namespacesCache.get( cacheKey );
297             if ( namespaces == null )
298             {
299                 namespaces = metadataRepository.getNamespaces( repoId, namespace );
300                 namespacesCache.put( cacheKey, namespaces );
301             }
302
303             exclusions.addAll( namespaces );
304
305             Collection<String> storageProjects =
306                 repositoryStorage.listProjects( repoId, namespace, new ExcludesFilter<>( exclusions ) );
307             if ( storageProjects != null && !storageProjects.isEmpty() )
308             {
309
310                 log.debug( "Resolved projects from storage: {}", storageProjects );
311                 for ( String projectId : storageProjects )
312                 {
313                     ProjectMetadata projectMetadata =
314                         repositoryStorage.readProjectMetadata( repoId, namespace, projectId );
315                     if ( projectMetadata != null )
316                     {
317                         try
318                         {
319                             metadataRepository.updateProject( repoId, projectMetadata );
320                         }
321                         catch ( MetadataRepositoryException e )
322                         {
323                             log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
324                         }
325                     }
326                 }
327                 session.markDirty();
328
329                 projects = new ArrayList<>( projects );
330                 projects.addAll( storageProjects );
331             }
332             return projects;
333         }
334         catch ( RepositoryStorageRuntimeException e )
335         {
336             throw new MetadataResolutionException( e.getMessage(), e );
337         }
338     }
339
340     @Override
341     public Collection<String> resolveProjectVersions( RepositorySession session, String repoId, String namespace,
342                                                       String projectId )
343         throws MetadataResolutionException
344     {
345         try
346         {
347             MetadataRepository metadataRepository = session.getRepository();
348
349             Collection<String> projectVersions = metadataRepository.getProjectVersions( repoId, namespace, projectId );
350             Collection<String> storageProjectVersions =
351                 repositoryStorage.listProjectVersions( repoId, namespace, projectId,
352                                                        new ExcludesFilter<String>( projectVersions ) );
353             if ( storageProjectVersions != null && !storageProjectVersions.isEmpty() )
354             {
355                 log.debug( "Resolved project versions from storage: {}", storageProjectVersions );
356
357                 for ( String projectVersion : storageProjectVersions )
358                 {
359                     try
360                     {
361                         ReadMetadataRequest readMetadataRequest =
362                             new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
363                                 projectId ).projectVersion( projectVersion );
364                         ProjectVersionMetadata versionMetadata =
365                             repositoryStorage.readProjectVersionMetadata( readMetadataRequest );
366                         for ( RepositoryListener listener : listeners )
367                         {
368                             listener.addArtifact( session, repoId, namespace, projectId, versionMetadata );
369                         }
370
371                         metadataRepository.updateProjectVersion( repoId, namespace, projectId, versionMetadata );
372                     }
373                     catch ( MetadataRepositoryException e )
374                     {
375                         log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
376                     }
377                     catch ( RepositoryStorageMetadataInvalidException e )
378                     {
379                         log.warn(
380                             "Not update project in metadata repository due to an error resolving it from storage: {}",
381                             e.getMessage() );
382
383                         for ( RepositoryListener listener : listeners )
384                         {
385                             listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
386                         }
387                     }
388                     catch ( RepositoryStorageMetadataNotFoundException e )
389                     {
390                         for ( RepositoryListener listener : listeners )
391                         {
392                             listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
393                         }
394                     }
395                 }
396                 session.markDirty();
397
398                 projectVersions = new ArrayList<>( projectVersions );
399                 projectVersions.addAll( storageProjectVersions );
400             }
401             return projectVersions;
402         }
403         catch ( RepositoryStorageRuntimeException e )
404         {
405             throw new MetadataResolutionException( e.getMessage(), e );
406         }
407     }
408
409     @Override
410     public Collection<ArtifactMetadata> resolveArtifacts( RepositorySession session, String repoId, String namespace,
411                                                           String projectId, String projectVersion )
412         throws MetadataResolutionException
413     {
414         try
415         {
416             MetadataRepository metadataRepository = session.getRepository();
417             Collection<ArtifactMetadata> artifacts =
418                 metadataRepository.getArtifacts( repoId, namespace, projectId, projectVersion );
419             ExcludesFilter<String> filter = new ExcludesFilter<String>( createArtifactIdList( artifacts ) );
420
421             ReadMetadataRequest readMetadataRequest =
422                 new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
423                     projectId ).projectVersion( projectVersion ).filter( filter );
424
425             Collection<ArtifactMetadata> storageArtifacts =
426                 repositoryStorage.readArtifactsMetadata( readMetadataRequest );
427             if ( storageArtifacts != null && !storageArtifacts.isEmpty() )
428             {
429
430                 log.debug( "Resolved artifacts from storage: {}", storageArtifacts );
431
432                 for ( ArtifactMetadata artifact : storageArtifacts )
433                 {
434                     try
435                     {
436                         metadataRepository.updateArtifact( repoId, namespace, projectId, projectVersion, artifact );
437                     }
438                     catch ( MetadataRepositoryException e )
439                     {
440                         log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
441                     }
442                 }
443                 session.markDirty();
444
445                 artifacts = new ArrayList<>( artifacts );
446                 artifacts.addAll( storageArtifacts );
447             }
448             return artifacts;
449         }
450         catch ( RepositoryStorageRuntimeException e )
451         {
452             for ( RepositoryListener listener : listeners )
453             {
454                 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
455             }
456             throw new MetadataResolutionException( e.getMessage(), e );
457         }
458     }
459
460     private Collection<String> createArtifactIdList( Collection<ArtifactMetadata> artifacts )
461     {
462         Collection<String> artifactIds = new ArrayList<>();
463         for ( ArtifactMetadata artifact : artifacts )
464         {
465             artifactIds.add( artifact.getId() );
466         }
467         return artifactIds;
468     }
469 }