Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFred Bricon2014-11-04 18:51:34 -0500
committerFred Bricon2015-05-01 21:43:18 -0400
commit168ae1ecdb5c2b0b2a0052e66b3fb90b832c7a69 (patch)
treecde98910c8f6fe2c21607bbe00523bb7a9445963
parentbde2dfd917a855efa62f67995b7d8f123a53ed61 (diff)
downloadm2e-core-168ae1ecdb5c2b0b2a0052e66b3fb90b832c7a69.tar.gz
m2e-core-168ae1ecdb5c2b0b2a0052e66b3fb90b832c7a69.tar.xz
m2e-core-168ae1ecdb5c2b0b2a0052e66b3fb90b832c7a69.zip
449495 : improve ProjectConfiguration sort order
introduced new runsAfter and runsBefore attributes to the configurator extension point. runsAfter : Optional comma-separated list of ids of required project configurators this configurator should run after. ids suffixed with ? are considered optional runsBefore : Optional comma-separated list of ids of optional project configurators this configurator should run before. ids suffixed with * are considered required The secondaryTo attribute has been deprecated. For any given mojoExecution, a Directed Acyclic Graph is built from the configurators list and their runsAfter/runsBefore configurator dependencies. Configurators are sorted twice : - once per mojoexecution. Only configurators participating to that mojoexecution are considered, - once for project configuration. All configurators from all mojoexecutions are sorted. So if a project configurator pc1 needs to run after another configurator pc2 defined in a separate mojoexecution, it will need to declare : runsAfter="pc2?", so the first mojoexecution sort doesn't fail. Change-Id: I501b5ae23750f957b256f3caafcc1046b4d3a846 Signed-off-by: Fred Bricon <fbricon@gmail.com>
-rw-r--r--org.eclipse.m2e.core/schema/projectConfigurators.exsd28
-rw-r--r--org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java4
-rw-r--r--org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/LifecycleMappingFactory.java221
-rw-r--r--org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/ProjectConfigurationElementSorter.java276
-rw-r--r--org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties4
-rw-r--r--org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java2
-rw-r--r--org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractCustomizableLifecycleMapping.java10
-rw-r--r--org.eclipse.m2e.tests.common/src/org/eclipse/m2e/tests/common/WorkspaceHelpers.java3
8 files changed, 448 insertions, 100 deletions
diff --git a/org.eclipse.m2e.core/schema/projectConfigurators.exsd b/org.eclipse.m2e.core/schema/projectConfigurators.exsd
index d1cc86f5..e287686b 100644
--- a/org.eclipse.m2e.core/schema/projectConfigurators.exsd
+++ b/org.eclipse.m2e.core/schema/projectConfigurators.exsd
@@ -50,7 +50,11 @@
<element name="configurator">
<annotation>
<documentation>
- Project configurator
+ Project configurator.
+
+Project Configurators are sorted during lifecycle mapping execution, per the runsAfter/runsBefore attributes :
+- once in the scope of a mojo execution, during a full/incremental build
+- once before project configuration updates (after collecting configurators from all mojo executions)
</documentation>
</annotation>
<complexType>
@@ -84,13 +88,29 @@
<attribute name="secondaryTo" type="string">
<annotation>
<documentation>
- Id of primary project configurator that must be enabled for a plugin execution in order for this (secondary) configurator to be enabled. One and only one primary configurator must be enable for each interesting plugin execution, but any number of secondary project configurators are allowed. Primary configurator will be invoked before its corresponding secondary configurators, if any, but invocation order among secondary configurators is not specified.
+ DEPRECATED : use runsAfter instead.
+
+Id of primary project configurator that must be enabled for a plugin execution in order for this (secondary) configurator to be enabled. One and only one primary configurator must be enable for each interesting plugin execution, but any number of secondary project configurators are allowed. Primary configurator will be invoked before its corresponding secondary configurators, if any, but invocation order among secondary configurators is not specified.
</documentation>
<appinfo>
- <meta.attribute kind="identifier" basedOn="org.eclipse.m2e.core.projectConfigurators/configurator/@id"/>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.m2e.core.projectConfigurators/configurator/@id" deprecated="true"/>
</appinfo>
</annotation>
</attribute>
+ <attribute name="runsAfter" type="string">
+ <annotation>
+ <documentation>
+ Optional comma-separated list of ids of required project configurators this project configurator should run after. By default, if one of the runsAfter configurators is missing/can not be executed, this configurator will be skipped. However, configurator ids suffixed with a ? are considered optional. This configurator will be invoked even if optional configurators it should run after are missing.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="runsBefore" type="string">
+ <annotation>
+ <documentation>
+ Optional comma-separated list of ids of optional project configurators this configurator should run before. By default, this configurator will be invoked even if configurators it should run before are missing. However, configurator ids suffixed with a * are considered required. If any of the required &quot;runBefore&quot; configurators is missing, this configurator will be skipped.
+ </documentation>
+ </annotation>
+ </attribute>
</complexType>
</element>
@@ -135,7 +155,7 @@
<meta.section type="copyright"/>
</appinfo>
<documentation>
- Copyright (c) 2007, 2008 Sonatype, Inc.
+ Copyright (c) 2007-2015 Sonatype, Inc and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java
index 33cc6d3d..d5d48924 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java
@@ -252,6 +252,10 @@ public class Messages extends NLS {
public static String AbstractMavenRuntime_unknownProject;
+ public static String ProjectConfiguratorToRunBeforeNotAvailable;
+
+ public static String ProjectConfiguratorToRunAfterNotAvailable;
+
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/LifecycleMappingFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/LifecycleMappingFactory.java
index f734d99d..4cf2a1ee 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/LifecycleMappingFactory.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/LifecycleMappingFactory.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008 Sonatype, Inc.
+ * Copyright (c) 2008-2015 Sonatype, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
* Contributors:
* Sonatype, Inc. - initial API and implementation
* Andrew Eisenberg - Work on Bug 350414
+ * Fred Bricon (Red Hat) - project configurator sort (Bug #449495)
*******************************************************************************/
package org.eclipse.m2e.core.internal.lifecyclemapping;
@@ -52,6 +53,7 @@ import org.eclipse.core.runtime.spi.RegistryContributor;
import org.eclipse.osgi.util.NLS;
import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.dag.CycleDetectedException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
@@ -79,6 +81,7 @@ import org.eclipse.m2e.core.internal.lifecyclemapping.model.PluginExecutionFilte
import org.eclipse.m2e.core.internal.lifecyclemapping.model.PluginExecutionMetadata;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.io.xpp3.LifecycleMappingMetadataSourceXpp3Reader;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.io.xpp3.LifecycleMappingMetadataSourceXpp3Writer;
+import org.eclipse.m2e.core.internal.markers.IMavenMarkerManager;
import org.eclipse.m2e.core.internal.markers.MavenProblemInfo;
import org.eclipse.m2e.core.internal.markers.SourceLocation;
import org.eclipse.m2e.core.internal.markers.SourceLocationHelper;
@@ -157,8 +160,6 @@ public class LifecycleMappingFactory {
private static final String ATTR_VERSION = "version";
- private static final String ATTR_SECONDARY_TO = "secondaryTo";
-
private static final String LIFECYCLE_MAPPING_METADATA_CLASSIFIER = "lifecycle-mapping-metadata";
private static List<LifecycleMappingMetadataSource> bundleMetadataSources = null;
@@ -544,35 +545,65 @@ public class LifecycleMappingFactory {
// PHASE 2. Bind project configurators to mojo executions.
//
- Map<MojoExecutionKey, List<IPluginExecutionMetadata>> executionMapping = new LinkedHashMap<MojoExecutionKey, List<IPluginExecutionMetadata>>();
+ Map<MojoExecutionKey, List<IPluginExecutionMetadata>> executionMapping = new LinkedHashMap<>();
+
+ if(mojoExecutions != null && !mojoExecutions.isEmpty()) {
+ Map<String, IConfigurationElement> elements = getProjectConfiguratorExtensions();
- if(mojoExecutions != null) {
for(MojoExecution execution : mojoExecutions) {
+
MojoExecutionKey executionKey = new MojoExecutionKey(execution);
+ Map<String, PluginExecutionMetadata> configuratorMetadataMap = new HashMap<>();
+
PluginExecutionMetadata primaryMetadata = null;
- // find primary mapping first
+ Map<MappingMetadataSource, List<PluginExecutionMetadata>> metadatasPerSource = new LinkedHashMap<>();
+
+ // collect all metadatasPerSource and extract all configurator execution metadatas
+ for(MappingMetadataSource source : metadataSources) {
+ try {
+ List<PluginExecutionMetadata> metadatas = applyParametersFilter(
+ source.getPluginExecutionMetadata(executionKey), mavenProject, execution, monitor);
+ metadatasPerSource.put(source, metadatas);
+ for(PluginExecutionMetadata executionMetadata : metadatas) {
+ if(isConfigurator(executionMetadata)) {
+ String id = getProjectConfiguratorId(executionMetadata);
+ configuratorMetadataMap.put(id, executionMetadata);
+ }
+ }
+ } catch(CoreException e) {
+ SourceLocation location = SourceLocationHelper.findLocation(mavenProject, executionKey);
+ result.addProblem(new MavenProblemInfo(location, e));
+ metadatasPerSource.put(source, Collections.<PluginExecutionMetadata> emptyList());
+ }
+ }
+
+ //Sort configurator execution metadatas
+ ProjectConfigurationElementSorter sorter = null;
try {
- for(MappingMetadataSource source : metadataSources) {
- try {
- List<PluginExecutionMetadata> metadatas = applyParametersFilter(
- source.getPluginExecutionMetadata(executionKey), mavenProject, execution, monitor);
- for(PluginExecutionMetadata executionMetadata : metadatas) {
- if(LifecycleMappingFactory.isPrimaryMapping(executionMetadata)) {
- if(primaryMetadata != null) {
- primaryMetadata = null;
- throw new DuplicateMappingException();
- }
- primaryMetadata = executionMetadata;
+ sorter = new ProjectConfigurationElementSorter(configuratorMetadataMap.keySet(), elements);
+ } catch(CycleDetectedException ex) {
+ log.error(ex.getMessage(), ex);
+ result.addProblem(new MavenProblemInfo(1, NLS.bind(
+ "Cyclic dependency detected between project configurators for {0}", mavenProject.toString())));
+ return;// fatal error
+ }
+
+ //find primary mapping across different sources
+ try {
+ for(Map.Entry<MappingMetadataSource, List<PluginExecutionMetadata>> entry : metadatasPerSource.entrySet()) {
+ for(PluginExecutionMetadata executionMetadata : entry.getValue()) {
+ if(isPrimaryMapping(executionMetadata, sorter)) {
+ if(primaryMetadata != null) {
+ primaryMetadata = null;
+ throw new DuplicateMappingException();
}
+ primaryMetadata = executionMetadata;
}
- if(primaryMetadata != null) {
- break;
- }
- } catch(CoreException e) {
- SourceLocation location = SourceLocationHelper.findLocation(mavenProject, executionKey);
- result.addProblem(new MavenProblemInfo(location, e));
+ }
+ if(primaryMetadata != null) {
+ break;
}
}
} catch(DuplicateMappingException e) {
@@ -588,35 +619,25 @@ public class LifecycleMappingFactory {
primaryMetadata = null;
}
- List<IPluginExecutionMetadata> executionMetadatas = new ArrayList<IPluginExecutionMetadata>();
+ //add secondary configurators in order
+ List<IPluginExecutionMetadata> executionMetadatas = new ArrayList<>();
if(primaryMetadata != null) {
executionMetadatas.add(primaryMetadata);
-
- if(primaryMetadata.getAction() == PluginExecutionAction.configurator) {
- // attach any secondary mapping
- for(MappingMetadataSource source : metadataSources) {
- try {
- List<PluginExecutionMetadata> metadatas = source.getPluginExecutionMetadata(executionKey);
- metadatas = applyParametersFilter(metadatas, mavenProject, execution, monitor);
- for(PluginExecutionMetadata metadata : metadatas) {
- if(isValidPluginExecutionMetadata(metadata)) {
- if(metadata.getAction() == PluginExecutionAction.configurator
- && isSecondaryMapping(metadata, primaryMetadata)) {
- executionMetadatas.add(metadata);
- }
- } else {
- log.debug("Invalid secondary lifecycle mapping metadata for {}.", executionKey.toString());
- }
- }
- } catch(CoreException e) {
- SourceLocation location = SourceLocationHelper.findLocation(mavenProject, executionKey);
- result.addProblem(new MavenProblemInfo(location, e));
+ if(isConfigurator(primaryMetadata)) {
+ String primaryConfiguratorId = getProjectConfiguratorId(primaryMetadata);
+ List<String> secondaryConfiguratorIds = sorter.getSecondaryConfigurators(primaryConfiguratorId);
+ for(String id : secondaryConfiguratorIds) {
+ IPluginExecutionMetadata metadata = configuratorMetadataMap.get(id);
+ if(metadata == null) {
+ log.debug("Invalid secondary lifecycle mapping metadata {} for {}.", id, executionKey.toString());
+ } else {
+ executionMetadatas.add(metadata);
}
}
}
}
- // TODO valid executionMetadatas and convert to error mapping invalid enties.
+ // TODO valid executionMetadatas and convert to error mapping invalid entries.
executionMapping.put(executionKey, executionMetadatas);
}
@@ -656,13 +677,19 @@ public class LifecycleMappingFactory {
case ignore:
return true;
case configurator:
- try {
- getProjectConfiguratorId(metadata);
- return true;
- } catch(LifecycleMappingConfigurationException e) {
- // fall through
- }
- return false;
+ return isConfigurator(metadata);
+ }
+ return false;
+ }
+
+ private static boolean isConfigurator(PluginExecutionMetadata metadata) {
+ if(PluginExecutionAction.configurator == metadata.getAction()) {
+ try {
+ getProjectConfiguratorId(metadata);
+ return true;
+ } catch(LifecycleMappingConfigurationException e) {
+ // fall through
+ }
}
return false;
}
@@ -803,7 +830,7 @@ public class LifecycleMappingFactory {
}
// TODO ideally, we need to reuse the same parent MavenProject instance in all child modules
- // each instance takes ~1M, so we can easily safe 100M+ of heap for larger workspaces
+ // each instance takes ~1M, so we can easily save 100M+ of heap for larger workspaces
project = maven.resolveParentProject(project, monitor);
} while(project != null);
@@ -963,8 +990,12 @@ public class LifecycleMappingFactory {
return extensions;
}
- private static IConfigurationElement getProjectConfiguratorExtension(String configuratorId) {
- IConfigurationElement element = getProjectConfiguratorExtensions().get(configuratorId);
+ private static IConfigurationElement getProjectConfiguratorExtension(String configuratorId,
+ Map<String, IConfigurationElement> elements) {
+ if(elements == null) {
+ return null;
+ }
+ IConfigurationElement element = elements.get(configuratorId);
if(element != null && element.getName().equals(ELEMENT_CONFIGURATOR)) {
if(configuratorId.equals(element.getAttribute(AbstractProjectConfigurator.ATTR_ID))) {
return element;
@@ -973,6 +1004,10 @@ public class LifecycleMappingFactory {
return null;
}
+ private static IConfigurationElement getProjectConfiguratorExtension(String configuratorId) {
+ return getProjectConfiguratorExtension(configuratorId, getProjectConfiguratorExtensions());
+ }
+
private static void checkCompatibleVersion(Plugin metadataPlugin) {
ComparableVersion version = new ComparableVersion(metadataPlugin.getVersion());
if(!version.equals(new ComparableVersion(LIFECYCLE_MAPPING_PLUGIN_VERSION))) {
@@ -1244,46 +1279,18 @@ public class LifecycleMappingFactory {
return null;
}
- private static boolean isPrimaryMapping(PluginExecutionMetadata executionMetadata) {
+ private static boolean isPrimaryMapping(PluginExecutionMetadata executionMetadata,
+ ProjectConfigurationElementSorter sorter) {
if(executionMetadata == null) {
return false;
}
- if(executionMetadata.getAction() == PluginExecutionAction.configurator) {
+ if(isConfigurator(executionMetadata)) {
String configuratorId = getProjectConfiguratorId(executionMetadata);
- IConfigurationElement element = getProjectConfiguratorExtension(configuratorId);
- if(element != null) {
- return element.getAttribute(ATTR_SECONDARY_TO) == null;
- }
+ return sorter.isRootConfigurator(configuratorId);
}
return true;
}
- private static boolean isSecondaryMapping(PluginExecutionMetadata metadata, PluginExecutionMetadata primaryMetadata) {
- if(metadata == null || primaryMetadata == null) {
- return false;
- }
- if(PluginExecutionAction.configurator != metadata.getAction()
- || PluginExecutionAction.configurator != primaryMetadata.getAction()) {
- return false;
- }
- if(!isPrimaryMapping(primaryMetadata)) {
- return false;
- }
- String primaryId = getProjectConfiguratorId(primaryMetadata);
- String secondaryId = getProjectConfiguratorId(metadata);
- if(primaryId == null || secondaryId == null) {
- return false;
- }
- if(secondaryId.equals(primaryId)) {
- return false;
- }
- IConfigurationElement extension = getProjectConfiguratorExtension(secondaryId);
- if(extension == null) {
- return false;
- }
- return primaryId.equals(extension.getAttribute(ATTR_SECONDARY_TO));
- }
-
public static ILifecycleMapping getLifecycleMapping(IMavenProjectFacade facade) {
ILifecycleMapping lifecycleMapping = (ILifecycleMapping) facade
.getSessionProperty(MavenProjectFacade.PROP_LIFECYCLE_MAPPING);
@@ -1308,10 +1315,46 @@ public class LifecycleMappingFactory {
// Project configurators are stored as a facade session property, so they are "lost" on eclipse restart.
LifecycleMappingResult result = new LifecycleMappingResult();
instantiateProjectConfigurators(facade.getMavenProject(), result, facade.getMojoExecutionMapping());
- configurators = result.getProjectConfigurators();
+ configurators = setProjectConfigurators(facade, result);
// TODO deal with configurators that have been removed since facade was first created
- facade.setSessionProperty(MavenProjectFacade.PROP_CONFIGURATORS, configurators);
+ if(result.hasProblems()) {
+ IMavenMarkerManager markerManager = MavenPluginActivator.getDefault().getMavenMarkerManager();
+ for(MavenProblemInfo problem : result.getProblems()) {
+ markerManager.addErrorMarker(facade.getPom(), IMavenConstants.MARKER_LIFECYCLEMAPPING_ID, problem);
+ }
+ }
+ }
+ return configurators;
+ }
+
+ public static Map<String, AbstractProjectConfigurator> setProjectConfigurators(IMavenProjectFacade facade,
+ LifecycleMappingResult mappingResult) {
+
+ Map<String, AbstractProjectConfigurator> unsorted = mappingResult.getProjectConfigurators();
+ if(unsorted == null || unsorted.isEmpty()) {
+ facade.setSessionProperty(MavenProjectFacade.PROP_CONFIGURATORS, unsorted);
+ return unsorted;
+ }
+
+ Map<String, AbstractProjectConfigurator> configurators = new LinkedHashMap<>(unsorted.size());
+ Map<String, IConfigurationElement> elements = getProjectConfiguratorExtensions();
+ try {
+ ProjectConfigurationElementSorter sorter = new ProjectConfigurationElementSorter(elements);
+ List<String> sortedConfigurators = sorter.getSortedConfigurators();
+ log.debug("{} is configured by :", facade.getProject().getName());
+ for(String id : sortedConfigurators) {
+ AbstractProjectConfigurator configurator = unsorted.get(id);
+ if(configurator != null) {
+ log.debug("\t- {}", id);
+ configurators.put(id, configurator);
+ }
+ }
+ } catch(CycleDetectedException e) {
+ log.error("Cycle detecting while sorting configurators", e);
+ SourceLocation location = SourceLocationHelper.findPackagingLocation(facade.getMavenProject());
+ mappingResult.addProblem(new MavenProblemInfo(location, e));
}
+ facade.setSessionProperty(MavenProjectFacade.PROP_CONFIGURATORS, configurators);
return configurators;
}
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/ProjectConfigurationElementSorter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/ProjectConfigurationElementSorter.java
new file mode 100644
index 00000000..2df199ab
--- /dev/null
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/ProjectConfigurationElementSorter.java
@@ -0,0 +1,276 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Red Hat, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.m2e.core.internal.lifecyclemapping;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.osgi.util.NLS;
+
+import org.codehaus.plexus.util.dag.CycleDetectedException;
+import org.codehaus.plexus.util.dag.DAG;
+import org.codehaus.plexus.util.dag.TopologicalSorter;
+
+import org.eclipse.m2e.core.internal.Messages;
+
+
+/**
+ * Sorts a list of Project Configurator Metadata according to their matching {@link IConfigurationElement}s
+ */
+public class ProjectConfigurationElementSorter {
+
+ private List<String> sortedConfigurators;
+
+ private Map<String, String> incompleteConfigurators;
+
+ private Set<String> missingRequiredConfigurators;
+
+ private Set<String> allSecondaryConfigurators = new HashSet<>();
+
+ private Map<String, List<String>> primaryConfigurators = new HashMap<>();
+
+ /**
+ * Sorts a list of ids, ordering it by Project Configurator {@link IConfigurationElement}s
+ *
+ * @param configuratorIds, a collection of configurator ids to sort
+ * @param configurators, a map of [id:project configurator's {@link IConfigurationElement}]
+ * @throws CycleDetectedException if a cycle is detected between configurators
+ */
+ public ProjectConfigurationElementSorter(Collection<String> configuratorIds,
+ Map<String, IConfigurationElement> configurators) throws CycleDetectedException {
+
+ Assert.isNotNull(configurators, "configuratorConfigElements parameter can not be null");
+
+ //DAG including required and optional configurators
+ DAG fullDag = new DAG();
+
+ //DAG including required configurators only
+ DAG requirementsDag = new DAG();
+
+ Map<String, String> _incompletes = new HashMap<>();
+ Set<String> _missingIds = new HashSet<>();
+
+ //Create a vertex for each configurator.
+ for(String key : configuratorIds) {
+ requirementsDag.addVertex(key);
+ fullDag.addVertex(key);
+
+ IConfigurationElement configurator = configurators.get(key);
+ if(configurator == null) {
+ _missingIds.add(key);
+ continue;
+ }
+ //Add edge for configurators this configurator should run after
+ String[] runsAfter = safeSplit(configurator.getAttribute("runsAfter"));
+
+ //fallback to legacy secondaryTo attribute
+ if(runsAfter == null) {
+ String secondaryTo = configurator.getAttribute("secondaryTo");
+ if(secondaryTo != null) {
+ runsAfter = new String[] {secondaryTo};
+ }
+ }
+
+ if(runsAfter != null) {
+ for(String id : runsAfter) {
+ id = id.trim();
+ if(id.isEmpty()) {
+ continue;
+ }
+ boolean isRequired = !id.endsWith("?");
+ String predecessorId = sanitize(id);
+ if(isRequired) {
+ requirementsDag.addEdge(key, predecessorId);
+ }
+
+ IConfigurationElement predecessor = configurators.get(predecessorId);
+ if(predecessor == null) {
+ if(isRequired) {
+ _missingIds.add(predecessorId);
+ _incompletes.put(key, NLS.bind(Messages.ProjectConfiguratorToRunAfterNotAvailable, key, predecessorId));
+ }
+ } else {
+ fullDag.addEdge(key, predecessorId);
+ }
+ }
+ }
+
+ //Add edges for configurators this configurator should run before
+
+ String[] runsBefore = safeSplit(configurator.getAttribute("runsBefore"));
+ if(runsBefore != null) {
+ for(String id : runsBefore) {
+ id = id.trim();
+ if(id.isEmpty()) {
+ continue;
+ }
+ boolean isRequired = id.endsWith("*");
+ String successorId = sanitize(id);
+ if(isRequired) {
+ requirementsDag.addEdge(successorId, key);
+ }
+ IConfigurationElement successor = configurators.get(successorId);
+ if(successor == null) {
+ if(isRequired) {
+ //missing required matching configElement
+ _missingIds.add(successorId);
+ _incompletes.put(key, NLS.bind(Messages.ProjectConfiguratorToRunBeforeNotAvailable, key, successorId));
+ }
+ } else {
+ fullDag.addEdge(successorId, key);
+ }
+ }
+ }
+
+ }
+
+ //1st sort, leaving out optional configurators, to detect broken required dependencies
+ List<String> sortedExecutions = TopologicalSorter.sort(requirementsDag);
+
+ for(String id : sortedExecutions) {
+ boolean isIncompleteOrMissing = _missingIds.contains(id) || _incompletes.containsKey(id);
+ if(isIncompleteOrMissing) {
+ Set<String> dependents = new HashSet<>();
+ getDependents(id, requirementsDag, dependents);
+ for(String next : dependents) {
+ if(configuratorIds.contains(next) && (_missingIds.contains(next) || !_incompletes.containsKey(next))) {
+ _incompletes.put(next, NLS.bind(Messages.ProjectConfiguratorNotAvailable, id, next));
+ }
+ }
+ }
+ }
+
+ //2nd sort, including optional dependencies
+ sortedExecutions = TopologicalSorter.sort(fullDag);
+
+ List<String> _sortedConfigurators = new ArrayList<>(sortedExecutions.size());
+
+ for(String id : sortedExecutions) {
+ //Remove incomplete metadata
+ if(_incompletes.containsKey(id) || _missingIds.contains(id)) {
+ continue;
+ }
+
+ List<String> children = fullDag.getChildLabels(id);
+ if(children == null || children.isEmpty()) {
+ //found primary configurator
+ //get secondaries now
+ Set<String> secondaries = new LinkedHashSet<>();
+ getDependents(id, fullDag, secondaries);
+ primaryConfigurators.put(id, new ArrayList<>(secondaries));
+ allSecondaryConfigurators.addAll(secondaries);
+ }
+
+ //add to resulting list
+ _sortedConfigurators.add(id);
+ }
+
+ sortedConfigurators = Collections.unmodifiableList(_sortedConfigurators);
+ incompleteConfigurators = Collections.unmodifiableMap(_incompletes);
+ missingRequiredConfigurators = Collections.unmodifiableSet(_missingIds);
+ }
+
+ public ProjectConfigurationElementSorter(Map<String, IConfigurationElement> configurators)
+ throws CycleDetectedException {
+ this(configurators.keySet(), configurators);
+ }
+
+ private static void getDependents(String id, DAG dag, Set<String> dependents) {
+ List<String> parents = dag.getParentLabels(id);
+ if(parents == null || parents.isEmpty()) {
+ return;
+ }
+ for(String parent : parents) {
+ if(dependents.add(parent)) {
+ getDependents(parent, dag, dependents);
+ }
+ }
+ }
+
+ private static String sanitize(String id) {
+ return (id.endsWith("?") || id.endsWith("*")) ? id.substring(0, id.length() - 1) : id;
+ }
+
+ private static String[] safeSplit(String value) {
+ return value == null ? null : value.split(",");
+ }
+
+ /**
+ * @return a sorted, unmodifiable list of configurator ids
+ */
+ public List<String> getSortedConfigurators() {
+ if(sortedConfigurators == null) {
+ sortedConfigurators = Collections.emptyList();
+ }
+ return sortedConfigurators;
+ }
+
+ /**
+ * @return an unmodifiable Map of ids of incomplete configurators; (each value corresponds to the reason why it was
+ * found to be incomplete)
+ */
+ public Map<String, String> getIncompleteConfigurators() {
+ if(incompleteConfigurators == null) {
+ incompleteConfigurators = Collections.emptyMap();
+ }
+ return incompleteConfigurators;
+ }
+
+ /**
+ * @return an unmodifiable set of missing configurator ids.
+ */
+ public Set<String> getMissingConfigurators() {
+ if(missingRequiredConfigurators == null) {
+ missingRequiredConfigurators = Collections.emptySet();
+ }
+ return missingRequiredConfigurators;
+ }
+
+ /**
+ * @return an ordered list of secondary configurator ids to this primaryConfigurator.
+ */
+ public List<String> getSecondaryConfigurators(String primaryConfigurator) {
+ List<String> secondaries = primaryConfigurators.get(primaryConfigurator);
+ if(secondaries == null) {
+ secondaries = Collections.emptyList();
+ }
+ return secondaries;
+ }
+
+ /**
+ * @return true if a configurator id is a root configurator (i.e. has no parent)
+ */
+ public boolean isRootConfigurator(String configuratorId) {
+ if(configuratorId == null) {
+ return false;
+ }
+ boolean isRoot = primaryConfigurators.containsKey(configuratorId);
+ if(!isRoot) {
+ isRoot = !allSecondaryConfigurators.contains(configuratorId);
+ }
+ return isRoot;
+ }
+
+ public String toString() {
+ return "ProjectConfigurationElementSorter [" + getSortedConfigurators() + "]";
+ }
+
+}
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties
index 65c91381..cc1c8418 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties
@@ -29,6 +29,8 @@ LifecycleMappingPluginVersionIncompatible=Incompatible lifecycle mapping plugin
PluginExecutionMappingDuplicate=Conflicting lifecycle mapping (plugin execution "{0}"). To enable full functionality, remove the conflicting mapping and run Maven->Update Project Configuration.
PluginExecutionMappingInvalid=Invalid plugin execution mapping (plugin execution "{0}"). To enable full functionality, correct the invalid mapping and run Maven->Update Project Configuration.
ProjectConfiguratorNotAvailable=Project configurator "{0}" required by plugin execution "{1}" is not available. To enable full functionality, install the project configurator and run Maven->Update Project Configuration.
+ProjectConfiguratorToRunBeforeNotAvailable={0} is missing required configurator "{1}" to run before. To enable full functionality, install the project configurator and run Maven->Update Project Configuration.
+ProjectConfiguratorToRunAfterNotAvailable={0} is missing required configurator "{1}" to run after. To enable full functionality, install the project configurator and run Maven->Update Project Configuration.
ProjectConfigurationUpdateRequired=Project configuration is not up-to-date with pom.xml. Select: Maven->Update Project... from the project context menu or use Quick Fix.
LocalProjectScanner_accessDeniedFromFolder=Can not access files from ''{0}''
LocalProjectScanner_task_scanning=Scanning folders
@@ -112,4 +114,4 @@ RepositoryRegistryUpdateJob_title=Repository registry initialization
pluginMarkerBuildError=Project build error\: {0}
importProjectExists=Project "{0}" already exists.
buildConextFileAccessOutsideOfProjectBasedir=Access "{0}" directory outside of project base directory.
-ProjectConversion_error_duplicate_conversion_participant=Duplicate Conversion Participant {0} unsupported. \ No newline at end of file
+ProjectConversion_error_duplicate_conversion_participant=Duplicate Conversion Participant {0} unsupported.
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java
index 66bacab4..a9ade0d9 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java
@@ -604,7 +604,7 @@ public class ProjectRegistryManager {
// XXX reconcile with corresponding LifecycleMappingFactory methods
newFacade.setSessionProperty(MavenProjectFacade.PROP_LIFECYCLE_MAPPING, mappingResult.getLifecycleMapping());
- newFacade.setSessionProperty(MavenProjectFacade.PROP_CONFIGURATORS, mappingResult.getProjectConfigurators());
+ LifecycleMappingFactory.setProjectConfigurators(newFacade, mappingResult);
markerManager.deleteMarkers(newFacade.getPom(), IMavenConstants.MARKER_LIFECYCLEMAPPING_ID);
if(mappingResult.hasProblems()) {
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractCustomizableLifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractCustomizableLifecycleMapping.java
index 76a32658..4a830cf7 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractCustomizableLifecycleMapping.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractCustomizableLifecycleMapping.java
@@ -59,30 +59,32 @@ public abstract class AbstractCustomizableLifecycleMapping extends AbstractLifec
List<AbstractBuildParticipant> executionMappings = new ArrayList<AbstractBuildParticipant>();
if(executionMetadatas != null) {
for(IPluginExecutionMetadata executionMetadata : executionMetadatas) {
- log.debug("\tAction: {}", executionMetadata.getAction());
switch(executionMetadata.getAction()) {
case execute:
+ log.debug("\tAction: {}", executionMetadata.getAction());
executionMappings.add(LifecycleMappingFactory.createMojoExecutionBuildParicipant(projectFacade,
projectFacade.getMojoExecution(mojoExecutionKey, monitor), executionMetadata));
break;
case configurator:
String configuratorId = LifecycleMappingFactory.getProjectConfiguratorId(executionMetadata);
- log.debug("\t\tProject configurator id: {}", configuratorId);
AbstractProjectConfigurator configurator = configurators.get(configuratorId);
if(configurator == null) {
- log.debug("\t\tProject configurator with id {} was not found");
+ log.debug("\t\tProject configurator with id {} was not found", configuratorId);
break;
}
- log.debug("\t\tProject configurator: {}", configurator.getClass().getName());
AbstractBuildParticipant buildParticipant = configurator.getBuildParticipant(projectFacade,
projectFacade.getMojoExecution(mojoExecutionKey, monitor), executionMetadata);
if(buildParticipant != null) {
+ log.debug("\tAction: {}", executionMetadata.getAction());
+ log.debug("\t\tProject configurator : id={} class={}", configuratorId, configurator.getClass()
+ .getName());
log.debug("\t\tBuild participant: {}", buildParticipant.getClass().getName());
executionMappings.add(buildParticipant);
}
break;
case ignore:
case error:
+ log.debug("\tAction: {}", executionMetadata.getAction());
break;
default:
throw new IllegalArgumentException("Missing handling for action=" + executionMetadata.getAction());
diff --git a/org.eclipse.m2e.tests.common/src/org/eclipse/m2e/tests/common/WorkspaceHelpers.java b/org.eclipse.m2e.tests.common/src/org/eclipse/m2e/tests/common/WorkspaceHelpers.java
index 4cb44f51..8efe5730 100644
--- a/org.eclipse.m2e.tests.common/src/org/eclipse/m2e/tests/common/WorkspaceHelpers.java
+++ b/org.eclipse.m2e.tests.common/src/org/eclipse/m2e/tests/common/WorkspaceHelpers.java
@@ -246,7 +246,8 @@ public class WorkspaceHelpers {
List<IMarker> markers = findMarkers(project, severity);
IMarker marker = findMarker(type, message, lineNumber, resourceRelativePath, markers);
if(marker == null) {
- Assert.fail("Marker not found. Found markers:" + toString(markers));
+ Assert.fail("Expected marker not found. Found " + (markers.isEmpty() ? "no markers" : "markers :")
+ + toString(markers));
}
Assert.assertTrue("Marker type " + type + " is not a subtype of " + IMarker.PROBLEM,
marker.isSubtypeOf(IMarker.PROBLEM));

Back to the top