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