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;
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 @SuppressWarnings( "serial" )
61 public class ShowArtifactAction
62 extends AbstractRepositoryBasedAction
63 implements Validateable
65 /* .\ Not Exposed \._____________________________________________ */
70 private RepositoryContentFactory repositoryFactory;
72 /* .\ Exposed Output Objects \.__________________________________ */
74 private String groupId;
76 private String artifactId;
78 private String version;
80 private String repositoryId;
83 * The model of this versioned project.
85 private ProjectVersionMetadata model;
88 * The list of artifacts that depend on this versioned project.
90 private List<ProjectVersionReference> dependees;
92 private List<MailingList> mailingLists;
94 private List<Dependency> dependencies;
96 private Map<String, List<ArtifactDownloadInfo>> artifacts;
98 private boolean dependencyTree = false;
100 private String deleteItem;
102 private Map<String, String> genericMetadata;
104 private String propertyName;
106 private String propertyValue;
109 * Show the versioned project information tab. TODO: Change name to 'project' - we are showing project versions
110 * here, not specific artifact information (though that is rendered in the download box).
112 public String artifact()
114 RepositorySession repositorySession = repositorySessionFactory.createSession();
117 return handleArtifact( repositorySession );
121 repositorySession.close();
125 private String handleArtifact( RepositorySession session )
127 // In the future, this should be replaced by the repository grouping mechanism, so that we are only making
128 // simple resource requests here and letting the resolver take care of it
129 ProjectVersionMetadata versionMetadata = getProjectVersionMetadata( session );
131 if ( versionMetadata == null )
133 addActionError( "Artifact not found" );
137 if ( versionMetadata.isIncomplete() )
139 addIncompleteModelWarning();
142 model = versionMetadata;
147 private ProjectVersionMetadata getProjectVersionMetadata( RepositorySession session )
149 ProjectVersionMetadata versionMetadata = null;
150 artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
152 List<String> repos = getObservableRepos();
154 MetadataResolver metadataResolver = session.getResolver();
155 for ( String repoId : repos )
157 if ( versionMetadata == null )
159 // we don't want the implementation being that intelligent - so another resolver to do the
160 // "just-in-time" nature of picking up the metadata (if appropriate for the repository type) is used
163 versionMetadata = metadataResolver.resolveProjectVersion( session, repoId, groupId, artifactId,
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;
181 artifacts = new ArrayList<ArtifactMetadata>( metadataResolver.resolveArtifacts( session, repoId,
186 catch ( MetadataResolutionException e )
188 addIncompleteModelWarning();
190 artifacts = Collections.emptyList();
192 Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
194 public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
196 // sort by version (reverse), then ID
197 // TODO: move version sorting into repository handling (maven2 specific), and perhaps add a
198 // way to get latest instead
199 int result = new DefaultArtifactVersion( o2.getVersion() ).compareTo(
200 new DefaultArtifactVersion( o1.getVersion() ) );
201 return result != 0 ? result : o1.getId().compareTo( o2.getId() );
205 for ( ArtifactMetadata artifact : artifacts )
207 List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
210 l = new ArrayList<ArtifactDownloadInfo>();
211 this.artifacts.put( artifact.getVersion(), l );
213 l.add( new ArtifactDownloadInfo( artifact ) );
219 return versionMetadata;
222 private void addIncompleteModelWarning()
225 "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
229 * Show the artifact information tab.
231 public String dependencies()
233 String result = artifact();
235 this.dependencies = model.getDependencies();
241 * Show the mailing lists information tab.
243 public String mailingLists()
245 String result = artifact();
247 this.mailingLists = model.getMailingLists();
253 * Show the reports tab.
255 public String reports()
257 // TODO: hook up reports on project
263 * Show the dependees (other artifacts that depend on this project) tab.
265 public String dependees()
266 throws MetadataResolutionException
268 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
269 // TODO: what if we get duplicates across repositories?
270 RepositorySession repositorySession = repositorySessionFactory.createSession();
273 MetadataResolver metadataResolver = repositorySession.getResolver();
274 for ( String repoId : getObservableRepos() )
276 // TODO: what about if we want to see this irrespective of version?
277 references.addAll( metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId,
278 artifactId, version ) );
283 repositorySession.close();
286 this.dependees = references;
288 // TODO: may need to note on the page that references will be incomplete if the other artifacts are not yet
289 // stored in the content repository
290 // (especially in the case of pre-population import)
296 * Show the dependencies of this versioned project tab.
298 public String dependencyTree()
300 // temporarily use this as we only need the model for the tag to perform, but we should be resolving the
301 // graph here instead
303 // TODO: may need to note on the page that tree will be incomplete if the other artifacts are not yet stored in
304 // the content repository
305 // (especially in the case of pre-population import)
307 // TODO: a bit ugly, should really be mapping all these results differently now
308 this.dependencyTree = true;
313 public String projectMetadata()
315 String result = artifact();
317 if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
319 genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
322 if ( genericMetadata == null )
324 genericMetadata = new HashMap<String, String>();
330 public String addMetadataProperty()
332 RepositorySession repositorySession = repositorySessionFactory.createSession();
333 ProjectVersionMetadata projectMetadata;
336 MetadataRepository metadataRepository = repositorySession.getRepository();
337 projectMetadata = getProjectVersionMetadata( repositorySession );
338 if ( projectMetadata == null )
340 addActionError( "Artifact not found" );
344 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
346 genericMetadata = new HashMap<String, String>();
350 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
353 if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null || "".equals(
354 propertyValue.trim() ) )
356 model = projectMetadata;
357 addActionError( "Property Name and Property Value are required." );
361 genericMetadata.put( propertyName, propertyValue );
365 updateProjectMetadata( projectMetadata, metadataRepository );
367 catch ( MetadataRepositoryException e )
369 log.warn( "Unable to persist modified project metadata after adding entry: " + e.getMessage(), e );
371 "Unable to add metadata item to underlying content storage - consult application logs." );
375 // TODO: why re-retrieve?
376 projectMetadata = getProjectVersionMetadata( repositorySession );
380 repositorySession.close();
383 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
385 model = projectMetadata;
393 public String deleteMetadataEntry()
395 RepositorySession repositorySession = repositorySessionFactory.createSession();
398 MetadataRepository metadataRepository = repositorySession.getRepository();
399 ProjectVersionMetadata projectMetadata = getProjectVersionMetadata( repositorySession );
401 if ( projectMetadata == null )
403 addActionError( "Artifact not found" );
407 if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) != null )
409 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
411 if ( !StringUtils.isEmpty( deleteItem ) )
413 genericMetadata.remove( deleteItem );
417 updateProjectMetadata( projectMetadata, metadataRepository );
419 catch ( MetadataRepositoryException e )
421 log.warn( "Unable to persist modified project metadata after removing entry: " + e.getMessage(),
424 "Unable to remove metadata item to underlying content storage - consult application logs." );
428 // TODO: why re-retrieve?
429 projectMetadata = getProjectVersionMetadata( repositorySession );
431 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
433 model = projectMetadata;
435 addActionMessage( "Property successfully deleted." );
442 addActionError( "No generic metadata facet for this artifact." );
448 repositorySession.close();
454 private void updateProjectMetadata( ProjectVersionMetadata projectMetadata, MetadataRepository metadataRepository )
455 throws MetadataRepositoryException
457 GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
458 genericMetadataFacet.fromProperties( genericMetadata );
460 projectMetadata.addFacet( genericMetadataFacet );
462 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
466 public void validate()
468 if ( StringUtils.isBlank( groupId ) )
470 addActionError( "You must specify a group ID to browse" );
473 if ( StringUtils.isBlank( artifactId ) )
475 addActionError( "You must specify a artifact ID to browse" );
478 if ( StringUtils.isBlank( version ) )
480 addActionError( "You must specify a version to browse" );
484 public ProjectVersionMetadata getModel()
489 public String getGroupId()
494 public void setGroupId( String groupId )
496 this.groupId = groupId;
499 public String getArtifactId()
504 public void setArtifactId( String artifactId )
506 this.artifactId = artifactId;
509 public String getVersion()
514 public void setVersion( String version )
516 this.version = version;
519 public List<MailingList> getMailingLists()
524 public List<Dependency> getDependencies()
529 public List<ProjectVersionReference> getDependees()
534 public String getRepositoryId()
539 public void setRepositoryId( String repositoryId )
541 this.repositoryId = repositoryId;
544 public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
549 public Collection<String> getSnapshotVersions()
551 return artifacts.keySet();
554 public boolean isDependencyTree()
556 return dependencyTree;
559 public void setDeleteItem( String deleteItem )
561 this.deleteItem = deleteItem;
564 public Map<String, String> getGenericMetadata()
566 return genericMetadata;
569 public void setGenericMetadata( Map<String, String> genericMetadata )
571 this.genericMetadata = genericMetadata;
574 public String getPropertyName()
579 public void setPropertyName( String propertyName )
581 this.propertyName = propertyName;
584 public String getPropertyValue()
586 return propertyValue;
589 public void setPropertyValue( String propertyValue )
591 this.propertyValue = propertyValue;
594 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
596 this.repositoryFactory = repositoryFactory;
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()