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.storage.maven2.MavenArtifactFacet;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.maven.archiva.model.ArtifactReference;
36 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
37 import org.apache.maven.archiva.repository.RepositoryContentFactory;
38 import org.apache.maven.archiva.repository.RepositoryException;
39 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
41 import java.text.DecimalFormat;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.Collections;
45 import java.util.Comparator;
46 import java.util.HashMap;
47 import java.util.LinkedHashMap;
48 import java.util.List;
52 * Browse the repository.
54 * TODO change name to ShowVersionedAction to conform to terminology.
56 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="showArtifactAction"
57 * instantiation-strategy="per-lookup"
59 @SuppressWarnings( "serial" )
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 String deleteItem;
111 private Map<String, String> genericMetadata;
113 private String propertyName;
115 private String propertyValue;
118 * Show the versioned project information tab. TODO: Change name to 'project' - we are showing project versions
119 * here, not specific artifact information (though that is rendered in the download box).
121 public String artifact()
124 // In the future, this should be replaced by the repository grouping mechanism, so that we are only making
125 // simple resource requests here and letting the resolver take care of it
126 String errorMsg = null;
127 ProjectVersionMetadata versionMetadata = getProjectVersionMetadata();
129 if ( versionMetadata == null )
131 addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
135 if ( versionMetadata.isIncomplete() )
137 addIncompleteModelWarning();
140 model = versionMetadata;
145 private ProjectVersionMetadata getProjectVersionMetadata()
147 ProjectVersionMetadata versionMetadata = null;
148 artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
150 List<String> repos = getObservableRepos();
152 for ( String repoId : repos )
154 if ( versionMetadata == null )
156 // we don't want the implementation being that intelligent - so another resolver to do the
157 // "just-in-time" nature of picking up the metadata (if appropriate for the repository type) is used
160 versionMetadata = metadataResolver.resolveProjectVersion( repoId, groupId, artifactId, version );
162 catch ( MetadataResolutionException e )
164 addIncompleteModelWarning();
166 // TODO: need a consistent way to construct this - same in ArchivaMetadataCreationConsumer
167 versionMetadata = new ProjectVersionMetadata();
168 versionMetadata.setId( version );
170 if ( versionMetadata != null )
172 repositoryId = repoId;
174 List<ArtifactMetadata> artifacts;
177 artifacts = new ArrayList<ArtifactMetadata>( metadataResolver.resolveArtifacts( repoId, groupId,
181 catch ( MetadataResolutionException e )
183 addIncompleteModelWarning();
185 artifacts = Collections.emptyList();
187 Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
189 public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
191 // sort by version (reverse), then ID
192 // TODO: move version sorting into repository handling (maven2 specific), and perhaps add a
193 // way to get latest instead
194 int result = new DefaultArtifactVersion( o2.getVersion() ).compareTo(
195 new DefaultArtifactVersion( o1.getVersion() ) );
196 return result != 0 ? result : o1.getId().compareTo( o2.getId() );
200 for ( ArtifactMetadata artifact : artifacts )
202 List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
205 l = new ArrayList<ArtifactDownloadInfo>();
206 this.artifacts.put( artifact.getVersion(), l );
208 l.add( new ArtifactDownloadInfo( artifact ) );
214 return versionMetadata;
217 private void addIncompleteModelWarning()
220 "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
224 * Show the artifact information tab.
226 public String dependencies()
228 String result = artifact();
230 this.dependencies = model.getDependencies();
236 * Show the mailing lists information tab.
238 public String mailingLists()
240 String result = artifact();
242 this.mailingLists = model.getMailingLists();
248 * Show the reports tab.
250 public String reports()
252 // TODO: hook up reports on project
258 * Show the dependees (other artifacts that depend on this project) tab.
260 public String dependees()
261 throws MetadataResolutionException
263 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
264 // TODO: what if we get duplicates across repositories?
265 for ( String repoId : getObservableRepos() )
267 // TODO: what about if we want to see this irrespective of version?
268 references.addAll( metadataResolver.resolveProjectReferences( repoId, groupId, artifactId, version ) );
271 this.dependees = references;
273 // TODO: may need to note on the page that references will be incomplete if the other artifacts are not yet
274 // stored in the content repository
275 // (especially in the case of pre-population import)
281 * Show the dependencies of this versioned project tab.
283 public String dependencyTree()
285 // temporarily use this as we only need the model for the tag to perform, but we should be resolving the
286 // graph here instead
288 // TODO: may need to note on the page that tree will be incomplete if the other artifacts are not yet stored in
289 // the content repository
290 // (especially in the case of pre-population import)
292 // TODO: a bit ugly, should really be mapping all these results differently now
293 this.dependencyTree = true;
298 public String projectMetadata()
300 String result = artifact();
302 if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
304 genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
307 if ( genericMetadata == null )
309 genericMetadata = new HashMap<String, String>();
315 public String addMetadataProperty()
317 String errorMsg = null;
319 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
320 if ( projectMetadata == null )
322 addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
326 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
328 genericMetadata = new HashMap<String, String>();
332 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
335 if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null || "".equals(
336 propertyValue.trim() ) )
338 model = projectMetadata;
339 addActionError( errorMsg != null ? errorMsg : "Property Name and Property Value are required." );
343 genericMetadata.put( propertyName, propertyValue );
347 updateProjectMetadata( projectMetadata );
349 catch ( MetadataRepositoryException e )
351 log.warn( "Unable to persist modified project metadata after adding entry: " + e.getMessage(), e );
352 addActionError( "Unable to add metadata item to underlying content storage - consult application logs." );
356 projectMetadata = getProjectVersionMetadata();
358 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
360 model = projectMetadata;
368 public String deleteMetadataEntry()
370 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
371 String errorMsg = null;
373 if ( projectMetadata == null )
375 addActionError( "Artifact not found" );
379 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) != null )
381 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
383 if ( !StringUtils.isEmpty( deleteItem ) )
385 genericMetadata.remove( deleteItem );
389 updateProjectMetadata( projectMetadata );
391 catch ( MetadataRepositoryException e )
393 log.warn( "Unable to persist modified project metadata after removing entry: " + e.getMessage(),
396 "Unable to remove metadata item to underlying content storage - consult application logs." );
400 projectMetadata = getProjectVersionMetadata();
402 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
404 model = projectMetadata;
406 addActionMessage( "Property successfully deleted." );
413 addActionError( "No generic metadata facet for this artifact." );
420 private void updateProjectMetadata( ProjectVersionMetadata projectMetadata )
421 throws MetadataRepositoryException
423 GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
424 genericMetadataFacet.fromProperties( genericMetadata );
426 projectMetadata.addFacet( genericMetadataFacet );
428 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
432 public void validate()
434 if ( StringUtils.isBlank( groupId ) )
436 addActionError( "You must specify a group ID to browse" );
439 if ( StringUtils.isBlank( artifactId ) )
441 addActionError( "You must specify a artifact ID to browse" );
444 if ( StringUtils.isBlank( version ) )
446 addActionError( "You must specify a version to browse" );
450 public ProjectVersionMetadata getModel()
455 public String getGroupId()
460 public void setGroupId( String groupId )
462 this.groupId = groupId;
465 public String getArtifactId()
470 public void setArtifactId( String artifactId )
472 this.artifactId = artifactId;
475 public String getVersion()
480 public void setVersion( String version )
482 this.version = version;
485 public List<MailingList> getMailingLists()
490 public List<Dependency> getDependencies()
495 public List<ProjectVersionReference> getDependees()
500 public String getRepositoryId()
505 public void setRepositoryId( String repositoryId )
507 this.repositoryId = repositoryId;
510 public MetadataResolver getMetadataResolver()
512 return metadataResolver;
515 public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
520 public Collection<String> getSnapshotVersions()
522 return artifacts.keySet();
525 public boolean isDependencyTree()
527 return dependencyTree;
530 public void setDeleteItem( String deleteItem )
532 this.deleteItem = deleteItem;
535 public Map<String, String> getGenericMetadata()
537 return genericMetadata;
540 public void setGenericMetadata( Map<String, String> genericMetadata )
542 this.genericMetadata = genericMetadata;
545 public String getPropertyName()
550 public void setPropertyName( String propertyName )
552 this.propertyName = propertyName;
555 public String getPropertyValue()
557 return propertyValue;
560 public void setPropertyValue( String propertyValue )
562 this.propertyValue = propertyValue;
565 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
567 this.repositoryFactory = repositoryFactory;
570 public void setMetadataRepository( MetadataRepository metadataRepository )
572 this.metadataRepository = metadataRepository;
575 // TODO: move this into the artifact metadata itself via facets where necessary
577 public class ArtifactDownloadInfo
581 private String namespace;
583 private String project;
589 private String repositoryId;
591 private String version;
595 public ArtifactDownloadInfo( ArtifactMetadata artifact )
597 repositoryId = artifact.getRepositoryId();
599 // TODO: use metadata resolver capability instead - maybe the storage path could be stored in the metadata
600 // though keep in mind the request may not necessarily need to reflect the storage
601 ManagedRepositoryContent repo;
604 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
606 catch ( RepositoryException e )
608 throw new RuntimeException( e );
611 ArtifactReference ref = new ArtifactReference();
612 ref.setArtifactId( artifact.getProject() );
613 ref.setGroupId( artifact.getNamespace() );
614 ref.setVersion( artifact.getVersion() );
615 path = repo.toPath( ref );
616 path = path.substring( 0, path.lastIndexOf( "/" ) + 1 ) + artifact.getId();
618 // TODO: need to accommodate Maven 1 layout too. Non-maven repository formats will need to generate this
619 // facet (perhaps on the fly) if wanting to display the Maven 2 elements on the Archiva pages
621 MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
624 type = facet.getType();
628 namespace = artifact.getNamespace();
629 project = artifact.getProject();
631 // TODO: find a reusable formatter for this
632 double s = artifact.getSize();
652 size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
653 id = artifact.getId();
654 version = artifact.getVersion();
657 public String getNamespace()
662 public String getType()
667 public String getProject()
672 public String getSize()
677 public String getId()
682 public String getVersion()
687 public String getRepositoryId()
692 public String getPath()