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