]> source.dussan.org Git - archiva.git/blob
3c84d2417b1b4c02350a2a32079de4f6a1fc520c
[archiva.git] /
1 package org.apache.maven.archiva.web.action;
2
3 /*
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
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
22 import com.opensymphony.xwork2.Validateable;
23
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.MailingList;
28 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
29 import org.apache.archiva.metadata.model.ProjectVersionReference;
30 import org.apache.archiva.metadata.repository.MetadataRepository;
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;
40
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;
49 import java.util.Map;
50
51 /**
52  * Browse the repository. 
53  * 
54  * TODO change name to ShowVersionedAction to conform to terminology.
55  * 
56  * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="showArtifactAction"
57  *                   instantiation-strategy="per-lookup"
58  */
59 @SuppressWarnings( "serial" )
60 public class ShowArtifactAction
61     extends AbstractRepositoryBasedAction
62     implements Validateable
63 {
64     /* .\ Not Exposed \._____________________________________________ */
65
66     /**
67      * @plexus.requirement
68      */
69     private MetadataResolver metadataResolver;
70
71     /**
72      * @plexus.requirement
73      */
74     private RepositoryContentFactory repositoryFactory;
75
76     /**
77      * @plexus.requirement
78      */
79     private MetadataRepository metadataRepository;
80
81     /* .\ Exposed Output Objects \.__________________________________ */
82
83     private String groupId;
84
85     private String artifactId;
86
87     private String version;
88
89     private String repositoryId;
90
91     /**
92      * The model of this versioned project.
93      */
94     private ProjectVersionMetadata model;
95
96     /**
97      * The list of artifacts that depend on this versioned project.
98      */
99     private List<ProjectVersionReference> dependees;
100
101     private List<MailingList> mailingLists;
102
103     private List<Dependency> dependencies;
104
105     private Map<String, List<ArtifactDownloadInfo>> artifacts;
106
107     private boolean dependencyTree = false;
108     
109     private String deleteItem;
110
111     private Map<String, String> genericMetadata;
112
113     private String propertyName;
114
115     private String propertyValue;
116
117     /**
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).
120      */
121     public String artifact()
122     {
123
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();
128
129         if ( versionMetadata == null )
130         {
131             addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
132             return ERROR;
133         }
134
135         if ( versionMetadata.isIncomplete() )
136         {
137             addIncompleteModelWarning();
138         }
139
140         model = versionMetadata;
141
142         return SUCCESS;
143     }
144
145     private ProjectVersionMetadata getProjectVersionMetadata()
146     {
147         ProjectVersionMetadata versionMetadata = null;
148         artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
149
150         List<String> repos = getObservableRepos();
151
152         for ( String repoId : repos )
153         {
154             if ( versionMetadata == null )
155             {
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
158                 try
159                 {
160                     versionMetadata = metadataResolver.getProjectVersion( repoId, groupId, artifactId, version );
161                 }
162                 catch ( MetadataResolutionException e )
163                 {
164                     addIncompleteModelWarning();
165
166                     // TODO: need a consistent way to construct this - same in ArchivaMetadataCreationConsumer
167                     versionMetadata = new ProjectVersionMetadata();
168                     versionMetadata.setId( version );
169                 }
170                 if ( versionMetadata != null )
171                 {
172                     repositoryId = repoId;
173
174                     List<ArtifactMetadata> artifacts =
175                         new ArrayList<ArtifactMetadata>( metadataResolver.getArtifacts( repoId, groupId, artifactId,
176                                                                                         version ) );
177                     Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
178                     {
179                         public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
180                         {
181                             // sort by version (reverse), then ID
182                             // TODO: move version sorting into repository handling (maven2 specific), and perhaps add a
183                             // way to get latest instead
184                             int result =
185                                 new DefaultArtifactVersion( o2.getVersion() ).compareTo( new DefaultArtifactVersion(
186                                                                                                                      o1.getVersion() ) );
187                             return result != 0 ? result : o1.getId().compareTo( o2.getId() );
188                         }
189                     } );
190
191                     for ( ArtifactMetadata artifact : artifacts )
192                     {
193                         List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
194                         if ( l == null )
195                         {
196                             l = new ArrayList<ArtifactDownloadInfo>();
197                             this.artifacts.put( artifact.getVersion(), l );
198                         }
199                         l.add( new ArtifactDownloadInfo( artifact ) );
200                     }
201                 }
202             }
203         }
204
205         return versionMetadata;
206     }
207
208     private void addIncompleteModelWarning()
209     {
210         addActionMessage( "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
211     }
212
213     /**
214      * Show the artifact information tab.
215      */
216     public String dependencies()
217     {
218         String result = artifact();
219
220         this.dependencies = model.getDependencies();
221
222         return result;
223     }
224
225     /**
226      * Show the mailing lists information tab.
227      */
228     public String mailingLists()
229     {
230         String result = artifact();
231
232         this.mailingLists = model.getMailingLists();
233
234         return result;
235     }
236
237     /**
238      * Show the reports tab.
239      */
240     public String reports()
241     {
242         // TODO: hook up reports on project
243
244         return SUCCESS;
245     }
246
247     /**
248      * Show the dependees (other artifacts that depend on this project) tab.
249      */
250     public String dependees()
251     {
252         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
253         // TODO: what if we get duplicates across repositories?
254         for ( String repoId : getObservableRepos() )
255         {
256             // TODO: what about if we want to see this irrespective of version?
257             references.addAll( metadataResolver.getProjectReferences( repoId, groupId, artifactId, version ) );
258         }
259
260         this.dependees = references;
261
262         // TODO: may need to note on the page that references will be incomplete if the other artifacts are not yet
263         // stored in the content repository
264         // (especially in the case of pre-population import)
265
266         return artifact();
267     }
268
269     /**
270      * Show the dependencies of this versioned project tab.
271      */
272     public String dependencyTree()
273     {
274         // temporarily use this as we only need the model for the tag to perform, but we should be resolving the
275         // graph here instead
276
277         // TODO: may need to note on the page that tree will be incomplete if the other artifacts are not yet stored in
278         // the content repository
279         // (especially in the case of pre-population import)
280
281         // TODO: a bit ugly, should really be mapping all these results differently now
282         this.dependencyTree = true;
283
284         return artifact();
285     }
286
287     public String projectMetadata()
288     {
289         String result = artifact();
290
291         if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
292         {
293             genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
294         }
295
296         if ( genericMetadata == null )
297         {
298             genericMetadata = new HashMap<String, String>();
299         }
300
301         return result;
302     }
303
304     public String addMetadataProperty()
305     {
306         String errorMsg = null;
307
308         ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
309         if ( projectMetadata == null )
310         {
311             addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
312             return ERROR;
313         }
314
315         if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
316         {
317             genericMetadata = new HashMap<String, String>();
318         }
319         else
320         {
321             genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
322         }
323
324         if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null ||
325             "".equals( propertyValue.trim() ) )
326         {
327             model = projectMetadata;
328             addActionError( errorMsg != null ? errorMsg : "Property Name and Property Value are required." );
329             return INPUT;
330         }
331
332         genericMetadata.put( propertyName, propertyValue );
333
334         updateProjectMetadata( projectMetadata );
335
336         projectMetadata = getProjectVersionMetadata();
337
338         genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
339
340         model = projectMetadata;
341         
342         propertyName = "";
343         propertyValue = "";
344
345         return SUCCESS;
346     }
347
348     public String deleteMetadataEntry()
349     {
350         ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
351         String errorMsg = null;
352        
353         if ( projectMetadata == null )
354         {
355             addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
356             return ERROR;
357         }
358
359         if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) != null )
360         {         
361             genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
362             
363             if ( !StringUtils.isEmpty( deleteItem ) )
364             {
365                 genericMetadata.remove( deleteItem );
366                 
367                 updateProjectMetadata( projectMetadata );
368                 
369                 projectMetadata = getProjectVersionMetadata();
370
371                 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
372
373                 model = projectMetadata;
374
375                 addActionMessage( "Property successfully deleted." );                
376             }
377             
378             deleteItem = "";           
379         }
380         else
381         {
382             addActionError( errorMsg != null ? errorMsg : "No generic metadata facet for this artifact." );
383             return ERROR;
384         }
385
386         return SUCCESS;
387     }
388
389     private void updateProjectMetadata( ProjectVersionMetadata projectMetadata )
390     {
391         GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
392         genericMetadataFacet.fromProperties( genericMetadata );
393         
394         projectMetadata.addFacet( genericMetadataFacet );
395         
396         metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );        
397     }
398
399     @Override
400     public void validate()
401     {
402         if ( StringUtils.isBlank( groupId ) )
403         {
404             addActionError( "You must specify a group ID to browse" );
405         }
406
407         if ( StringUtils.isBlank( artifactId ) )
408         {
409             addActionError( "You must specify a artifact ID to browse" );
410         }
411
412         if ( StringUtils.isBlank( version ) )
413         {
414             addActionError( "You must specify a version to browse" );
415         }
416     }
417
418     public ProjectVersionMetadata getModel()
419     {
420         return model;
421     }
422
423     public String getGroupId()
424     {
425         return groupId;
426     }
427
428     public void setGroupId( String groupId )
429     {
430         this.groupId = groupId;
431     }
432
433     public String getArtifactId()
434     {
435         return artifactId;
436     }
437
438     public void setArtifactId( String artifactId )
439     {
440         this.artifactId = artifactId;
441     }
442
443     public String getVersion()
444     {
445         return version;
446     }
447
448     public void setVersion( String version )
449     {
450         this.version = version;
451     }
452
453     public List<MailingList> getMailingLists()
454     {
455         return mailingLists;
456     }
457
458     public List<Dependency> getDependencies()
459     {
460         return dependencies;
461     }
462
463     public List<ProjectVersionReference> getDependees()
464     {
465         return dependees;
466     }
467
468     public String getRepositoryId()
469     {
470         return repositoryId;
471     }
472
473     public void setRepositoryId( String repositoryId )
474     {
475         this.repositoryId = repositoryId;
476     }
477
478     public MetadataResolver getMetadataResolver()
479     {
480         return metadataResolver;
481     }
482
483     public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
484     {
485         return artifacts;
486     }
487
488     public Collection<String> getSnapshotVersions()
489     {
490         return artifacts.keySet();
491     }
492     
493     public boolean isDependencyTree()
494     {
495         return dependencyTree;
496     }
497
498     public void setDeleteItem( String deleteItem )
499     {
500         this.deleteItem = deleteItem;
501     }
502
503     public Map<String, String> getGenericMetadata()
504     {
505         return genericMetadata;
506     }
507
508     public void setGenericMetadata( Map<String, String> genericMetadata )
509     {
510         this.genericMetadata = genericMetadata;
511     }
512
513     public String getPropertyName()
514     {
515         return propertyName;
516     }
517
518     public void setPropertyName( String propertyName )
519     {
520         this.propertyName = propertyName;
521     }
522
523     public String getPropertyValue()
524     {
525         return propertyValue;
526     }
527
528     public void setPropertyValue( String propertyValue )
529     {
530         this.propertyValue = propertyValue;
531     }
532     
533     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
534     {
535         this.repositoryFactory = repositoryFactory;
536     }
537
538     public void setMetadataRepository( MetadataRepository metadataRepository )
539     {
540         this.metadataRepository = metadataRepository;
541     }
542
543     // TODO: move this into the artifact metadata itself via facets where necessary
544
545     public class ArtifactDownloadInfo
546     {
547         private String type;
548
549         private String namespace;
550
551         private String project;
552
553         private String size;
554
555         private String id;
556
557         private String repositoryId;
558
559         private String version;
560
561         private String path;
562
563         public ArtifactDownloadInfo( ArtifactMetadata artifact )
564         {
565             repositoryId = artifact.getRepositoryId();
566
567             // TODO: use metadata resolver capability instead - maybe the storage path could be stored in the metadata
568             // though keep in mind the request may not necessarily need to reflect the storage
569             ManagedRepositoryContent repo;
570             try
571             {
572                 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
573             }
574             catch ( RepositoryException e )
575             {
576                 throw new RuntimeException( e );
577             }
578
579             ArtifactReference ref = new ArtifactReference();
580             ref.setArtifactId( artifact.getProject() );
581             ref.setGroupId( artifact.getNamespace() );
582             ref.setVersion( artifact.getVersion() );
583             path = repo.toPath( ref );
584             path = path.substring( 0, path.lastIndexOf( "/" ) + 1 ) + artifact.getId();
585
586             // TODO: need to accommodate Maven 1 layout too. Non-maven repository formats will need to generate this
587             // facet (perhaps on the fly) if wanting to display the Maven 2 elements on the Archiva pages
588             String type = null;
589             MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
590             if ( facet != null )
591             {
592                 type = facet.getType();
593             }
594             this.type = type;
595
596             namespace = artifact.getNamespace();
597             project = artifact.getProject();
598
599             // TODO: find a reusable formatter for this
600             double s = artifact.getSize();
601             String symbol = "b";
602             if ( s > 1024 )
603             {
604                 symbol = "K";
605                 s /= 1024;
606
607                 if ( s > 1024 )
608                 {
609                     symbol = "M";
610                     s /= 1024;
611
612                     if ( s > 1024 )
613                     {
614                         symbol = "G";
615                         s /= 1024;
616                     }
617                 }
618             }
619
620             size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
621             id = artifact.getId();
622             version = artifact.getVersion();
623         }
624
625         public String getNamespace()
626         {
627             return namespace;
628         }
629
630         public String getType()
631         {
632             return type;
633         }
634
635         public String getProject()
636         {
637             return project;
638         }
639
640         public String getSize()
641         {
642             return size;
643         }
644
645         public String getId()
646         {
647             return id;
648         }
649
650         public String getVersion()
651         {
652             return version;
653         }
654
655         public String getRepositoryId()
656         {
657             return repositoryId;
658         }
659
660         public String getPath()
661         {
662             return path;
663         }
664     }
665 }