]> source.dussan.org Git - archiva.git/blob
f75229ec884b18ad54c18b4db0e8f1553aa7da81
[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 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;
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.resolveProjectVersion( 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                     try
176                     {
177                         artifacts = new ArrayList<ArtifactMetadata>( metadataResolver.resolveArtifacts( repoId, groupId,
178                                                                                                         artifactId,
179                                                                                                         version ) );
180                     }
181                     catch ( MetadataResolutionException e )
182                     {
183                         addIncompleteModelWarning();
184
185                         artifacts = Collections.emptyList();
186                     }
187                     Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
188                     {
189                         public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
190                         {
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() );
197                         }
198                     } );
199
200                     for ( ArtifactMetadata artifact : artifacts )
201                     {
202                         List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
203                         if ( l == null )
204                         {
205                             l = new ArrayList<ArtifactDownloadInfo>();
206                             this.artifacts.put( artifact.getVersion(), l );
207                         }
208                         l.add( new ArtifactDownloadInfo( artifact ) );
209                     }
210                 }
211             }
212         }
213
214         return versionMetadata;
215     }
216
217     private void addIncompleteModelWarning()
218     {
219         addActionMessage(
220             "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
221     }
222
223     /**
224      * Show the artifact information tab.
225      */
226     public String dependencies()
227     {
228         String result = artifact();
229
230         this.dependencies = model.getDependencies();
231
232         return result;
233     }
234
235     /**
236      * Show the mailing lists information tab.
237      */
238     public String mailingLists()
239     {
240         String result = artifact();
241
242         this.mailingLists = model.getMailingLists();
243
244         return result;
245     }
246
247     /**
248      * Show the reports tab.
249      */
250     public String reports()
251     {
252         // TODO: hook up reports on project
253
254         return SUCCESS;
255     }
256
257     /**
258      * Show the dependees (other artifacts that depend on this project) tab.
259      */
260     public String dependees()
261         throws MetadataResolutionException
262     {
263         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
264         // TODO: what if we get duplicates across repositories?
265         for ( String repoId : getObservableRepos() )
266         {
267             // TODO: what about if we want to see this irrespective of version?
268             references.addAll( metadataResolver.resolveProjectReferences( repoId, groupId, artifactId, version ) );
269         }
270
271         this.dependees = references;
272
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)
276
277         return artifact();
278     }
279
280     /**
281      * Show the dependencies of this versioned project tab.
282      */
283     public String dependencyTree()
284     {
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
287
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)
291
292         // TODO: a bit ugly, should really be mapping all these results differently now
293         this.dependencyTree = true;
294
295         return artifact();
296     }
297
298     public String projectMetadata()
299     {
300         String result = artifact();
301
302         if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
303         {
304             genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
305         }
306
307         if ( genericMetadata == null )
308         {
309             genericMetadata = new HashMap<String, String>();
310         }
311
312         return result;
313     }
314
315     public String addMetadataProperty()
316     {
317         String errorMsg = null;
318
319         ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
320         if ( projectMetadata == null )
321         {
322             addActionError( errorMsg != null ? errorMsg : "Artifact not found" );
323             return ERROR;
324         }
325
326         if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
327         {
328             genericMetadata = new HashMap<String, String>();
329         }
330         else
331         {
332             genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
333         }
334
335         if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null || "".equals(
336             propertyValue.trim() ) )
337         {
338             model = projectMetadata;
339             addActionError( errorMsg != null ? errorMsg : "Property Name and Property Value are required." );
340             return INPUT;
341         }
342
343         genericMetadata.put( propertyName, propertyValue );
344
345         try
346         {
347             updateProjectMetadata( projectMetadata );
348         }
349         catch ( MetadataRepositoryException e )
350         {
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." );
353             return ERROR;
354         }
355
356         projectMetadata = getProjectVersionMetadata();
357
358         genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
359
360         model = projectMetadata;
361
362         propertyName = "";
363         propertyValue = "";
364
365         return SUCCESS;
366     }
367
368     public String deleteMetadataEntry()
369     {
370         ProjectVersionMetadata projectMetadata = getProjectVersionMetadata();
371         String errorMsg = null;
372
373         if ( projectMetadata == null )
374         {
375             addActionError( "Artifact not found" );
376             return ERROR;
377         }
378
379         if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) != null )
380         {
381             genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
382
383             if ( !StringUtils.isEmpty( deleteItem ) )
384             {
385                 genericMetadata.remove( deleteItem );
386
387                 try
388                 {
389                     updateProjectMetadata( projectMetadata );
390                 }
391                 catch ( MetadataRepositoryException e )
392                 {
393                     log.warn( "Unable to persist modified project metadata after removing entry: " + e.getMessage(),
394                               e );
395                     addActionError(
396                         "Unable to remove metadata item to underlying content storage - consult application logs." );
397                     return ERROR;
398                 }
399
400                 projectMetadata = getProjectVersionMetadata();
401
402                 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
403
404                 model = projectMetadata;
405
406                 addActionMessage( "Property successfully deleted." );
407             }
408
409             deleteItem = "";
410         }
411         else
412         {
413             addActionError( "No generic metadata facet for this artifact." );
414             return ERROR;
415         }
416
417         return SUCCESS;
418     }
419
420     private void updateProjectMetadata( ProjectVersionMetadata projectMetadata )
421         throws MetadataRepositoryException
422     {
423         GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
424         genericMetadataFacet.fromProperties( genericMetadata );
425
426         projectMetadata.addFacet( genericMetadataFacet );
427
428         metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
429     }
430
431     @Override
432     public void validate()
433     {
434         if ( StringUtils.isBlank( groupId ) )
435         {
436             addActionError( "You must specify a group ID to browse" );
437         }
438
439         if ( StringUtils.isBlank( artifactId ) )
440         {
441             addActionError( "You must specify a artifact ID to browse" );
442         }
443
444         if ( StringUtils.isBlank( version ) )
445         {
446             addActionError( "You must specify a version to browse" );
447         }
448     }
449
450     public ProjectVersionMetadata getModel()
451     {
452         return model;
453     }
454
455     public String getGroupId()
456     {
457         return groupId;
458     }
459
460     public void setGroupId( String groupId )
461     {
462         this.groupId = groupId;
463     }
464
465     public String getArtifactId()
466     {
467         return artifactId;
468     }
469
470     public void setArtifactId( String artifactId )
471     {
472         this.artifactId = artifactId;
473     }
474
475     public String getVersion()
476     {
477         return version;
478     }
479
480     public void setVersion( String version )
481     {
482         this.version = version;
483     }
484
485     public List<MailingList> getMailingLists()
486     {
487         return mailingLists;
488     }
489
490     public List<Dependency> getDependencies()
491     {
492         return dependencies;
493     }
494
495     public List<ProjectVersionReference> getDependees()
496     {
497         return dependees;
498     }
499
500     public String getRepositoryId()
501     {
502         return repositoryId;
503     }
504
505     public void setRepositoryId( String repositoryId )
506     {
507         this.repositoryId = repositoryId;
508     }
509
510     public MetadataResolver getMetadataResolver()
511     {
512         return metadataResolver;
513     }
514
515     public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
516     {
517         return artifacts;
518     }
519
520     public Collection<String> getSnapshotVersions()
521     {
522         return artifacts.keySet();
523     }
524
525     public boolean isDependencyTree()
526     {
527         return dependencyTree;
528     }
529
530     public void setDeleteItem( String deleteItem )
531     {
532         this.deleteItem = deleteItem;
533     }
534
535     public Map<String, String> getGenericMetadata()
536     {
537         return genericMetadata;
538     }
539
540     public void setGenericMetadata( Map<String, String> genericMetadata )
541     {
542         this.genericMetadata = genericMetadata;
543     }
544
545     public String getPropertyName()
546     {
547         return propertyName;
548     }
549
550     public void setPropertyName( String propertyName )
551     {
552         this.propertyName = propertyName;
553     }
554
555     public String getPropertyValue()
556     {
557         return propertyValue;
558     }
559
560     public void setPropertyValue( String propertyValue )
561     {
562         this.propertyValue = propertyValue;
563     }
564
565     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
566     {
567         this.repositoryFactory = repositoryFactory;
568     }
569
570     public void setMetadataRepository( MetadataRepository metadataRepository )
571     {
572         this.metadataRepository = metadataRepository;
573     }
574
575     // TODO: move this into the artifact metadata itself via facets where necessary
576
577     public class ArtifactDownloadInfo
578     {
579         private String type;
580
581         private String namespace;
582
583         private String project;
584
585         private String size;
586
587         private String id;
588
589         private String repositoryId;
590
591         private String version;
592
593         private String path;
594
595         public ArtifactDownloadInfo( ArtifactMetadata artifact )
596         {
597             repositoryId = artifact.getRepositoryId();
598
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;
602             try
603             {
604                 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
605             }
606             catch ( RepositoryException e )
607             {
608                 throw new RuntimeException( e );
609             }
610
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();
617
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
620             String type = null;
621             MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
622             if ( facet != null )
623             {
624                 type = facet.getType();
625             }
626             this.type = type;
627
628             namespace = artifact.getNamespace();
629             project = artifact.getProject();
630
631             // TODO: find a reusable formatter for this
632             double s = artifact.getSize();
633             String symbol = "b";
634             if ( s > 1024 )
635             {
636                 symbol = "K";
637                 s /= 1024;
638
639                 if ( s > 1024 )
640                 {
641                     symbol = "M";
642                     s /= 1024;
643
644                     if ( s > 1024 )
645                     {
646                         symbol = "G";
647                         s /= 1024;
648                     }
649                 }
650             }
651
652             size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
653             id = artifact.getId();
654             version = artifact.getVersion();
655         }
656
657         public String getNamespace()
658         {
659             return namespace;
660         }
661
662         public String getType()
663         {
664             return type;
665         }
666
667         public String getProject()
668         {
669             return project;
670         }
671
672         public String getSize()
673         {
674             return size;
675         }
676
677         public String getId()
678         {
679             return id;
680         }
681
682         public String getVersion()
683         {
684             return version;
685         }
686
687         public String getRepositoryId()
688         {
689             return repositoryId;
690         }
691
692         public String getPath()
693         {
694             return path;
695         }
696     }
697 }