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;
24 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
25 import org.apache.archiva.metadata.model.ArtifactMetadata;
26 import org.apache.archiva.metadata.model.Dependency;
27 import org.apache.archiva.metadata.model.License;
28 import org.apache.archiva.metadata.model.MailingList;
29 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
30 import org.apache.archiva.metadata.model.ProjectVersionReference;
31 import org.apache.archiva.metadata.repository.MetadataRepository;
32 import org.apache.archiva.metadata.repository.MetadataResolutionException;
33 import org.apache.archiva.metadata.repository.MetadataResolver;
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;
42 import java.text.DecimalFormat;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.HashMap;
48 import java.util.LinkedHashMap;
49 import java.util.List;
51 import java.util.TreeMap;
54 * Browse the repository.
56 * TODO change name to ShowVersionedAction to conform to terminology.
58 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="showArtifactAction" instantiation-strategy="per-lookup"
60 public class ShowArtifactAction
61 extends AbstractRepositoryBasedAction
62 implements Validateable
64 /* .\ Not Exposed \._____________________________________________ */
69 private MetadataResolver metadataResolver;
74 private RepositoryContentFactory repositoryFactory;
79 private MetadataRepository metadataRepository;
81 /* .\ Exposed Output Objects \.__________________________________ */
83 private String groupId;
85 private String artifactId;
87 private String version;
89 private String repositoryId;
92 * The model of this versioned project.
94 private ProjectVersionMetadata model;
97 * The list of artifacts that depend on this versioned project.
99 private List<ProjectVersionReference> dependees;
101 private List<MailingList> mailingLists;
103 private List<Dependency> dependencies;
105 private Map<String, List<ArtifactDownloadInfo>> artifacts;
107 private boolean dependencyTree = false;
109 private ProjectVersionMetadata projectMetadata;
111 private String deleteItem;
113 private String itemValue;
115 private Map<String, String> genericMetadata;
117 private String propertyName;
119 private String propertyValue;
123 * Show the versioned project information tab.
124 * TODO: Change name to 'project' - we are showing project versions here, not specific artifact information (though
125 * that is rendered in the download box).
127 public String artifact()
130 // In the future, this should be replaced by the repository grouping mechanism, so that we are only making
131 // simple resource requests here and letting the resolver take care of it
132 String errorMsg = null;
133 ProjectVersionMetadata versionMetadata = getProjectVersionMetadata();
135 if ( versionMetadata == null )
137 addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
141 if ( versionMetadata.isIncomplete() )
143 addIncompleteModelWarning();
146 model = versionMetadata;
151 private ProjectVersionMetadata getProjectVersionMetadata()
153 ProjectVersionMetadata versionMetadata = null;
154 artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
156 List<String> repos = getObservableRepos();
158 for ( String repoId : repos )
160 if ( versionMetadata == null )
162 // we don't want the implementation being that intelligent - so another resolver to do the
163 // "just-in-time" nature of picking up the metadata (if appropriate for the repository type) is used
166 versionMetadata = metadataResolver.getProjectVersion( repoId, groupId, artifactId, version );
168 catch ( MetadataResolutionException e )
170 addIncompleteModelWarning();
172 // TODO: need a consistent way to construct this - same in ArchivaMetadataCreationConsumer
173 versionMetadata = new ProjectVersionMetadata();
174 versionMetadata.setId( version );
176 if ( versionMetadata != null )
178 repositoryId = repoId;
180 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>(
181 metadataResolver.getArtifacts( repoId, groupId, artifactId, version ) );
182 Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
184 public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
186 // sort by version (reverse), then ID
187 // TODO: move version sorting into repository handling (maven2 specific), and perhaps add a
188 // way to get latest instead
189 int result = new DefaultArtifactVersion( o2.getVersion() ).compareTo(
190 new DefaultArtifactVersion( o1.getVersion() ) );
191 return result != 0 ? result : o1.getId().compareTo( o2.getId() );
195 for ( ArtifactMetadata artifact : artifacts )
197 List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
200 l = new ArrayList<ArtifactDownloadInfo>();
201 this.artifacts.put( artifact.getVersion(), l );
203 l.add( new ArtifactDownloadInfo( artifact ) );
209 return versionMetadata;
212 private void addIncompleteModelWarning()
214 addActionMessage( "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
218 * Show the artifact information tab.
220 public String dependencies()
222 String result = artifact();
224 this.dependencies = model.getDependencies();
230 * Show the mailing lists information tab.
232 public String mailingLists()
234 String result = artifact();
236 this.mailingLists = model.getMailingLists();
242 * Show the reports tab.
244 public String reports()
246 // TODO: hook up reports on project
252 * Show the dependees (other artifacts that depend on this project) tab.
254 public String dependees()
256 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
257 // TODO: what if we get duplicates across repositories?
258 for ( String repoId : getObservableRepos() )
260 // TODO: what about if we want to see this irrespective of version?
261 references.addAll( metadataResolver.getProjectReferences( repoId, groupId, artifactId, version ) );
264 this.dependees = references;
266 // TODO: may need to note on the page that references will be incomplete if the other artifacts are not yet stored in the content repository
267 // (especially in the case of pre-population import)
273 * Show the dependencies of this versioned project tab.
275 public String dependencyTree()
277 // temporarily use this as we only need the model for the tag to perform, but we should be resolving the
278 // graph here instead
280 // TODO: may need to note on the page that tree will be incomplete if the other artifacts are not yet stored in the content repository
281 // (especially in the case of pre-population import)
283 // TODO: a bit ugly, should really be mapping all these results differently now
284 this.dependencyTree = true;
289 public String projectMetadata()
291 String result = artifact();
293 if( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
295 genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
298 if( genericMetadata == null )
300 genericMetadata = new HashMap<String, String>();
306 public String addMetadataProperty()
308 String errorMsg = null;
310 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
311 if( projectMetadata == null )
313 addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
317 if( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
319 genericMetadata = new HashMap<String, String>();
323 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
326 if( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null || "".equals( propertyValue.trim() ) )
328 model = projectMetadata;
329 addActionError( errorMsg != null ? errorMsg : "Property Name and Property Value are required." );
333 genericMetadata.put( propertyName, propertyValue );
335 GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
336 genericMetadataFacet.fromProperties( genericMetadata );
339 projectMetadata.addFacet( genericMetadataFacet );
341 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
343 projectMetadata = getProjectVersionMetadata();
345 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
347 model = projectMetadata;
355 public String updateProjectMetadata()
357 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
362 public String deleteMetadataEntry()
364 projectMetadata = getProjectVersionMetadata();
366 if ( !StringUtils.isEmpty( deleteItem ) && !StringUtils.isEmpty( itemValue ) )
368 if ( "dependency".equals( deleteItem ) )
372 else if ( "mailingList".equals( deleteItem ) )
376 else if ( "license".equals( deleteItem ) )
385 return updateProjectMetadata();
388 private void removeDependency()
390 List<Dependency> dependencies = projectMetadata.getDependencies();
391 List<Dependency> newDependencies = new ArrayList<Dependency>();
393 if ( dependencies != null )
395 for ( Dependency dependency : dependencies )
397 if ( !StringUtils.equals( itemValue, dependency.getArtifactId() ) )
399 newDependencies.add( dependency );
404 projectMetadata.setDependencies( newDependencies );
407 private void removeMailingList()
409 List<MailingList> mailingLists = projectMetadata.getMailingLists();
410 List<MailingList> newMailingLists = new ArrayList<MailingList>();
412 if ( mailingLists != null )
414 for ( MailingList mailingList : mailingLists )
416 if ( !StringUtils.equals( itemValue, mailingList.getName() ) )
418 newMailingLists.add( mailingList );
423 projectMetadata.setMailingLists( newMailingLists );
426 private void removeLicense()
428 List<License> licenses = projectMetadata.getLicenses();
429 List<License> newLicenses = new ArrayList<License>();
431 if ( licenses != null )
433 for ( License license : licenses )
435 if ( !StringUtils.equals( itemValue, license.getName() ) )
437 newLicenses.add( license );
442 projectMetadata.setLicenses( newLicenses );
446 public void validate()
448 if ( StringUtils.isBlank( groupId ) )
450 addActionError( "You must specify a group ID to browse" );
453 if ( StringUtils.isBlank( artifactId ) )
455 addActionError( "You must specify a artifact ID to browse" );
458 if ( StringUtils.isBlank( version ) )
460 addActionError( "You must specify a version to browse" );
464 public ProjectVersionMetadata getModel()
469 public String getGroupId()
474 public void setGroupId( String groupId )
476 this.groupId = groupId;
479 public String getArtifactId()
484 public void setArtifactId( String artifactId )
486 this.artifactId = artifactId;
489 public String getVersion()
494 public void setVersion( String version )
496 this.version = version;
499 public List<MailingList> getMailingLists()
504 public List<Dependency> getDependencies()
509 public List<ProjectVersionReference> getDependees()
514 public String getRepositoryId()
519 public void setRepositoryId( String repositoryId )
521 this.repositoryId = repositoryId;
524 public MetadataResolver getMetadataResolver()
526 return metadataResolver;
529 public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
534 public Collection<String> getSnapshotVersions()
536 return artifacts.keySet();
539 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
541 this.repositoryFactory = repositoryFactory;
544 public boolean isDependencyTree()
546 return dependencyTree;
549 public ProjectVersionMetadata getProjectMetadata()
551 return projectMetadata;
554 public void setProjectMetadata( ProjectVersionMetadata projectMetadata )
556 this.projectMetadata = projectMetadata;
559 public void setDeleteItem( String deleteItem )
561 this.deleteItem = deleteItem;
564 public void setItemValue( String itemValue )
566 this.itemValue = itemValue;
569 public Map<String, String> getGenericMetadata()
571 return genericMetadata;
574 public void setGenericMetadata( Map<String, String> genericMetadata )
576 this.genericMetadata = genericMetadata;
579 public String getPropertyName()
584 public void setPropertyName( String propertyName )
586 this.propertyName = propertyName;
589 public String getPropertyValue()
591 return propertyValue;
594 public void setPropertyValue( String propertyValue )
596 this.propertyValue = propertyValue;
599 // TODO: move this into the artifact metadata itself via facets where necessary
601 public class ArtifactDownloadInfo
605 private String namespace;
607 private String project;
613 private String repositoryId;
615 private String version;
619 public ArtifactDownloadInfo( ArtifactMetadata artifact )
621 repositoryId = artifact.getRepositoryId();
623 // TODO: use metadata resolver capability instead - maybe the storage path could be stored in the metadata
624 // though keep in mind the request may not necessarily need to reflect the storage
625 ManagedRepositoryContent repo;
628 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
630 catch ( RepositoryException e )
632 throw new RuntimeException( e );
635 ArtifactReference ref = new ArtifactReference();
636 ref.setArtifactId( artifact.getProject() );
637 ref.setGroupId( artifact.getNamespace() );
638 ref.setVersion( artifact.getVersion() );
639 path = repo.toPath( ref );
640 path = path.substring( 0, path.lastIndexOf( "/" ) + 1 ) + artifact.getId();
642 // TODO: need to accommodate Maven 1 layout too. Non-maven repository formats will need to generate this
643 // facet (perhaps on the fly) if wanting to display the Maven 2 elements on the Archiva pages
645 MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
648 type = facet.getType();
652 namespace = artifact.getNamespace();
653 project = artifact.getProject();
655 // TODO: find a reusable formatter for this
656 double s = artifact.getSize();
676 size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
677 id = artifact.getId();
678 version = artifact.getVersion();
681 public String getNamespace()
686 public String getType()
691 public String getProject()
696 public String getSize()
701 public String getId()
706 public String getVersion()
711 public String getRepositoryId()
716 public String getPath()