001: package org.apache.maven.extension;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import org.apache.maven.artifact.ArtifactUtils;
023: import org.apache.maven.artifact.repository.ArtifactRepository;
024: import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
025: import org.apache.maven.artifact.resolver.ArtifactResolutionException;
026: import org.apache.maven.context.BuildContextManager;
027: import org.apache.maven.model.Build;
028: import org.apache.maven.model.Extension;
029: import org.apache.maven.model.Model;
030: import org.apache.maven.model.Parent;
031: import org.apache.maven.profiles.ProfileManager;
032: import org.apache.maven.profiles.activation.CustomActivatorAdvice;
033: import org.apache.maven.project.MavenProject;
034: import org.apache.maven.project.MavenProjectBuilder;
035: import org.apache.maven.project.ProjectBuildingException;
036: import org.apache.maven.project.build.model.ModelLineage;
037: import org.apache.maven.project.build.model.ModelLineageBuilder;
038: import org.apache.maven.project.build.model.ModelLineageIterator;
039: import org.apache.maven.project.interpolation.ModelInterpolationException;
040: import org.apache.maven.project.interpolation.ModelInterpolator;
041: import org.codehaus.plexus.PlexusContainerException;
042: import org.codehaus.plexus.logging.LogEnabled;
043: import org.codehaus.plexus.logging.Logger;
044: import org.codehaus.plexus.logging.console.ConsoleLogger;
045:
046: import java.io.File;
047: import java.io.IOException;
048: import java.util.ArrayList;
049: import java.util.Collections;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.Properties;
055:
056: public class DefaultBuildExtensionScanner implements
057: BuildExtensionScanner, LogEnabled {
058:
059: private Logger logger;
060:
061: private BuildContextManager buildContextManager;
062:
063: private ExtensionManager extensionManager;
064:
065: private MavenProjectBuilder projectBuilder;
066:
067: private ModelLineageBuilder modelLineageBuilder;
068:
069: private ModelInterpolator modelInterpolator;
070:
071: public void scanForBuildExtensions(List files,
072: ArtifactRepository localRepository,
073: ProfileManager globalProfileManager)
074: throws ExtensionScanningException {
075: List visited = new ArrayList();
076:
077: for (Iterator it = files.iterator(); it.hasNext();) {
078: File pom = (File) it.next();
079:
080: scanInternal(pom, localRepository, globalProfileManager,
081: visited, files);
082: }
083: }
084:
085: public void scanForBuildExtensions(File pom,
086: ArtifactRepository localRepository,
087: ProfileManager globalProfileManager)
088: throws ExtensionScanningException {
089: scanInternal(pom, localRepository, globalProfileManager,
090: new ArrayList(), Collections.singletonList(pom));
091: }
092:
093: // TODO: Use a build-context cache object for visitedModelIdx and reactorFiles,
094: // once we move to just-in-time project scanning.
095: private void scanInternal(File pom,
096: ArtifactRepository localRepository,
097: ProfileManager globalProfileManager, List visitedModelIds,
098: List reactorFiles) throws ExtensionScanningException {
099:
100: // setup the CustomActivatorAdvice to fail quietly while we discover extensions...then, we'll
101: // reset it.
102: CustomActivatorAdvice activatorAdvice = CustomActivatorAdvice
103: .getCustomActivatorAdvice(buildContextManager);
104: activatorAdvice.setFailQuietly(true);
105: activatorAdvice.store(buildContextManager);
106:
107: try {
108: List originalRemoteRepositories = getInitialRemoteRepositories();
109:
110: getLogger().debug(
111: "Pre-scanning POM lineage of: " + pom
112: + " for build extensions.");
113:
114: ModelLineage lineage = buildModelLineage(pom,
115: localRepository, originalRemoteRepositories,
116: globalProfileManager);
117:
118: Map inheritedInterpolationValues = new HashMap();
119:
120: List inheritedRemoteRepositories = new ArrayList();
121:
122: inheritedRemoteRepositories
123: .addAll(originalRemoteRepositories);
124:
125: for (ModelLineageIterator lineageIterator = lineage
126: .reversedLineageIterator(); lineageIterator
127: .hasNext();) {
128: Model model = (Model) lineageIterator.next();
129: File modelPom = lineageIterator.getPOMFile();
130:
131: List remoteRepos = lineageIterator
132: .getArtifactRepositories();
133: if ((remoteRepos != null) && !remoteRepos.isEmpty()) {
134: inheritedRemoteRepositories.addAll(remoteRepos);
135: }
136:
137: String key = createKey(model);
138:
139: if (visitedModelIds.contains(key)) {
140: getLogger()
141: .debug(
142: "Already visited: " + key
143: + "; continuing.");
144: continue;
145: }
146:
147: visitedModelIds.add(key);
148:
149: getLogger().debug(
150: "Checking: " + model.getId()
151: + " for extensions. (It has "
152: + model.getModules().size()
153: + " modules.)");
154:
155: if (inheritedInterpolationValues == null) {
156: inheritedInterpolationValues = new HashMap();
157: }
158:
159: model = modelInterpolator.interpolate(model,
160: inheritedInterpolationValues, false);
161:
162: checkModelBuildForExtensions(model, localRepository,
163: inheritedRemoteRepositories);
164:
165: if (!reactorFiles.contains(modelPom)) {
166: getLogger()
167: .debug(
168: "POM: "
169: + modelPom
170: + " is not in the current reactor. Its modules will not be scanned.");
171: } else {
172: checkModulesForExtensions(modelPom, model,
173: localRepository,
174: originalRemoteRepositories,
175: globalProfileManager, visitedModelIds,
176: reactorFiles);
177: }
178:
179: Properties modelProps = model.getProperties();
180: if (modelProps != null) {
181: inheritedInterpolationValues.putAll(modelProps);
182: }
183: }
184:
185: getLogger().debug(
186: "Finished pre-scanning: " + pom
187: + " for build extensions.");
188:
189: extensionManager.registerWagons();
190: } catch (ModelInterpolationException e) {
191: throw new ExtensionScanningException(
192: "Failed to interpolate model from: " + pom
193: + " prior to scanning for extensions.", e);
194: } finally {
195: activatorAdvice.reset();
196: activatorAdvice.store(buildContextManager);
197: }
198: }
199:
200: private String createKey(Model model) {
201: Parent parent = model.getParent();
202:
203: String groupId = model.getGroupId();
204: if (groupId == null) {
205: groupId = parent.getGroupId();
206: }
207:
208: String artifactId = model.getArtifactId();
209:
210: return groupId + ":" + artifactId;
211: }
212:
213: private void checkModulesForExtensions(File containingPom,
214: Model model, ArtifactRepository localRepository,
215: List originalRemoteRepositories,
216: ProfileManager globalProfileManager, List visitedModelIds,
217: List reactorFiles) throws ExtensionScanningException {
218: // FIXME: This gets a little sticky, because modules can be added by profiles that require
219: // an extension in place before they can be activated.
220: List modules = model.getModules();
221:
222: if (modules != null) {
223: File basedir = containingPom.getParentFile();
224: getLogger().debug("Basedir is: " + basedir);
225:
226: for (Iterator it = modules.iterator(); it.hasNext();) {
227: // TODO: change this if we ever find a way to replace module definitions with g:a:v
228: String moduleSubpath = (String) it.next();
229:
230: getLogger().debug("Scanning module: " + moduleSubpath);
231:
232: File modulePomDirectory;
233:
234: try {
235: modulePomDirectory = new File(basedir,
236: moduleSubpath).getCanonicalFile();
237:
238: // ----------------------------------------------------------------------------
239: // We need to make sure we don't loop infinitely in the case where we have
240: // something like:
241: //
242: // <modules>
243: // <module>../MNGECLIPSE-256web</module>
244: // <module>../MNGECLIPSE-256utility</module>
245: // </modules>
246: //
247: // Where once we walk into the first module it will just get its parent dir
248: // containing its POM over and over again unless we make a comparison to
249: // basedir and the modulePomDirectory.
250: // ----------------------------------------------------------------------------
251:
252: if (modulePomDirectory.equals(basedir
253: .getCanonicalFile())) {
254: break;
255: }
256: } catch (IOException e) {
257: throw new ExtensionScanningException(
258: "Error getting canonical path for modulePomDirectory.",
259: e);
260: }
261:
262: if (modulePomDirectory.isDirectory()) {
263: getLogger().debug(
264: "Assuming POM file 'pom.xml' in module: "
265: + moduleSubpath
266: + " under basedir: " + basedir);
267: modulePomDirectory = new File(modulePomDirectory,
268: "pom.xml");
269: }
270:
271: if (!modulePomDirectory.exists()) {
272: getLogger()
273: .debug(
274: "Cannot find POM for module: "
275: + moduleSubpath
276: + "; continuing scan with next module. (Full path was: "
277: + modulePomDirectory + ")");
278: continue;
279: }
280:
281: scanInternal(modulePomDirectory, localRepository,
282: globalProfileManager, visitedModelIds,
283: reactorFiles);
284: }
285: }
286: }
287:
288: private void checkModelBuildForExtensions(Model model,
289: ArtifactRepository localRepository, List remoteRepositories)
290: throws ExtensionScanningException {
291: Build build = model.getBuild();
292:
293: if (build != null) {
294: List extensions = build.getExtensions();
295:
296: if ((extensions != null) && !extensions.isEmpty()) {
297: // thankfully, we don't have to deal with dependencyManagement here, yet.
298: // TODO Revisit if/when extensions are made to use the info in dependencyManagement
299: for (Iterator extensionIterator = extensions.iterator(); extensionIterator
300: .hasNext();) {
301: Extension extension = (Extension) extensionIterator
302: .next();
303:
304: getLogger().debug(
305: "Adding extension: "
306: + ArtifactUtils.versionlessKey(
307: extension.getGroupId(),
308: extension.getArtifactId())
309: + " from model: " + model.getId());
310:
311: try {
312: extensionManager.addExtension(extension, model,
313: remoteRepositories, localRepository);
314: } catch (ArtifactResolutionException e) {
315: throw new ExtensionScanningException(
316: "Cannot resolve pre-scanned extension artifact: "
317: + extension.getGroupId() + ":"
318: + extension.getArtifactId()
319: + ": " + e.getMessage(), e);
320: } catch (ArtifactNotFoundException e) {
321: throw new ExtensionScanningException(
322: "Cannot find pre-scanned extension artifact: "
323: + extension.getGroupId() + ":"
324: + extension.getArtifactId()
325: + ": " + e.getMessage(), e);
326: } catch (PlexusContainerException e) {
327: throw new ExtensionScanningException(
328: "Failed to add pre-scanned extension: "
329: + extension.getGroupId() + ":"
330: + extension.getArtifactId()
331: + ": " + e.getMessage(), e);
332: }
333: }
334: }
335: }
336: }
337:
338: private ModelLineage buildModelLineage(File pom,
339: ArtifactRepository localRepository,
340: List originalRemoteRepositories,
341: ProfileManager globalProfileManager)
342: throws ExtensionScanningException {
343: ModelLineage lineage;
344: try {
345: getLogger().debug(
346: "Building model-lineage for: " + pom
347: + " to pre-scan for extensions.");
348:
349: lineage = modelLineageBuilder.buildModelLineage(pom,
350: localRepository, originalRemoteRepositories,
351: globalProfileManager, false);
352: } catch (ProjectBuildingException e) {
353: throw new ExtensionScanningException(
354: "Error building model lineage in order to pre-scan for extensions: "
355: + e.getMessage(), e);
356: }
357:
358: return lineage;
359: }
360:
361: private List getInitialRemoteRepositories()
362: throws ExtensionScanningException {
363: MavenProject super Project;
364:
365: try {
366: super Project = projectBuilder.buildStandaloneSuperProject();
367: } catch (ProjectBuildingException e) {
368: throw new ExtensionScanningException(
369: "Error building super-POM for retrieving the default remote repository list: "
370: + e.getMessage(), e);
371: }
372:
373: return super Project.getRemoteArtifactRepositories();
374: }
375:
376: protected Logger getLogger() {
377: if (logger == null) {
378: logger = new ConsoleLogger(Logger.LEVEL_DEBUG,
379: "DefaultBuildExtensionScanner:internal");
380: }
381:
382: return logger;
383: }
384:
385: public void enableLogging(Logger logger) {
386: this.logger = logger;
387: }
388:
389: }
|