]> source.dussan.org Git - archiva.git/blob
dfa486870d395aa6937c6e16734f3f6f4fd8fb8c
[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.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;
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 @SuppressWarnings( "serial" )
61 public class ShowArtifactAction
62     extends AbstractRepositoryBasedAction
63     implements Validateable
64 {
65     /* .\ Not Exposed \._____________________________________________ */
66
67     /**
68      * @plexus.requirement
69      */
70     private RepositoryContentFactory repositoryFactory;
71
72     /* .\ Exposed Output Objects \.__________________________________ */
73
74     private String groupId;
75
76     private String artifactId;
77
78     private String version;
79
80     private String repositoryId;
81
82     /**
83      * The model of this versioned project.
84      */
85     private ProjectVersionMetadata model;
86
87     /**
88      * The list of artifacts that depend on this versioned project.
89      */
90     private List<ProjectVersionReference> dependees;
91
92     private List<MailingList> mailingLists;
93
94     private List<Dependency> dependencies;
95
96     private Map<String, List<ArtifactDownloadInfo>> artifacts;
97
98     private boolean dependencyTree = false;
99
100     private String deleteItem;
101
102     private Map<String, String> genericMetadata;
103
104     private String propertyName;
105
106     private String propertyValue;
107
108     /**
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).
111      */
112     public String artifact()
113     {
114         RepositorySession repositorySession = repositorySessionFactory.createSession();
115         try
116         {
117             return handleArtifact( repositorySession );
118         }
119         finally
120         {
121             repositorySession.close();
122         }
123     }
124
125     private String handleArtifact( RepositorySession session )
126     {
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 );
130
131         if ( versionMetadata == null )
132         {
133             addActionError( "Artifact not found" );
134             return ERROR;
135         }
136
137         if ( versionMetadata.isIncomplete() )
138         {
139             addIncompleteModelWarning();
140         }
141
142         model = versionMetadata;
143
144         return SUCCESS;
145     }
146
147     private ProjectVersionMetadata getProjectVersionMetadata( RepositorySession session )
148     {
149         ProjectVersionMetadata versionMetadata = null;
150         artifacts = new LinkedHashMap<String, List<ArtifactDownloadInfo>>();
151
152         List<String> repos = getObservableRepos();
153
154         MetadataResolver metadataResolver = session.getResolver();
155         for ( String repoId : repos )
156         {
157             if ( versionMetadata == null )
158             {
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
161                 try
162                 {
163                     versionMetadata = metadataResolver.resolveProjectVersion( session, repoId, groupId, artifactId,
164                                                                               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                     try
180                     {
181                         artifacts = new ArrayList<ArtifactMetadata>( metadataResolver.resolveArtifacts( session, repoId,
182                                                                                                         groupId,
183                                                                                                         artifactId,
184                                                                                                         version ) );
185                     }
186                     catch ( MetadataResolutionException e )
187                     {
188                         addIncompleteModelWarning();
189
190                         artifacts = Collections.emptyList();
191                     }
192                     Collections.sort( artifacts, new Comparator<ArtifactMetadata>()
193                     {
194                         public int compare( ArtifactMetadata o1, ArtifactMetadata o2 )
195                         {
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() );
202                         }
203                     } );
204
205                     for ( ArtifactMetadata artifact : artifacts )
206                     {
207                         List<ArtifactDownloadInfo> l = this.artifacts.get( artifact.getVersion() );
208                         if ( l == null )
209                         {
210                             l = new ArrayList<ArtifactDownloadInfo>();
211                             this.artifacts.put( artifact.getVersion(), l );
212                         }
213                         l.add( new ArtifactDownloadInfo( artifact ) );
214                     }
215                 }
216             }
217         }
218
219         return versionMetadata;
220     }
221
222     private void addIncompleteModelWarning()
223     {
224         addActionMessage(
225             "The model may be incomplete due to a previous error in resolving information. Refer to the repository problem reports for more information." );
226     }
227
228     /**
229      * Show the artifact information tab.
230      */
231     public String dependencies()
232     {
233         String result = artifact();
234
235         this.dependencies = model.getDependencies();
236
237         return result;
238     }
239
240     /**
241      * Show the mailing lists information tab.
242      */
243     public String mailingLists()
244     {
245         String result = artifact();
246
247         this.mailingLists = model.getMailingLists();
248
249         return result;
250     }
251
252     /**
253      * Show the reports tab.
254      */
255     public String reports()
256     {
257         // TODO: hook up reports on project
258
259         return SUCCESS;
260     }
261
262     /**
263      * Show the dependees (other artifacts that depend on this project) tab.
264      */
265     public String dependees()
266         throws MetadataResolutionException
267     {
268         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
269         // TODO: what if we get duplicates across repositories?
270         RepositorySession repositorySession = repositorySessionFactory.createSession();
271         try
272         {
273             MetadataResolver metadataResolver = repositorySession.getResolver();
274             for ( String repoId : getObservableRepos() )
275             {
276                 // TODO: what about if we want to see this irrespective of version?
277                 references.addAll( metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId,
278                                                                               artifactId, version ) );
279             }
280         }
281         finally
282         {
283             repositorySession.close();
284         }
285
286         this.dependees = references;
287
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)
291
292         return artifact();
293     }
294
295     /**
296      * Show the dependencies of this versioned project tab.
297      */
298     public String dependencyTree()
299     {
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
302
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)
306
307         // TODO: a bit ugly, should really be mapping all these results differently now
308         this.dependencyTree = true;
309
310         return artifact();
311     }
312
313     public String projectMetadata()
314     {
315         String result = artifact();
316
317         if ( model.getFacet( GenericMetadataFacet.FACET_ID ) != null )
318         {
319             genericMetadata = model.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
320         }
321
322         if ( genericMetadata == null )
323         {
324             genericMetadata = new HashMap<String, String>();
325         }
326
327         return result;
328     }
329
330     public String addMetadataProperty()
331     {
332         RepositorySession repositorySession = repositorySessionFactory.createSession();
333         ProjectVersionMetadata projectMetadata;
334         try
335         {
336             MetadataRepository metadataRepository = repositorySession.getRepository();
337             projectMetadata = getProjectVersionMetadata( repositorySession );
338             if ( projectMetadata == null )
339             {
340                 addActionError( "Artifact not found" );
341                 return ERROR;
342             }
343
344             if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) == null )
345             {
346                 genericMetadata = new HashMap<String, String>();
347             }
348             else
349             {
350                 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
351             }
352
353             if ( propertyName == null || "".equals( propertyName.trim() ) || propertyValue == null || "".equals(
354                 propertyValue.trim() ) )
355             {
356                 model = projectMetadata;
357                 addActionError( "Property Name and Property Value are required." );
358                 return INPUT;
359             }
360
361             genericMetadata.put( propertyName, propertyValue );
362
363             try
364             {
365                 updateProjectMetadata( projectMetadata, metadataRepository );
366             }
367             catch ( MetadataRepositoryException e )
368             {
369                 log.warn( "Unable to persist modified project metadata after adding entry: " + e.getMessage(), e );
370                 addActionError(
371                     "Unable to add metadata item to underlying content storage - consult application logs." );
372                 return ERROR;
373             }
374
375             // TODO: why re-retrieve?
376             projectMetadata = getProjectVersionMetadata( repositorySession );
377         }
378         finally
379         {
380             repositorySession.close();
381         }
382
383         genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
384
385         model = projectMetadata;
386
387         propertyName = "";
388         propertyValue = "";
389
390         return SUCCESS;
391     }
392
393     public String deleteMetadataEntry()
394     {
395         RepositorySession repositorySession = repositorySessionFactory.createSession();
396         try
397         {
398             MetadataRepository metadataRepository = repositorySession.getRepository();
399             ProjectVersionMetadata projectMetadata = getProjectVersionMetadata( repositorySession );
400
401             if ( projectMetadata == null )
402             {
403                 addActionError( "Artifact not found" );
404                 return ERROR;
405             }
406
407             if ( projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ) != null )
408             {
409                 genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
410
411                 if ( !StringUtils.isEmpty( deleteItem ) )
412                 {
413                     genericMetadata.remove( deleteItem );
414
415                     try
416                     {
417                         updateProjectMetadata( projectMetadata, metadataRepository );
418                     }
419                     catch ( MetadataRepositoryException e )
420                     {
421                         log.warn( "Unable to persist modified project metadata after removing entry: " + e.getMessage(),
422                                   e );
423                         addActionError(
424                             "Unable to remove metadata item to underlying content storage - consult application logs." );
425                         return ERROR;
426                     }
427
428                     // TODO: why re-retrieve?
429                     projectMetadata = getProjectVersionMetadata( repositorySession );
430
431                     genericMetadata = projectMetadata.getFacet( GenericMetadataFacet.FACET_ID ).toProperties();
432
433                     model = projectMetadata;
434
435                     addActionMessage( "Property successfully deleted." );
436                 }
437
438                 deleteItem = "";
439             }
440             else
441             {
442                 addActionError( "No generic metadata facet for this artifact." );
443                 return ERROR;
444             }
445         }
446         finally
447         {
448             repositorySession.close();
449         }
450
451         return SUCCESS;
452     }
453
454     private void updateProjectMetadata( ProjectVersionMetadata projectMetadata, MetadataRepository metadataRepository )
455         throws MetadataRepositoryException
456     {
457         GenericMetadataFacet genericMetadataFacet = new GenericMetadataFacet();
458         genericMetadataFacet.fromProperties( genericMetadata );
459
460         projectMetadata.addFacet( genericMetadataFacet );
461
462         metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectMetadata );
463     }
464
465     @Override
466     public void validate()
467     {
468         if ( StringUtils.isBlank( groupId ) )
469         {
470             addActionError( "You must specify a group ID to browse" );
471         }
472
473         if ( StringUtils.isBlank( artifactId ) )
474         {
475             addActionError( "You must specify a artifact ID to browse" );
476         }
477
478         if ( StringUtils.isBlank( version ) )
479         {
480             addActionError( "You must specify a version to browse" );
481         }
482     }
483
484     public ProjectVersionMetadata getModel()
485     {
486         return model;
487     }
488
489     public String getGroupId()
490     {
491         return groupId;
492     }
493
494     public void setGroupId( String groupId )
495     {
496         this.groupId = groupId;
497     }
498
499     public String getArtifactId()
500     {
501         return artifactId;
502     }
503
504     public void setArtifactId( String artifactId )
505     {
506         this.artifactId = artifactId;
507     }
508
509     public String getVersion()
510     {
511         return version;
512     }
513
514     public void setVersion( String version )
515     {
516         this.version = version;
517     }
518
519     public List<MailingList> getMailingLists()
520     {
521         return mailingLists;
522     }
523
524     public List<Dependency> getDependencies()
525     {
526         return dependencies;
527     }
528
529     public List<ProjectVersionReference> getDependees()
530     {
531         return dependees;
532     }
533
534     public String getRepositoryId()
535     {
536         return repositoryId;
537     }
538
539     public void setRepositoryId( String repositoryId )
540     {
541         this.repositoryId = repositoryId;
542     }
543
544     public Map<String, List<ArtifactDownloadInfo>> getArtifacts()
545     {
546         return artifacts;
547     }
548
549     public Collection<String> getSnapshotVersions()
550     {
551         return artifacts.keySet();
552     }
553
554     public boolean isDependencyTree()
555     {
556         return dependencyTree;
557     }
558
559     public void setDeleteItem( String deleteItem )
560     {
561         this.deleteItem = deleteItem;
562     }
563
564     public Map<String, String> getGenericMetadata()
565     {
566         return genericMetadata;
567     }
568
569     public void setGenericMetadata( Map<String, String> genericMetadata )
570     {
571         this.genericMetadata = genericMetadata;
572     }
573
574     public String getPropertyName()
575     {
576         return propertyName;
577     }
578
579     public void setPropertyName( String propertyName )
580     {
581         this.propertyName = propertyName;
582     }
583
584     public String getPropertyValue()
585     {
586         return propertyValue;
587     }
588
589     public void setPropertyValue( String propertyValue )
590     {
591         this.propertyValue = propertyValue;
592     }
593
594     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
595     {
596         this.repositoryFactory = repositoryFactory;
597     }
598
599     // TODO: move this into the artifact metadata itself via facets where necessary
600
601     public class ArtifactDownloadInfo
602     {
603         private String type;
604
605         private String namespace;
606
607         private String project;
608
609         private String size;
610
611         private String id;
612
613         private String repositoryId;
614
615         private String version;
616
617         private String path;
618
619         public ArtifactDownloadInfo( ArtifactMetadata artifact )
620         {
621             repositoryId = artifact.getRepositoryId();
622
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;
626             try
627             {
628                 repo = repositoryFactory.getManagedRepositoryContent( repositoryId );
629             }
630             catch ( RepositoryException e )
631             {
632                 throw new RuntimeException( e );
633             }
634
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();
641
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
644             String type = null;
645             MavenArtifactFacet facet = (MavenArtifactFacet) artifact.getFacet( MavenArtifactFacet.FACET_ID );
646             if ( facet != null )
647             {
648                 type = facet.getType();
649             }
650             this.type = type;
651
652             namespace = artifact.getNamespace();
653             project = artifact.getProject();
654
655             // TODO: find a reusable formatter for this
656             double s = artifact.getSize();
657             String symbol = "b";
658             if ( s > 1024 )
659             {
660                 symbol = "K";
661                 s /= 1024;
662
663                 if ( s > 1024 )
664                 {
665                     symbol = "M";
666                     s /= 1024;
667
668                     if ( s > 1024 )
669                     {
670                         symbol = "G";
671                         s /= 1024;
672                     }
673                 }
674             }
675
676             size = new DecimalFormat( "#,###.##" ).format( s ) + " " + symbol;
677             id = artifact.getId();
678             version = artifact.getVersion();
679         }
680
681         public String getNamespace()
682         {
683             return namespace;
684         }
685
686         public String getType()
687         {
688             return type;
689         }
690
691         public String getProject()
692         {
693             return project;
694         }
695
696         public String getSize()
697         {
698             return size;
699         }
700
701         public String getId()
702         {
703             return id;
704         }
705
706         public String getVersion()
707         {
708             return version;
709         }
710
711         public String getRepositoryId()
712         {
713             return repositoryId;
714         }
715
716         public String getPath()
717         {
718             return path;
719         }
720     }
721 }