]> source.dussan.org Git - archiva.git/blob
24a22f90c00667bf20a5fa1ace38677eb1cc560f
[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.VersionedReference;
28 import org.apache.maven.archiva.repository.project.ProjectModelException;
29 import org.apache.maven.archiva.repository.project.ProjectModelFilter;
30 import org.apache.maven.archiva.repository.project.ProjectModelMerge;
31 import org.apache.maven.archiva.repository.project.ProjectModelResolverFactory;
32 import org.codehaus.plexus.cache.Cache;
33
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38
39 /**
40  * Builder for the Effective Project Model.  
41  *
42  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
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         // Setup Expression Evaluation pieces.
104         effectiveProject = expressionFilter.filter( effectiveProject );
105
106         DEBUG( "Starting build of effective with: " + effectiveProject );
107
108         // Merge in all the parent poms.
109         effectiveProject = mergeParent( effectiveProject );
110
111         // Resolve dependency versions from dependency management.
112         applyDependencyManagement( effectiveProject );
113
114         // Do not add project into cache if it contains no groupId and
115         // version information
116         if ( project.getGroupId() != null && project.getVersion() != null )
117         {
118             synchronized ( effectiveProjectCache )
119             {
120                 DEBUG( "Putting (to cache/projectKey): " + projectKey );
121                 effectiveProjectCache.put( projectKey, effectiveProject );
122             }
123         }
124
125         // Return what we got.
126         return effectiveProject;
127     }
128
129     private void applyDependencyManagement( ArchivaProjectModel pom )
130     {
131         if ( CollectionUtils.isEmpty( pom.getDependencyManagement() )
132             || CollectionUtils.isEmpty( pom.getDependencies() ) )
133         {
134             // Nothing to do. All done!
135             return;
136         }
137
138         Map<String, Dependency> managedDependencies = createDependencyMap( pom.getDependencyManagement() );
139         Iterator<Dependency> it = pom.getDependencies().iterator();
140         while ( it.hasNext() )
141         {
142             Dependency dep = it.next();
143             String key = toVersionlessDependencyKey( dep );
144
145             // Do we need to do anything?
146             if ( managedDependencies.containsKey( key ) )
147             {
148                 Dependency mgmtDep = (Dependency) managedDependencies.get( key );
149
150                 dep.setVersion( mgmtDep.getVersion() );
151                 dep.setScope( mgmtDep.getScope() );
152                 dep.setExclusions( ProjectModelMerge.mergeExclusions( dep.getExclusions(), mgmtDep.getExclusions() ) );
153             }
154         }
155     }
156
157     private ArchivaProjectModel mergeParent( ArchivaProjectModel pom )
158         throws ProjectModelException
159     {
160         ArchivaProjectModel mixedProject;
161
162         DEBUG( "Project: " + toProjectKey( pom ) );
163
164         if ( pom.getParentProject() != null )
165         {
166             // Use parent reference.
167             VersionedReference parentRef = pom.getParentProject();
168
169             String parentKey = VersionedReference.toKey( parentRef );
170
171             DEBUG( "Has parent: " + parentKey );
172
173             ArchivaProjectModel parentProject;
174
175             synchronized ( effectiveProjectCache )
176             {
177                 // is the pre-merged parent in the cache? 
178                 if ( effectiveProjectCache.hasKey( parentKey ) )
179                 {
180                     DEBUG( "Fetching (from cache/parentKey): " + parentKey );
181                     // Use the one from the cache.
182                     parentProject = (ArchivaProjectModel) effectiveProjectCache.get( parentKey );
183                 }
184                 else
185                 {
186                     // Look it up, using resolvers.
187                     parentProject = this.resolverFactory.getCurrentResolverStack().findProject( parentRef );
188                 }
189             }
190
191             if ( parentProject != null )
192             {
193                 // Merge the pom with the parent pom.
194                 parentProject = expressionFilter.filter( parentProject );
195                 parentProject = mergeParent( parentProject );
196
197                 // Cache the pre-merged parent.
198                 synchronized ( effectiveProjectCache )
199                 {
200                     DEBUG( "Putting (to cache/parentKey/merged): " + parentKey );
201                     // Add the merged parent pom to the cache.
202                     effectiveProjectCache.put( parentKey, parentProject );
203                 }
204
205                 // Now merge the parent with the current
206                 mixedProject = ProjectModelMerge.merge( pom, parentProject );
207             }
208             else
209             {
210                 // Shortcircuit due to missing parent pom.
211                 // TODO: Document this via a monitor.
212                 mixedProject = mixinSuperPom( pom );
213
214                 // Cache the non-existant parent.
215                 synchronized ( effectiveProjectCache )
216                 {
217                     DEBUG( "Putting (to cache/parentKey/basicPom): " + parentKey );
218                     // Add the basic pom to cache.
219                     effectiveProjectCache.put( parentKey, createBasicPom( parentRef ) );
220                 }
221             }
222         }
223         else
224         {
225             DEBUG( "No parent found" );
226
227             /* Mix in the super-pom.
228              * 
229              * Super POM from maven/components contains many things.
230              * However, for purposes of archiva, only the <repositories>
231              * and <pluginRepositories> sections are of any value.
232              */
233
234             mixedProject = mixinSuperPom( pom );
235         }
236
237         return mixedProject;
238     }
239
240     private ArchivaProjectModel createBasicPom( VersionedReference ref )
241     {
242         ArchivaProjectModel model = new ArchivaProjectModel();
243         model.setGroupId( ref.getGroupId() );
244         model.setArtifactId( ref.getArtifactId() );
245         model.setVersion( ref.getVersion() );
246         model.setPackaging( "jar" );
247
248         return model;
249     }
250
251     /**
252      * Super POM from maven/components contains many things.
253      * However, for purposes of archiva, only the <repositories>
254      * and <pluginRepositories> sections are of any value.
255      * 
256      * @param pom
257      * @return
258      */
259     private ArchivaProjectModel mixinSuperPom( ArchivaProjectModel pom )
260     {
261         // TODO: add super pom repositories.
262         DEBUG( "Mix in Super POM: " + pom );
263
264         return pom;
265     }
266
267     private static Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
268     {
269         Map<String, Dependency> ret = new HashMap<String, Dependency>();
270
271         Iterator<Dependency> it = dependencies.iterator();
272         while ( it.hasNext() )
273         {
274             Dependency dep = it.next();
275             String key = toVersionlessDependencyKey( dep );
276             ret.put( key, dep );
277         }
278
279         return ret;
280     }
281
282     private static String toVersionlessDependencyKey( Dependency dep )
283     {
284         StringBuffer key = new StringBuffer();
285
286         key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
287         key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
288         key.append( dep.getType() );
289
290         return key.toString();
291     }
292
293     private String toProjectKey( ArchivaProjectModel project )
294     {
295         StringBuffer key = new StringBuffer();
296
297         key.append( project.getGroupId() ).append( ":" );
298         key.append( project.getArtifactId() ).append( ":" );
299         key.append( project.getVersion() );
300
301         return key.toString();
302     }
303
304     private void DEBUG( String msg )
305     {
306         // Used in debugging of this object.
307         // System.out.println( "[EffectiveProjectModelFilter] " + msg );
308     }
309 }