]> source.dussan.org Git - archiva.git/blob
186b7d1a40c792245841ff73218789b7d9ce375b
[archiva.git] /
1 package org.apache.maven.archiva.repository.project.filters;
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 org.apache.commons.collections.CollectionUtils;
23 import org.apache.commons.lang.StringUtils;
24 import org.apache.maven.archiva.model.ArchivaModelCloner;
25 import org.apache.maven.archiva.model.ArchivaProjectModel;
26 import org.apache.maven.archiva.model.Dependency;
27 import org.apache.maven.archiva.model.IssueManagement;
28 import org.apache.maven.archiva.model.VersionedReference;
29 import org.apache.maven.archiva.repository.project.ProjectModelException;
30 import org.apache.maven.archiva.repository.project.ProjectModelFilter;
31 import org.apache.maven.archiva.repository.project.ProjectModelMerge;
32 import org.apache.maven.archiva.repository.project.ProjectModelResolverFactory;
33 import org.codehaus.plexus.cache.Cache;
34
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39
40 /**
41  * Builder for the Effective Project Model.  
42  *
43  * @version $Id$
44  * @plexus.component role="org.apache.maven.archiva.repository.project.ProjectModelFilter" 
45  *                   role-hint="effective" 
46  */
47 public class EffectiveProjectModelFilter
48     implements ProjectModelFilter
49 {
50     private ProjectModelFilter expressionFilter = new ProjectModelExpressionFilter();
51
52     /**
53      * @plexus.requirement
54      */
55     private ProjectModelResolverFactory resolverFactory;
56
57     /**
58      * @plexus.requirement role-hint="effective-project-cache"
59      */
60     private Cache effectiveProjectCache;
61
62     /**
63      * Take the provided {@link ArchivaProjectModel} and build the effective {@link ArchivaProjectModel}.
64      * 
65      * Steps:
66      * 1) Expand any expressions / properties.
67      * 2) Walk the parent project references and merge.
68      * 3) Apply dependency management settings.
69      * 
70      * @param project the project to create the effective {@link ArchivaProjectModel} from.
71      * @return a the effective {@link ArchivaProjectModel}.
72      * @throws ProjectModelException if there was a problem building the effective pom.
73      */
74     public ArchivaProjectModel filter( final ArchivaProjectModel project )
75         throws ProjectModelException
76     {
77         if ( project == null )
78         {
79             return null;
80         }
81
82         if ( resolverFactory.getCurrentResolverStack().isEmpty() )
83         {
84             throw new IllegalStateException( "Unable to build effective pom with no project model resolvers defined." );
85         }
86
87         ArchivaProjectModel effectiveProject;
88         String projectKey = toProjectKey( project );
89
90         synchronized ( effectiveProjectCache )
91         {
92             if ( effectiveProjectCache.hasKey( projectKey ) )
93             {
94                 DEBUG( "Fetching (from cache/projectKey): " + projectKey );
95                 effectiveProject = (ArchivaProjectModel) effectiveProjectCache.get( projectKey );
96                 return effectiveProject;
97             }
98         }
99
100         // Clone submitted project (so that we don't mess with it) 
101         effectiveProject = ArchivaModelCloner.clone( project );
102         
103         System.out.println( "++++[EFFECTIVE Project] effectiveProject --> " + effectiveProject  );
104         IssueManagement iM = effectiveProject.getIssueManagement();
105         if( iM != null )
106         {
107             System.out.println( "++++[EFFECTIVE Project] issue mgnt URL --> " + iM.getUrl() + " $$ " +
108                 iM.getIssueManagementUrl() );
109         }
110
111         DEBUG( "Starting build of effective with: " + effectiveProject );
112
113         // Merge in all the parent poms.
114         effectiveProject = mergeParent( effectiveProject );
115
116         // Setup Expression Evaluation pieces.
117         effectiveProject = expressionFilter.filter( effectiveProject );
118
119         // Resolve dependency versions from dependency management.
120         applyDependencyManagement( effectiveProject );
121
122         // groupId or version could be updated by parent or expressions
123         projectKey = toProjectKey( effectiveProject );
124         
125         // Do not add project into cache if it contains no groupId and
126         // version information
127         if ( effectiveProject.getGroupId() != null && effectiveProject.getVersion() != null )
128         {
129             synchronized ( effectiveProjectCache )
130             {
131                 DEBUG( "Putting (to cache/projectKey): " + projectKey );
132                 effectiveProjectCache.put( projectKey, effectiveProject );
133             }
134         }
135
136         // Return what we got.
137         return effectiveProject;
138     }
139
140     private void applyDependencyManagement( ArchivaProjectModel pom )
141     {
142         if ( CollectionUtils.isEmpty( pom.getDependencyManagement() )
143             || CollectionUtils.isEmpty( pom.getDependencies() ) )
144         {
145             // Nothing to do. All done!
146             return;
147         }
148
149         Map<String, Dependency> managedDependencies = createDependencyMap( pom.getDependencyManagement() );
150         Iterator<Dependency> it = pom.getDependencies().iterator();
151         while ( it.hasNext() )
152         {
153             Dependency dep = it.next();
154             String key = toVersionlessDependencyKey( dep );
155
156             // Do we need to do anything?
157             if ( managedDependencies.containsKey( key ) )
158             {
159                 Dependency mgmtDep = (Dependency) managedDependencies.get( key );
160
161                 dep.setVersion( mgmtDep.getVersion() );
162                 dep.setScope( mgmtDep.getScope() );
163                 dep.setExclusions( ProjectModelMerge.mergeExclusions( dep.getExclusions(), mgmtDep.getExclusions() ) );
164             }
165         }
166     }
167
168     private ArchivaProjectModel mergeParent( ArchivaProjectModel pom )
169         throws ProjectModelException
170     {
171         ArchivaProjectModel mixedProject;
172
173         DEBUG( "Project: " + toProjectKey( pom ) );
174
175         if ( pom.getParentProject() != null )
176         {
177             // Use parent reference.
178             VersionedReference parentRef = pom.getParentProject();
179
180             String parentKey = VersionedReference.toKey( parentRef );
181
182             DEBUG( "Has parent: " + parentKey );
183
184             ArchivaProjectModel parentProject;
185
186             synchronized ( effectiveProjectCache )
187             {
188                 // is the pre-merged parent in the cache? 
189                 if ( effectiveProjectCache.hasKey( parentKey ) )
190                 {
191                     DEBUG( "Fetching (from cache/parentKey): " + parentKey );
192                     // Use the one from the cache.
193                     parentProject = (ArchivaProjectModel) effectiveProjectCache.get( parentKey );
194                 }
195                 else
196                 {
197                     // Look it up, using resolvers.
198                     parentProject = this.resolverFactory.getCurrentResolverStack().findProject( parentRef );
199                 }
200             }
201
202             if ( parentProject != null )
203             {
204                 // Merge the pom with the parent pom.
205                 parentProject = mergeParent( parentProject );
206                 parentProject = expressionFilter.filter( parentProject );
207
208                 // Cache the pre-merged parent.
209                 synchronized ( effectiveProjectCache )
210                 {
211                     DEBUG( "Putting (to cache/parentKey/merged): " + parentKey );
212                     // Add the merged parent pom to the cache.
213                     effectiveProjectCache.put( parentKey, parentProject );
214                 }
215
216                 // Now merge the parent with the current
217                 mixedProject = ProjectModelMerge.merge( pom, parentProject );
218             }
219             else
220             {
221                 // Shortcircuit due to missing parent pom.
222                 // TODO: Document this via a monitor.
223                 mixedProject = mixinSuperPom( pom );
224
225                 // Cache the non-existant parent.
226                 synchronized ( effectiveProjectCache )
227                 {
228                     DEBUG( "Putting (to cache/parentKey/basicPom): " + parentKey );
229                     // Add the basic pom to cache.
230                     effectiveProjectCache.put( parentKey, createBasicPom( parentRef ) );
231                 }
232             }
233         }
234         else
235         {
236             DEBUG( "No parent found" );
237
238             /* Mix in the super-pom.
239              * 
240              * Super POM from maven/components contains many things.
241              * However, for purposes of archiva, only the <repositories>
242              * and <pluginRepositories> sections are of any value.
243              */
244
245             mixedProject = mixinSuperPom( pom );
246         }
247
248         return mixedProject;
249     }
250
251     private ArchivaProjectModel createBasicPom( VersionedReference ref )
252     {
253         ArchivaProjectModel model = new ArchivaProjectModel();
254         model.setGroupId( ref.getGroupId() );
255         model.setArtifactId( ref.getArtifactId() );
256         model.setVersion( ref.getVersion() );
257         model.setPackaging( "jar" );
258
259         return model;
260     }
261
262     /**
263      * Super POM from maven/components contains many things.
264      * However, for purposes of archiva, only the <repositories>
265      * and <pluginRepositories> sections are of any value.
266      * 
267      * @param pom
268      * @return
269      */
270     private ArchivaProjectModel mixinSuperPom( ArchivaProjectModel pom )
271     {
272         // TODO: add super pom repositories.
273         DEBUG( "Mix in Super POM: " + pom );
274
275         return pom;
276     }
277
278     private static Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
279     {
280         Map<String, Dependency> ret = new HashMap<String, Dependency>();
281
282         Iterator<Dependency> it = dependencies.iterator();
283         while ( it.hasNext() )
284         {
285             Dependency dep = it.next();
286             String key = toVersionlessDependencyKey( dep );
287             ret.put( key, dep );
288         }
289
290         return ret;
291     }
292
293     private static String toVersionlessDependencyKey( Dependency dep )
294     {
295         StringBuffer key = new StringBuffer();
296
297         key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
298         key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
299         key.append( dep.getType() );
300
301         return key.toString();
302     }
303
304     private String toProjectKey( ArchivaProjectModel project )
305     {
306         StringBuffer key = new StringBuffer();
307
308         key.append( project.getGroupId() ).append( ":" );
309         key.append( project.getArtifactId() ).append( ":" );
310         key.append( project.getVersion() );
311
312         return key.toString();
313     }
314
315     private void DEBUG( String msg )
316     {
317         // Used in debugging of this object.
318         // System.out.println( "[EffectiveProjectModelFilter] " + msg );
319     }
320 }