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;
53 * Browse the repository.
55 * TODO change name to ShowVersionedAction to conform to terminology.
57 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="showArtifactAction"
58 * 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;
122 * Show the versioned project information tab. TODO: Change name to 'project' - we are showing project versions
123 * here, not specific artifact information (though that is rendered in the download box).
125 public String artifact()
128 // In the future, this should be replaced by the repository grouping mechanism, so that we are only making
129 // simple resource requests here and letting the resolver take care of it
130 String errorMsg = null;
131 ProjectVersionMetadata versionMetadata = getProjectVersionMetadata();
133 if ( versionMetadata == null )
135 addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
139 if ( versionMetadata.isIncomplete() )
141 addIncompleteModelWarning();
144 model = versionMetadata;
149 private ProjectVersionMetadata getProjectVersionMetadata()
151 ProjectVersionMetadata versionMetadata = null;
152 artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
154 List<String> repos = getObservableRepos();
156 for ( String repoId : repos )
158 if ( versionMetadata == null )
160 // we don't want the implementation being that intelligent - so another resolver to do the
161 // "just-in-time" nature of picking up the metadata (if appropriate for the repository type) is used
164 versionMetadata = metadataResolver.getProjectVersion( repoId, groupId, artifactId, version );
166 catch ( MetadataResolutionException e )
168 addIncompleteModelWarning();
170 // TODO: need a consistent way to construct this - same in ArchivaMetadataCreationConsumer
171 versionMetadata = new ProjectVersionMetadata();
172 versionMetadata.setId( version );
174 if ( versionMetadata != null )
176 repositoryId = repoId;
178 List<ArtifactMetadata> artifacts =
179 new ArrayList<ArtifactMetadata>( metadataResolver.getArtifacts( repoId, groupId, artifactId,
181 Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
183 public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
185 // sort by version (reverse), then ID
186 // TODO: move version sorting into repository handling (maven2 specific), and perhaps add a
187 // way to get latest instead
189 new DefaultArtifactVersion( o2.getVersion() ).compareTo( new DefaultArtifactVersion(
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
267 // stored in the content repository
268 // (especially in the case of pre-population import)
274 * Show the dependencies of this versioned project tab.
276 public String dependencyTree()
278 // temporarily use this as we only need the model for the tag to perform, but we should be resolving the
279 // graph here instead
281 // TODO: may need to note on the page that tree will be incomplete if the other artifacts are not yet stored in
282 // the content repository
283 // (especially in the case of pre-population import)
285 // TODO: a bit ugly, should really be mapping all these results differently now
286 this.dependencyTree = true;
291 public String projectMetadata()
293 String result = artifact();
295 if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
297 genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
300 if ( genericMetadata == null )
302 genericMetadata = new HashMap<String, String>();
308 public String addMetadataProperty()
310 String errorMsg = null;
312 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
313 if ( projectMetadata == null )
315 addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
319 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
321 genericMetadata = new HashMap<String, String>();
325 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
328 if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null ||
329 "".equals( propertyValue.trim() ) )
331 model = projectMetadata;
332 addActionError( errorMsg != null ? errorMsg : "Property Name and Property Value are required." );
336 genericMetadata.put( propertyName, propertyValue );
338 GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
339 genericMetadataFacet.fromProperties( genericMetadata );
342 projectMetadata.addFacet( genericMetadataFacet );
344 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
346 projectMetadata = getProjectVersionMetadata();
348 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
350 model = projectMetadata;
358 public String updateProjectMetadata()
360 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
365 public String deleteMetadataEntry()
367 projectMetadata = getProjectVersionMetadata();
369 if ( !StringUtils.isEmpty( deleteItem ) && !StringUtils.isEmpty( itemValue ) )
371 if ( "dependency".equals( deleteItem ) )
375 else if ( "mailingList".equals( deleteItem ) )
379 else if ( "license".equals( deleteItem ) )
388 return updateProjectMetadata();
391 private void removeDependency()
393 List<Dependency> dependencies = projectMetadata.getDependencies();
394 List<Dependency> newDependencies = new ArrayList<Dependency>();
396 if ( dependencies != null )
398 for ( Dependency dependency : dependencies )
400 if ( !StringUtils.equals( itemValue, dependency.getArtifactId() ) )
402 newDependencies.add( dependency );
407 projectMetadata.setDependencies( newDependencies );
410 private void removeMailingList()
412 List<MailingList> mailingLists = projectMetadata.getMailingLists();
413 List<MailingList> newMailingLists = new ArrayList<MailingList>();
415 if ( mailingLists != null )
417 for ( MailingList mailingList : mailingLists )
419 if ( !StringUtils.equals( itemValue, mailingList.getName() ) )
421 newMailingLists.add( mailingList );
426 projectMetadata.setMailingLists( newMailingLists );
429 private void removeLicense()
431 List<License> licenses = projectMetadata.getLicenses();
432 List<License> newLicenses = new ArrayList<License>();
434 if ( licenses != null )
436 for ( License license : licenses )
438 if ( !StringUtils.equals( itemValue, license.getName() ) )
440 newLicenses.add( license );
445 projectMetadata.setLicenses( newLicenses );
449 public void validate()
451 if ( StringUtils.isBlank( groupId ) )
453 addActionError( "You must specify a group ID to browse" );
456 if ( StringUtils.isBlank( artifactId ) )
458 addActionError( "You must specify a artifact ID to browse" );
461 if ( StringUtils.isBlank( version ) )
463 addActionError( "You must specify a version to browse" );
467 public ProjectVersionMetadata getModel()
472 public String getGroupId()
477 public void setGroupId( String groupId )
479 this.groupId = groupId;
482 public String getArtifactId()
487 public void setArtifactId( String artifactId )
489 this.artifactId = artifactId;
492 public String getVersion()
497 public void setVersion( String version )
499 this.version = version;
502 public List<MailingList> getMailingLists()
507 public List<Dependency> getDependencies()
512 public List<ProjectVersionReference> getDependees()
517 public String getRepositoryId()
522 public void setRepositoryId( String repositoryId )
524 this.repositoryId = repositoryId;
527 public MetadataResolver getMetadataResolver()
529 return metadataResolver;
532 public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
537 public Collection<String> getSnapshotVersions()
539 return artifacts.keySet();
542 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
544 this.repositoryFactory = repositoryFactory;
547 public boolean isDependencyTree()
549 return dependencyTree;
552 public ProjectVersionMetadata getProjectMetadata()
554 return projectMetadata;
557 public void setProjectMetadata( ProjectVersionMetadata projectMetadata )
559 this.projectMetadata = projectMetadata;
562 public void setDeleteItem( String deleteItem )
564 this.deleteItem = deleteItem;
567 public void setItemValue( String itemValue )
569 this.itemValue = itemValue;
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 // TODO: move this into the artifact metadata itself via facets where necessary
604 public class ArtifactDownloadInfo
608 private String namespace;
610 private String project;
616 private String repositoryId;
618 private String version;
622 public ArtifactDownloadInfo( ArtifactMetadata artifact )
624 repositoryId = artifact.getRepositoryId();
626 // TODO: use metadata resolver capability instead - maybe the storage path could be stored in the metadata
627 // though keep in mind the request may not necessarily need to reflect the storage
628 ManagedRepositoryContent repo;
631 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
633 catch ( RepositoryException e )
635 throw new RuntimeException( e );
638 ArtifactReference ref = new ArtifactReference();
639 ref.setArtifactId( artifact.getProject() );
640 ref.setGroupId( artifact.getNamespace() );
641 ref.setVersion( artifact.getVersion() );
642 path = repo.toPath( ref );
643 path = path.substring( 0, path.lastIndexOf( "/" ) + 1 ) + artifact.getId();
645 // TODO: need to accommodate Maven 1 layout too. Non-maven repository formats will need to generate this
646 // facet (perhaps on the fly) if wanting to display the Maven 2 elements on the Archiva pages
648 MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
651 type = facet.getType();
655 namespace = artifact.getNamespace();
656 project = artifact.getProject();
658 // TODO: find a reusable formatter for this
659 double s = artifact.getSize();
679 size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
680 id = artifact.getId();
681 version = artifact.getVersion();
684 public String getNamespace()
689 public String getType()
694 public String getProject()
699 public String getSize()
704 public String getId()
709 public String getVersion()
714 public String getRepositoryId()
719 public String getPath()