]> source.dussan.org Git - archiva.git/blob
591815986fa0f1e284400ceab9d60ad37c7f8ce2
[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.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;
41
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;
50 import java.util.Map;
51
52 /**
53  * Browse the repository. 
54  * 
55  * TODO change name to ShowVersionedAction to conform to terminology.
56  * 
57  * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="showArtifactAction"
58  *                   instantiation-strategy="per-lookup"
59  */
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 ProjectVersionMetadata projectMetadata;
110
111     private String deleteItem;
112
113     private String itemValue;
114
115     private Map<String, String> genericMetadata;
116
117     private String propertyName;
118
119     private String propertyValue;
120
121     /**
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).
124      */
125     public String artifact()
126     {
127
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();
132
133         if ( versionMetadata == null )
134         {
135             addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
136             return ERROR;
137         }
138
139         if ( versionMetadata.isIncomplete() )
140         {
141             addIncompleteModelWarning();
142         }
143
144         model = versionMetadata;
145
146         return SUCCESS;
147     }
148
149     private ProjectVersionMetadata getProjectVersionMetadata()
150     {
151         ProjectVersionMetadata versionMetadata = null;
152         artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
153
154         List<String> repos = getObservableRepos();
155
156         for ( String repoId : repos )
157         {
158             if ( versionMetadata == null )
159             {
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
162                 try
163                 {
164                     versionMetadata = metadataResolver.getProjectVersion( repoId, groupId, artifactId, version );
165                 }
166                 catch ( MetadataResolutionException e )
167                 {
168                     addIncompleteModelWarning();
169
170                     // TODO: need a consistent way to construct this - same in ArchivaMetadataCreationConsumer
171                     versionMetadata = new ProjectVersionMetadata();
172                     versionMetadata.setId( version );
173                 }
174                 if ( versionMetadata != null )
175                 {
176                     repositoryId = repoId;
177
178                     List<ArtifactMetadata> artifacts =
179                         new ArrayList<ArtifactMetadata>( metadataResolver.getArtifacts( repoId, groupId, artifactId,
180                                                                                         version ) );
181                     Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
182                     {
183                         public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
184                         {
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
188                             int result =
189                                 new DefaultArtifactVersion( o2.getVersion() ).compareTo( new DefaultArtifactVersion(
190                                                                                                                      o1.getVersion() ) );
191                             return result != 0 ? result : o1.getId().compareTo( o2.getId() );
192                         }
193                     } );
194
195                     for ( ArtifactMetadata artifact : artifacts )
196                     {
197                         List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
198                         if ( l == null )
199                         {
200                             l = new ArrayList<ArtifactDownloadInfo>();
201                             this.artifacts.put( artifact.getVersion(), l );
202                         }
203                         l.add( new ArtifactDownloadInfo( artifact ) );
204                     }
205                 }
206             }
207         }
208
209         return versionMetadata;
210     }
211
212     private void addIncompleteModelWarning()
213     {
214         addActionMessage( "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
215     }
216
217     /**
218      * Show the artifact information tab.
219      */
220     public String dependencies()
221     {
222         String result = artifact();
223
224         this.dependencies = model.getDependencies();
225
226         return result;
227     }
228
229     /**
230      * Show the mailing lists information tab.
231      */
232     public String mailingLists()
233     {
234         String result = artifact();
235
236         this.mailingLists = model.getMailingLists();
237
238         return result;
239     }
240
241     /**
242      * Show the reports tab.
243      */
244     public String reports()
245     {
246         // TODO: hook up reports on project
247
248         return SUCCESS;
249     }
250
251     /**
252      * Show the dependees (other artifacts that depend on this project) tab.
253      */
254     public String dependees()
255     {
256         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
257         // TODO: what if we get duplicates across repositories?
258         for ( String repoId : getObservableRepos() )
259         {
260             // TODO: what about if we want to see this irrespective of version?
261             references.addAll( metadataResolver.getProjectReferences( repoId, groupId, artifactId, version ) );
262         }
263
264         this.dependees = references;
265
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)
269
270         return artifact();
271     }
272
273     /**
274      * Show the dependencies of this versioned project tab.
275      */
276     public String dependencyTree()
277     {
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
280
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)
284
285         // TODO: a bit ugly, should really be mapping all these results differently now
286         this.dependencyTree = true;
287
288         return artifact();
289     }
290
291     public String projectMetadata()
292     {
293         String result = artifact();
294
295         if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
296         {
297             genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
298         }
299
300         if ( genericMetadata == null )
301         {
302             genericMetadata = new HashMap<String, String>();
303         }
304
305         return result;
306     }
307
308     public String addMetadataProperty()
309     {
310         String errorMsg = null;
311
312         ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
313         if ( projectMetadata == null )
314         {
315             addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
316             return ERROR;
317         }
318
319         if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
320         {
321             genericMetadata = new HashMap<String, String>();
322         }
323         else
324         {
325             genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
326         }
327
328         if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null ||
329             "".equals( propertyValue.trim() ) )
330         {
331             model = projectMetadata;
332             addActionError( errorMsg != null ? errorMsg : "Property Name and Property Value are required." );
333             return INPUT;
334         }
335
336         genericMetadata.put( propertyName, propertyValue );
337
338         GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
339         genericMetadataFacet.fromProperties( genericMetadata );
340
341         // add updated facet
342         projectMetadata.addFacet( genericMetadataFacet );
343
344         metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
345
346         projectMetadata = getProjectVersionMetadata();
347
348         genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
349
350         model = projectMetadata;
351
352         propertyName = "";
353         propertyValue = "";
354
355         return SUCCESS;
356     }
357
358     public String updateProjectMetadata()
359     {
360         metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
361
362         return SUCCESS;
363     }
364
365     public String deleteMetadataEntry()
366     {
367         projectMetadata = getProjectVersionMetadata();
368
369         if ( !StringUtils.isEmpty( deleteItem ) && !StringUtils.isEmpty( itemValue ) )
370         {
371             if ( "dependency".equals( deleteItem ) )
372             {
373                 removeDependency();
374             }
375             else if ( "mailingList".equals( deleteItem ) )
376             {
377                 removeMailingList();
378             }
379             else if ( "license".equals( deleteItem ) )
380             {
381                 removeLicense();
382             }
383
384             deleteItem = "";
385             itemValue = "";
386         }
387
388         return updateProjectMetadata();
389     }
390
391     private void removeDependency()
392     {
393         List<Dependency> dependencies = projectMetadata.getDependencies();
394         List<Dependency> newDependencies = new ArrayList<Dependency>();
395
396         if ( dependencies != null )
397         {
398             for ( Dependency dependency : dependencies )
399             {
400                 if ( !StringUtils.equals( itemValue, dependency.getArtifactId() ) )
401                 {
402                     newDependencies.add( dependency );
403                 }
404             }
405         }
406
407         projectMetadata.setDependencies( newDependencies );
408     }
409
410     private void removeMailingList()
411     {
412         List<MailingList> mailingLists = projectMetadata.getMailingLists();
413         List<MailingList> newMailingLists = new ArrayList<MailingList>();
414
415         if ( mailingLists != null )
416         {
417             for ( MailingList mailingList : mailingLists )
418             {
419                 if ( !StringUtils.equals( itemValue, mailingList.getName() ) )
420                 {
421                     newMailingLists.add( mailingList );
422                 }
423             }
424         }
425
426         projectMetadata.setMailingLists( newMailingLists );
427     }
428
429     private void removeLicense()
430     {
431         List<License> licenses = projectMetadata.getLicenses();
432         List<License> newLicenses = new ArrayList<License>();
433
434         if ( licenses != null )
435         {
436             for ( License license : licenses )
437             {
438                 if ( !StringUtils.equals( itemValue, license.getName() ) )
439                 {
440                     newLicenses.add( license );
441                 }
442             }
443         }
444
445         projectMetadata.setLicenses( newLicenses );
446     }
447
448     @Override
449     public void validate()
450     {
451         if ( StringUtils.isBlank( groupId ) )
452         {
453             addActionError( "You must specify a group ID to browse" );
454         }
455
456         if ( StringUtils.isBlank( artifactId ) )
457         {
458             addActionError( "You must specify a artifact ID to browse" );
459         }
460
461         if ( StringUtils.isBlank( version ) )
462         {
463             addActionError( "You must specify a version to browse" );
464         }
465     }
466
467     public ProjectVersionMetadata getModel()
468     {
469         return model;
470     }
471
472     public String getGroupId()
473     {
474         return groupId;
475     }
476
477     public void setGroupId( String groupId )
478     {
479         this.groupId = groupId;
480     }
481
482     public String getArtifactId()
483     {
484         return artifactId;
485     }
486
487     public void setArtifactId( String artifactId )
488     {
489         this.artifactId = artifactId;
490     }
491
492     public String getVersion()
493     {
494         return version;
495     }
496
497     public void setVersion( String version )
498     {
499         this.version = version;
500     }
501
502     public List<MailingList> getMailingLists()
503     {
504         return mailingLists;
505     }
506
507     public List<Dependency> getDependencies()
508     {
509         return dependencies;
510     }
511
512     public List<ProjectVersionReference> getDependees()
513     {
514         return dependees;
515     }
516
517     public String getRepositoryId()
518     {
519         return repositoryId;
520     }
521
522     public void setRepositoryId( String repositoryId )
523     {
524         this.repositoryId = repositoryId;
525     }
526
527     public MetadataResolver getMetadataResolver()
528     {
529         return metadataResolver;
530     }
531
532     public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
533     {
534         return artifacts;
535     }
536
537     public Collection<String> getSnapshotVersions()
538     {
539         return artifacts.keySet();
540     }
541
542     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
543     {
544         this.repositoryFactory = repositoryFactory;
545     }
546
547     public boolean isDependencyTree()
548     {
549         return dependencyTree;
550     }
551
552     public ProjectVersionMetadata getProjectMetadata()
553     {
554         return projectMetadata;
555     }
556
557     public void setProjectMetadata( ProjectVersionMetadata projectMetadata )
558     {
559         this.projectMetadata = projectMetadata;
560     }
561
562     public void setDeleteItem( String deleteItem )
563     {
564         this.deleteItem = deleteItem;
565     }
566
567     public void setItemValue( String itemValue )
568     {
569         this.itemValue = itemValue;
570     }
571
572     public Map<String, String> getGenericMetadata()
573     {
574         return genericMetadata;
575     }
576
577     public void setGenericMetadata( Map<String, String> genericMetadata )
578     {
579         this.genericMetadata = genericMetadata;
580     }
581
582     public String getPropertyName()
583     {
584         return propertyName;
585     }
586
587     public void setPropertyName( String propertyName )
588     {
589         this.propertyName = propertyName;
590     }
591
592     public String getPropertyValue()
593     {
594         return propertyValue;
595     }
596
597     public void setPropertyValue( String propertyValue )
598     {
599         this.propertyValue = propertyValue;
600     }
601
602     // TODO: move this into the artifact metadata itself via facets where necessary
603
604     public class ArtifactDownloadInfo
605     {
606         private String type;
607
608         private String namespace;
609
610         private String project;
611
612         private String size;
613
614         private String id;
615
616         private String repositoryId;
617
618         private String version;
619
620         private String path;
621
622         public ArtifactDownloadInfo( ArtifactMetadata artifact )
623         {
624             repositoryId = artifact.getRepositoryId();
625
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;
629             try
630             {
631                 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
632             }
633             catch ( RepositoryException e )
634             {
635                 throw new RuntimeException( e );
636             }
637
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();
644
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
647             String type = null;
648             MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
649             if ( facet != null )
650             {
651                 type = facet.getType();
652             }
653             this.type = type;
654
655             namespace = artifact.getNamespace();
656             project = artifact.getProject();
657
658             // TODO: find a reusable formatter for this
659             double s = artifact.getSize();
660             String symbol = "b";
661             if ( s > 1024 )
662             {
663                 symbol = "K";
664                 s /= 1024;
665
666                 if ( s > 1024 )
667                 {
668                     symbol = "M";
669                     s /= 1024;
670
671                     if ( s > 1024 )
672                     {
673                         symbol = "G";
674                         s /= 1024;
675                     }
676                 }
677             }
678
679             size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
680             id = artifact.getId();
681             version = artifact.getVersion();
682         }
683
684         public String getNamespace()
685         {
686             return namespace;
687         }
688
689         public String getType()
690         {
691             return type;
692         }
693
694         public String getProject()
695         {
696             return project;
697         }
698
699         public String getSize()
700         {
701             return size;
702         }
703
704         public String getId()
705         {
706             return id;
707         }
708
709         public String getVersion()
710         {
711             return version;
712         }
713
714         public String getRepositoryId()
715         {
716             return repositoryId;
717         }
718
719         public String getPath()
720         {
721             return path;
722         }
723     }
724 }