This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.metadata.repository;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.archiva.metadata.model.ArtifactMetadata;
023import org.apache.archiva.metadata.model.ProjectMetadata;
024import org.apache.archiva.metadata.model.ProjectVersionMetadata;
025import org.apache.archiva.metadata.model.ProjectVersionReference;
026import org.apache.archiva.filter.ExcludesFilter;
027import org.apache.archiva.metadata.repository.storage.ReadMetadataRequest;
028import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
029import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException;
030import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException;
031import org.apache.archiva.metadata.repository.storage.RepositoryStorageRuntimeException;
032import org.apache.archiva.components.cache.Cache;
033import org.apache.archiva.metadata.audit.RepositoryListener;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036import org.springframework.beans.factory.annotation.Autowired;
037import org.springframework.stereotype.Service;
038
039import javax.inject.Inject;
040import javax.inject.Named;
041import java.util.ArrayList;
042import java.util.Collection;
043import java.util.List;
044
045/**
046 * <p>
047 * Default implementation of the metadata resolver API. At present it will handle updating the content repository
048 * from new or changed information in the model and artifacts from the repository storage.
049 * </p>
050 * <p>
051 * This is a singleton component to allow an alternate implementation to be provided. It is intended to be the same
052 * system-wide for the whole content repository instead of on a per-managed-repository basis. Therefore, the session is
053 * passed in as an argument to obtain any necessary resources, rather than the class being instantiated within the
054 * session in the context of a single managed repository's resolution needs.
055 * </p>
056 * <p>
057 * Note that the caller is responsible for the session, such as closing and saving (which is implied by the resolver
058 * being obtained from within the session). The {@link RepositorySession#markDirty()} method is used as a hint to ensure
059 * that the session knows we've made changes at close. We cannot ensure the changes will be persisted if the caller
060 * chooses to revert first. This is preferable to storing the metadata immediately - a separate session would require
061 * having a bi-directional link with the session factory, and saving the existing session might save other changes
062 * unknowingly by the caller.
063 * </p>
064 */
065@Service("metadataResolver#default")
066public class DefaultMetadataResolver
067    implements MetadataResolver
068{
069
070    private Logger log = LoggerFactory.getLogger( DefaultMetadataResolver.class );
071
072    /**
073     * <p>
074     * FIXME: this needs to be configurable based on storage type - and could also be instantiated per repo. Change to a
075     * factory, and perhaps retrieve from the session. We should avoid creating one per request, however.
076     * </p>
077     * <p>
078     * TODO: Also need to accommodate availability of proxy module
079     * ... could be a different type since we need methods to modify the storage metadata, which would also allow more
080     * appropriate methods to pass in the already determined repository configuration, for example, instead of the ID
081     * </p>
082     */
083    @Inject
084    @Named(value = "repositoryStorage#maven2")
085    private RepositoryStorage repositoryStorage;
086
087    @Inject
088    @Autowired(required = false)
089    private List<RepositoryListener> listeners = new ArrayList<>();
090
091    /**
092     * Cache used for namespaces
093     */
094    @Inject
095    @Named( value = "cache#namespaces" )
096    private Cache<String, Collection<String>> namespacesCache;
097
098    @Override
099    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}