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.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;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
41 * Builder for the Effective Project Model.
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 System.out.println( "++++[EFFECTIVE Project] effectiveProject --> " + effectiveProject );
104 IssueManagement iM = effectiveProject.getIssueManagement();
107 System.out.println( "++++[EFFECTIVE Project] issue mgnt URL --> " + iM.getUrl() + " $$ " +
108 iM.getIssueManagementUrl() );
111 DEBUG( "Starting build of effective with: " + effectiveProject );
113 // Merge in all the parent poms.
114 effectiveProject = mergeParent( effectiveProject );
116 // Setup Expression Evaluation pieces.
117 effectiveProject = expressionFilter.filter( effectiveProject );
119 // Resolve dependency versions from dependency management.
120 applyDependencyManagement( effectiveProject );
122 // groupId or version could be updated by parent or expressions
123 projectKey = toProjectKey( effectiveProject );
125 // Do not add project into cache if it contains no groupId and
126 // version information
127 if ( effectiveProject.getGroupId() != null && effectiveProject.getVersion() != null )
129 synchronized ( effectiveProjectCache )
131 DEBUG( "Putting (to cache/projectKey): " + projectKey );
132 effectiveProjectCache.put( projectKey, effectiveProject );
136 // Return what we got.
137 return effectiveProject;
140 private void applyDependencyManagement( ArchivaProjectModel pom )
142 if ( CollectionUtils.isEmpty( pom.getDependencyManagement() )
143 || CollectionUtils.isEmpty( pom.getDependencies() ) )
145 // Nothing to do. All done!
149 Map<String, Dependency> managedDependencies = createDependencyMap( pom.getDependencyManagement() );
150 Iterator<Dependency> it = pom.getDependencies().iterator();
151 while ( it.hasNext() )
153 Dependency dep = it.next();
154 String key = toVersionlessDependencyKey( dep );
156 // Do we need to do anything?
157 if ( managedDependencies.containsKey( key ) )
159 Dependency mgmtDep = (Dependency) managedDependencies.get( key );
161 dep.setVersion( mgmtDep.getVersion() );
162 dep.setScope( mgmtDep.getScope() );
163 dep.setExclusions( ProjectModelMerge.mergeExclusions( dep.getExclusions(), mgmtDep.getExclusions() ) );
168 private ArchivaProjectModel mergeParent( ArchivaProjectModel pom )
169 throws ProjectModelException
171 ArchivaProjectModel mixedProject;
173 DEBUG( "Project: " + toProjectKey( pom ) );
175 if ( pom.getParentProject() != null )
177 // Use parent reference.
178 VersionedReference parentRef = pom.getParentProject();
180 String parentKey = VersionedReference.toKey( parentRef );
182 DEBUG( "Has parent: " + parentKey );
184 ArchivaProjectModel parentProject;
186 synchronized ( effectiveProjectCache )
188 // is the pre-merged parent in the cache?
189 if ( effectiveProjectCache.hasKey( parentKey ) )
191 DEBUG( "Fetching (from cache/parentKey): " + parentKey );
192 // Use the one from the cache.
193 parentProject = (ArchivaProjectModel) effectiveProjectCache.get( parentKey );
197 // Look it up, using resolvers.
198 parentProject = this.resolverFactory.getCurrentResolverStack().findProject( parentRef );
202 if ( parentProject != null )
204 // Merge the pom with the parent pom.
205 parentProject = mergeParent( parentProject );
206 parentProject = expressionFilter.filter( parentProject );
208 // Cache the pre-merged parent.
209 synchronized ( effectiveProjectCache )
211 DEBUG( "Putting (to cache/parentKey/merged): " + parentKey );
212 // Add the merged parent pom to the cache.
213 effectiveProjectCache.put( parentKey, parentProject );
216 // Now merge the parent with the current
217 mixedProject = ProjectModelMerge.merge( pom, parentProject );
221 // Shortcircuit due to missing parent pom.
222 // TODO: Document this via a monitor.
223 mixedProject = mixinSuperPom( pom );
225 // Cache the non-existant parent.
226 synchronized ( effectiveProjectCache )
228 DEBUG( "Putting (to cache/parentKey/basicPom): " + parentKey );
229 // Add the basic pom to cache.
230 effectiveProjectCache.put( parentKey, createBasicPom( parentRef ) );
236 DEBUG( "No parent found" );
238 /* Mix in the super-pom.
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.
245 mixedProject = mixinSuperPom( pom );
251 private ArchivaProjectModel createBasicPom( VersionedReference ref )
253 ArchivaProjectModel model = new ArchivaProjectModel();
254 model.setGroupId( ref.getGroupId() );
255 model.setArtifactId( ref.getArtifactId() );
256 model.setVersion( ref.getVersion() );
257 model.setPackaging( "jar" );
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.
270 private ArchivaProjectModel mixinSuperPom( ArchivaProjectModel pom )
272 // TODO: add super pom repositories.
273 DEBUG( "Mix in Super POM: " + pom );
278 private static Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
280 Map<String, Dependency> ret = new HashMap<String, Dependency>();
282 Iterator<Dependency> it = dependencies.iterator();
283 while ( it.hasNext() )
285 Dependency dep = it.next();
286 String key = toVersionlessDependencyKey( dep );
293 private static String toVersionlessDependencyKey( Dependency dep )
295 StringBuffer key = new StringBuffer();
297 key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
298 key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
299 key.append( dep.getType() );
301 return key.toString();
304 private String toProjectKey( ArchivaProjectModel project )
306 StringBuffer key = new StringBuffer();
308 key.append( project.getGroupId() ).append( ":" );
309 key.append( project.getArtifactId() ).append( ":" );
310 key.append( project.getVersion() );
312 return key.toString();
315 private void DEBUG( String msg )
317 // Used in debugging of this object.
318 // System.out.println( "[EffectiveProjectModelFilter] " + msg );