1 package org.apache.maven.archiva.repository.project.filters;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
40 * Builder for the Effective Project Model.
42 * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
44 * @plexus.component role="org.apache.maven.archiva.repository.project.ProjectModelFilter"
45 * role-hint="effective"
47 public class EffectiveProjectModelFilter
48 implements ProjectModelFilter
50 private ProjectModelFilter expressionFilter = new ProjectModelExpressionFilter();
55 private ProjectModelResolverFactory resolverFactory;
58 * @plexus.requirement role-hint="effective-project-cache"
60 private Cache effectiveProjectCache;
63 * Take the provided {@link ArchivaProjectModel} and build the effective {@link ArchivaProjectModel}.
66 * 1) Expand any expressions / properties.
67 * 2) Walk the parent project references and merge.
68 * 3) Apply dependency management settings.
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.
74 public ArchivaProjectModel filter( final ArchivaProjectModel project )
75 throws ProjectModelException
77 if ( project == null )
82 if ( resolverFactory.getCurrentResolverStack().isEmpty() )
84 throw new IllegalStateException( "Unable to build effective pom with no project model resolvers defined." );
87 ArchivaProjectModel effectiveProject;
88 String projectKey = toProjectKey( project );
90 synchronized ( effectiveProjectCache )
92 if ( effectiveProjectCache.hasKey( projectKey ) )
94 DEBUG( "Fetching (from cache/projectKey): " + projectKey );
95 effectiveProject = (ArchivaProjectModel) effectiveProjectCache.get( projectKey );
96 return effectiveProject;
100 // Clone submitted project (so that we don't mess with it)
101 effectiveProject = ArchivaModelCloner.clone( project );
103 // Setup Expression Evaluation pieces.
104 effectiveProject = expressionFilter.filter( effectiveProject );
106 DEBUG( "Starting build of effective with: " + effectiveProject );
108 // Merge in all the parent poms.
109 effectiveProject = mergeParent( effectiveProject );
111 // Resolve dependency versions from dependency management.
112 applyDependencyManagement( effectiveProject );
114 // Do not add project into cache if it contains no groupId and
115 // version information
116 if ( project.getGroupId() != null && project.getVersion() != null )
118 synchronized ( effectiveProjectCache )
120 DEBUG( "Putting (to cache/projectKey): " + projectKey );
121 effectiveProjectCache.put( projectKey, effectiveProject );
125 // Return what we got.
126 return effectiveProject;
129 private void applyDependencyManagement( ArchivaProjectModel pom )
131 if ( CollectionUtils.isEmpty( pom.getDependencyManagement() )
132 || CollectionUtils.isEmpty( pom.getDependencies() ) )
134 // Nothing to do. All done!
138 Map<String, Dependency> managedDependencies = createDependencyMap( pom.getDependencyManagement() );
139 Iterator<Dependency> it = pom.getDependencies().iterator();
140 while ( it.hasNext() )
142 Dependency dep = it.next();
143 String key = toVersionlessDependencyKey( dep );
145 // Do we need to do anything?
146 if ( managedDependencies.containsKey( key ) )
148 Dependency mgmtDep = (Dependency) managedDependencies.get( key );
150 dep.setVersion( mgmtDep.getVersion() );
151 dep.setScope( mgmtDep.getScope() );
152 dep.setExclusions( ProjectModelMerge.mergeExclusions( dep.getExclusions(), mgmtDep.getExclusions() ) );
157 private ArchivaProjectModel mergeParent( ArchivaProjectModel pom )
158 throws ProjectModelException
160 ArchivaProjectModel mixedProject;
162 DEBUG( "Project: " + toProjectKey( pom ) );
164 if ( pom.getParentProject() != null )
166 // Use parent reference.
167 VersionedReference parentRef = pom.getParentProject();
169 String parentKey = VersionedReference.toKey( parentRef );
171 DEBUG( "Has parent: " + parentKey );
173 ArchivaProjectModel parentProject;
175 synchronized ( effectiveProjectCache )
177 // is the pre-merged parent in the cache?
178 if ( effectiveProjectCache.hasKey( parentKey ) )
180 DEBUG( "Fetching (from cache/parentKey): " + parentKey );
181 // Use the one from the cache.
182 parentProject = (ArchivaProjectModel) effectiveProjectCache.get( parentKey );
186 // Look it up, using resolvers.
187 parentProject = this.resolverFactory.getCurrentResolverStack().findProject( parentRef );
191 if ( parentProject != null )
193 // Merge the pom with the parent pom.
194 parentProject = expressionFilter.filter( parentProject );
195 parentProject = mergeParent( parentProject );
197 // Cache the pre-merged parent.
198 synchronized ( effectiveProjectCache )
200 DEBUG( "Putting (to cache/parentKey/merged): " + parentKey );
201 // Add the merged parent pom to the cache.
202 effectiveProjectCache.put( parentKey, parentProject );
205 // Now merge the parent with the current
206 mixedProject = ProjectModelMerge.merge( pom, parentProject );
210 // Shortcircuit due to missing parent pom.
211 // TODO: Document this via a monitor.
212 mixedProject = mixinSuperPom( pom );
214 // Cache the non-existant parent.
215 synchronized ( effectiveProjectCache )
217 DEBUG( "Putting (to cache/parentKey/basicPom): " + parentKey );
218 // Add the basic pom to cache.
219 effectiveProjectCache.put( parentKey, createBasicPom( parentRef ) );
225 DEBUG( "No parent found" );
227 /* Mix in the super-pom.
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.
234 mixedProject = mixinSuperPom( pom );
240 private ArchivaProjectModel createBasicPom( VersionedReference ref )
242 ArchivaProjectModel model = new ArchivaProjectModel();
243 model.setGroupId( ref.getGroupId() );
244 model.setArtifactId( ref.getArtifactId() );
245 model.setVersion( ref.getVersion() );
246 model.setPackaging( "jar" );
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.
259 private ArchivaProjectModel mixinSuperPom( ArchivaProjectModel pom )
261 // TODO: add super pom repositories.
262 DEBUG( "Mix in Super POM: " + pom );
267 private static Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
269 Map<String, Dependency> ret = new HashMap<String, Dependency>();
271 Iterator<Dependency> it = dependencies.iterator();
272 while ( it.hasNext() )
274 Dependency dep = it.next();
275 String key = toVersionlessDependencyKey( dep );
282 private static String toVersionlessDependencyKey( Dependency dep )
284 StringBuffer key = new StringBuffer();
286 key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
287 key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
288 key.append( dep.getType() );
290 return key.toString();
293 private String toProjectKey( ArchivaProjectModel project )
295 StringBuffer key = new StringBuffer();
297 key.append( project.getGroupId() ).append( ":" );
298 key.append( project.getArtifactId() ).append( ":" );
299 key.append( project.getVersion() );
301 return key.toString();
304 private void DEBUG( String msg )
306 // Used in debugging of this object.
307 // System.out.println( "[EffectiveProjectModelFilter] " + msg );