1 package org.apache.maven.archiva.web.action;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import com.opensymphony.xwork2.Validateable;
23 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
24 import org.apache.archiva.metadata.model.ArtifactMetadata;
25 import org.apache.archiva.metadata.model.Dependency;
26 import org.apache.archiva.metadata.model.MailingList;
27 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
28 import org.apache.archiva.metadata.model.ProjectVersionReference;
29 import org.apache.archiva.metadata.repository.MetadataRepository;
30 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
31 import org.apache.archiva.metadata.repository.MetadataResolutionException;
32 import org.apache.archiva.metadata.repository.MetadataResolver;
33 import org.apache.archiva.metadata.repository.RepositorySession;
34 import org.apache.archiva.metadata.repository.storage.maven2.MavenArtifactFacet;
35 import org.apache.commons.lang.StringUtils;
36 import org.apache.maven.archiva.model.ArtifactReference;
37 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
38 import org.apache.maven.archiva.repository.RepositoryContentFactory;
39 import org.apache.maven.archiva.repository.RepositoryException;
40 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
41 import org.springframework.context.annotation.Scope;
42 import org.springframework.stereotype.Controller;
44 import java.text.DecimalFormat;
45 import java.text.DecimalFormatSymbols;
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.Comparator;
50 import java.util.HashMap;
51 import java.util.LinkedHashMap;
52 import java.util.List;
53 import java.util.Locale;
57 * Browse the repository.
59 * TODO change name to ShowVersionedAction to conform to terminology.
61 * plexus.component role="com.opensymphony.xwork2.Action" role-hint="showArtifactAction"
62 * instantiation-strategy="per-lookup"
64 @SuppressWarnings( "serial" )
65 @Controller( "showArtifactAction" )
67 public class ShowArtifactAction
68 extends AbstractRepositoryBasedAction
69 implements Validateable
71 /* .\ Not Exposed \._____________________________________________ */
76 private RepositoryContentFactory repositoryFactory;
78 /* .\ Exposed Output Objects \.__________________________________ */
80 private String groupId;
82 private String artifactId;
84 private String version;
86 private String repositoryId;
89 * The model of this versioned project.
91 private ProjectVersionMetadata model;
94 * The list of artifacts that depend on this versioned project.
96 private List<ProjectVersionReference> dependees;
98 private List<MailingList> mailingLists;
100 private List<Dependency> dependencies;
102 private Map<String, List<ArtifactDownloadInfo>> artifacts;
104 private boolean dependencyTree = false;
106 private String deleteItem;
108 private Map<String, String> genericMetadata;
110 private String propertyName;
112 private String propertyValue;
115 * Show the versioned project information tab. TODO: Change name to 'project' - we are showing project versions
116 * here, not specific artifact information (though that is rendered in the download box).
118 public String artifact()
120 RepositorySession repositorySession = repositorySessionFactory.createSession();
123 return handleArtifact( repositorySession );
127 repositorySession.close();
131 private String handleArtifact( RepositorySession session )
133 // In the future, this should be replaced by the repository grouping mechanism, so that we are only making
134 // simple resource requests here and letting the resolver take care of it
135 ProjectVersionMetadata versionMetadata = getProjectVersionMetadata( session );
137 if ( versionMetadata == null )
139 addActionError( "Artifact not found" );
143 if ( versionMetadata.isIncomplete() )
145 addIncompleteModelWarning();
148 model = versionMetadata;
153 private ProjectVersionMetadata getProjectVersionMetadata( RepositorySession session )
155 ProjectVersionMetadata versionMetadata = null;
156 artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
158 List<String> repos = getObservableRepos();
160 MetadataResolver metadataResolver = session.getResolver();
161 for ( String repoId : repos )
163 if ( versionMetadata == null )
165 // we don't want the implementation being that intelligent - so another resolver to do the
166 // "just-in-time" nature of picking up the metadata (if appropriate for the repository type) is used
169 versionMetadata = metadataResolver.resolveProjectVersion( session, repoId, groupId, artifactId,
172 catch ( MetadataResolutionException e )
174 addIncompleteModelWarning();
176 // TODO: need a consistent way to construct this - same in ArchivaMetadataCreationConsumer
177 versionMetadata = new ProjectVersionMetadata();
178 versionMetadata.setId( version );
180 if ( versionMetadata != null )
182 repositoryId = repoId;
184 List<ArtifactMetadata> artifacts;
187 artifacts = new ArrayList<ArtifactMetadata>( metadataResolver.resolveArtifacts( session, repoId,
192 catch ( MetadataResolutionException e )
194 addIncompleteModelWarning();
196 artifacts = Collections.emptyList();
198 Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
200 public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
202 // sort by version (reverse), then ID
203 // TODO: move version sorting into repository handling (maven2 specific), and perhaps add a
204 // way to get latest instead
205 int result = new DefaultArtifactVersion( o2.getVersion() ).compareTo(
206 new DefaultArtifactVersion( o1.getVersion() ) );
207 return result != 0 ? result : o1.getId().compareTo( o2.getId() );
211 for ( ArtifactMetadata artifact : artifacts )
213 List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
216 l = new ArrayList<ArtifactDownloadInfo>();
217 this.artifacts.put( artifact.getVersion(), l );
219 l.add( new ArtifactDownloadInfo( artifact ) );
225 return versionMetadata;
228 private void addIncompleteModelWarning()
231 "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
235 * Show the artifact information tab.
237 public String dependencies()
239 String result = artifact();
241 this.dependencies = model.getDependencies();
247 * Show the mailing lists information tab.
249 public String mailingLists()
251 String result = artifact();
253 this.mailingLists = model.getMailingLists();
259 * Show the reports tab.
261 public String reports()
263 // TODO: hook up reports on project
269 * Show the dependees (other artifacts that depend on this project) tab.
271 public String dependees()
272 throws MetadataResolutionException
274 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
275 // TODO: what if we get duplicates across repositories?
276 RepositorySession repositorySession = repositorySessionFactory.createSession();
279 MetadataResolver metadataResolver = repositorySession.getResolver();
280 for ( String repoId : getObservableRepos() )
282 // TODO: what about if we want to see this irrespective of version?
283 references.addAll( metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId,
284 artifactId, version ) );
289 repositorySession.close();
292 this.dependees = references;
294 // TODO: may need to note on the page that references will be incomplete if the other artifacts are not yet
295 // stored in the content repository
296 // (especially in the case of pre-population import)
302 * Show the dependencies of this versioned project tab.
304 public String dependencyTree()
306 // temporarily use this as we only need the model for the tag to perform, but we should be resolving the
307 // graph here instead
309 // TODO: may need to note on the page that tree will be incomplete if the other artifacts are not yet stored in
310 // the content repository
311 // (especially in the case of pre-population import)
313 // TODO: a bit ugly, should really be mapping all these results differently now
314 this.dependencyTree = true;
319 public String projectMetadata()
321 String result = artifact();
323 if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
325 genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
328 if ( genericMetadata == null )
330 genericMetadata = new HashMap<String, String>();
336 public String addMetadataProperty()
338 RepositorySession repositorySession = repositorySessionFactory.createSession();
339 ProjectVersionMetadata projectMetadata;
342 MetadataRepository metadataRepository = repositorySession.getRepository();
343 projectMetadata = getProjectVersionMetadata( repositorySession );
344 if ( projectMetadata == null )
346 addActionError( "Artifact not found" );
350 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
352 genericMetadata = new HashMap<String, String>();
356 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
359 if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null || "".equals(
360 propertyValue.trim() ) )
362 model = projectMetadata;
363 addActionError( "Property Name and Property Value are required." );
367 genericMetadata.put( propertyName, propertyValue );
371 updateProjectMetadata( projectMetadata, metadataRepository );
372 repositorySession.save();
374 catch ( MetadataRepositoryException e )
376 log.warn( "Unable to persist modified project metadata after adding entry: " + e.getMessage(), e );
378 "Unable to add metadata item to underlying content storage - consult application logs." );
382 // TODO: why re-retrieve?
383 projectMetadata = getProjectVersionMetadata( repositorySession );
387 repositorySession.close();
390 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
392 model = projectMetadata;
400 public String deleteMetadataEntry()
402 RepositorySession repositorySession = repositorySessionFactory.createSession();
405 MetadataRepository metadataRepository = repositorySession.getRepository();
406 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata( repositorySession );
408 if ( projectMetadata == null )
410 addActionError( "Artifact not found" );
414 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) != null )
416 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
418 if ( !StringUtils.isEmpty( deleteItem ) )
420 genericMetadata.remove( deleteItem );
424 updateProjectMetadata( projectMetadata, metadataRepository );
425 repositorySession.save();
427 catch ( MetadataRepositoryException e )
429 log.warn( "Unable to persist modified project metadata after removing entry: " + e.getMessage(),
432 "Unable to remove metadata item to underlying content storage - consult application logs." );
436 // TODO: why re-retrieve?
437 projectMetadata = getProjectVersionMetadata( repositorySession );
439 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
441 model = projectMetadata;
443 addActionMessage( "Property successfully deleted." );
450 addActionError( "No generic metadata facet for this artifact." );
456 repositorySession.close();
462 private void updateProjectMetadata( ProjectVersionMetadata projectMetadata, MetadataRepository metadataRepository )
463 throws MetadataRepositoryException
465 GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
466 genericMetadataFacet.fromProperties( genericMetadata );
468 projectMetadata.addFacet( genericMetadataFacet );
470 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
474 public void validate()
476 if ( StringUtils.isBlank( groupId ) )
478 addActionError( "You must specify a group ID to browse" );
481 if ( StringUtils.isBlank( artifactId ) )
483 addActionError( "You must specify a artifact ID to browse" );
486 if ( StringUtils.isBlank( version ) )
488 addActionError( "You must specify a version to browse" );
492 public ProjectVersionMetadata getModel()
497 public String getGroupId()
502 public void setGroupId( String groupId )
504 this.groupId = groupId;
507 public String getArtifactId()
512 public void setArtifactId( String artifactId )
514 this.artifactId = artifactId;
517 public String getVersion()
522 public void setVersion( String version )
524 this.version = version;
527 public List<MailingList> getMailingLists()
532 public List<Dependency> getDependencies()
537 public List<ProjectVersionReference> getDependees()
542 public String getRepositoryId()
547 public void setRepositoryId( String repositoryId )
549 this.repositoryId = repositoryId;
552 public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
557 public Collection<String> getSnapshotVersions()
559 return artifacts.keySet();
562 public boolean isDependencyTree()
564 return dependencyTree;
567 public void setDeleteItem( String deleteItem )
569 this.deleteItem = deleteItem;
572 public Map<String, String> getGenericMetadata()
574 return genericMetadata;
577 public void setGenericMetadata( Map<String, String> genericMetadata )
579 this.genericMetadata = genericMetadata;
582 public String getPropertyName()
587 public void setPropertyName( String propertyName )
589 this.propertyName = propertyName;
592 public String getPropertyValue()
594 return propertyValue;
597 public void setPropertyValue( String propertyValue )
599 this.propertyValue = propertyValue;
602 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
604 this.repositoryFactory = repositoryFactory;
607 // TODO: move this into the artifact metadata itself via facets where necessary
609 public class ArtifactDownloadInfo
613 private String namespace;
615 private String project;
621 private String repositoryId;
623 private String version;
627 public ArtifactDownloadInfo( ArtifactMetadata artifact )
629 repositoryId = artifact.getRepositoryId();
631 // TODO: use metadata resolver capability instead - maybe the storage path could be stored in the metadata
632 // though keep in mind the request may not necessarily need to reflect the storage
633 ManagedRepositoryContent repo;
636 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
638 catch ( RepositoryException e )
640 throw new RuntimeException( e );
643 ArtifactReference ref = new ArtifactReference();
644 ref.setArtifactId( artifact.getProject() );
645 ref.setGroupId( artifact.getNamespace() );
646 ref.setVersion( artifact.getVersion() );
647 path = repo.toPath( ref );
648 path = path.substring( 0, path.lastIndexOf( "/" ) + 1 ) + artifact.getId();
650 // TODO: need to accommodate Maven 1 layout too. Non-maven repository formats will need to generate this
651 // facet (perhaps on the fly) if wanting to display the Maven 2 elements on the Archiva pages
653 MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
656 type = facet.getType();
660 namespace = artifact.getNamespace();
661 project = artifact.getProject();
663 // TODO: find a reusable formatter for this
664 double s = artifact.getSize();
684 DecimalFormat df = new DecimalFormat( "#,###.##", new DecimalFormatSymbols( Locale.US) );
685 size = df.format( s ) + " " + symbol;
686 id = artifact.getId();
687 version = artifact.getVersion();
690 public String getNamespace()
695 public String getType()
700 public String getProject()
705 public String getSize()
710 public String getId()
715 public String getVersion()
720 public String getRepositoryId()
725 public String getPath()