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