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 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
309 String errorMsg = null;
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 genericMetadata.put( propertyName, propertyValue );
328 GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
329 genericMetadataFacet.fromProperties( genericMetadata );
332 projectMetadata.addFacet( genericMetadataFacet );
334 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
336 projectMetadata = getProjectVersionMetadata();
338 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
340 model = projectMetadata;
348 public String updateProjectMetadata()
350 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
355 public String deleteMetadataEntry()
357 projectMetadata = getProjectVersionMetadata();
359 if ( !StringUtils.isEmpty( deleteItem ) && !StringUtils.isEmpty( itemValue ) )
361 if ( "dependency".equals( deleteItem ) )
365 else if ( "mailingList".equals( deleteItem ) )
369 else if ( "license".equals( deleteItem ) )
378 return updateProjectMetadata();
381 private void removeDependency()
383 List<Dependency> dependencies = projectMetadata.getDependencies();
384 List<Dependency> newDependencies = new ArrayList<Dependency>();
386 if ( dependencies != null )
388 for ( Dependency dependency : dependencies )
390 if ( !StringUtils.equals( itemValue, dependency.getArtifactId() ) )
392 newDependencies.add( dependency );
397 projectMetadata.setDependencies( newDependencies );
400 private void removeMailingList()
402 List<MailingList> mailingLists = projectMetadata.getMailingLists();
403 List<MailingList> newMailingLists = new ArrayList<MailingList>();
405 if ( mailingLists != null )
407 for ( MailingList mailingList : mailingLists )
409 if ( !StringUtils.equals( itemValue, mailingList.getName() ) )
411 newMailingLists.add( mailingList );
416 projectMetadata.setMailingLists( newMailingLists );
419 private void removeLicense()
421 List<License> licenses = projectMetadata.getLicenses();
422 List<License> newLicenses = new ArrayList<License>();
424 if ( licenses != null )
426 for ( License license : licenses )
428 if ( !StringUtils.equals( itemValue, license.getName() ) )
430 newLicenses.add( license );
435 projectMetadata.setLicenses( newLicenses );
439 public void validate()
441 if ( StringUtils.isBlank( groupId ) )
443 addActionError( "You must specify a group ID to browse" );
446 if ( StringUtils.isBlank( artifactId ) )
448 addActionError( "You must specify a artifact ID to browse" );
451 if ( StringUtils.isBlank( version ) )
453 addActionError( "You must specify a version to browse" );
457 public ProjectVersionMetadata getModel()
462 public String getGroupId()
467 public void setGroupId( String groupId )
469 this.groupId = groupId;
472 public String getArtifactId()
477 public void setArtifactId( String artifactId )
479 this.artifactId = artifactId;
482 public String getVersion()
487 public void setVersion( String version )
489 this.version = version;
492 public List<MailingList> getMailingLists()
497 public List<Dependency> getDependencies()
502 public List<ProjectVersionReference> getDependees()
507 public String getRepositoryId()
512 public void setRepositoryId( String repositoryId )
514 this.repositoryId = repositoryId;
517 public MetadataResolver getMetadataResolver()
519 return metadataResolver;
522 public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
527 public Collection<String> getSnapshotVersions()
529 return artifacts.keySet();
532 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
534 this.repositoryFactory = repositoryFactory;
537 public boolean isDependencyTree()
539 return dependencyTree;
542 public ProjectVersionMetadata getProjectMetadata()
544 return projectMetadata;
547 public void setProjectMetadata( ProjectVersionMetadata projectMetadata )
549 this.projectMetadata = projectMetadata;
552 public void setDeleteItem( String deleteItem )
554 this.deleteItem = deleteItem;
557 public void setItemValue( String itemValue )
559 this.itemValue = itemValue;
562 public Map<String, String> getGenericMetadata()
564 return genericMetadata;
567 public void setGenericMetadata( Map<String, String> genericMetadata )
569 this.genericMetadata = genericMetadata;
572 public String getPropertyName()
577 public void setPropertyName( String propertyName )
579 this.propertyName = propertyName;
582 public String getPropertyValue()
584 return propertyValue;
587 public void setPropertyValue( String propertyValue )
589 this.propertyValue = propertyValue;
592 // TODO: move this into the artifact metadata itself via facets where necessary
594 public class ArtifactDownloadInfo
598 private String namespace;
600 private String project;
606 private String repositoryId;
608 private String version;
612 public ArtifactDownloadInfo( ArtifactMetadata artifact )
614 repositoryId = artifact.getRepositoryId();
616 // TODO: use metadata resolver capability instead - maybe the storage path could be stored in the metadata
617 // though keep in mind the request may not necessarily need to reflect the storage
618 ManagedRepositoryContent repo;
621 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
623 catch ( RepositoryException e )
625 throw new RuntimeException( e );
628 ArtifactReference ref = new ArtifactReference();
629 ref.setArtifactId( artifact.getProject() );
630 ref.setGroupId( artifact.getNamespace() );
631 ref.setVersion( artifact.getVersion() );
632 path = repo.toPath( ref );
633 path = path.substring( 0, path.lastIndexOf( "/" ) + 1 ) + artifact.getId();
635 // TODO: need to accommodate Maven 1 layout too. Non-maven repository formats will need to generate this
636 // facet (perhaps on the fly) if wanting to display the Maven 2 elements on the Archiva pages
638 MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
641 type = facet.getType();
645 namespace = artifact.getNamespace();
646 project = artifact.getProject();
648 // TODO: find a reusable formatter for this
649 double s = artifact.getSize();
669 size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
670 id = artifact.getId();
671 version = artifact.getVersion();
674 public String getNamespace()
679 public String getType()
684 public String getProject()
689 public String getSize()
694 public String getId()
699 public String getVersion()
704 public String getRepositoryId()
709 public String getPath()