View Javadoc

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