415021 : try to prevent NPE when calling VirtualComponent.setReferences
Change-Id: I4074cfd9ccadb605425bdd62f53393b6c3bef768
Signed-off-by: Fred Bricon <fbricon@gmail.com>
diff --git a/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/WebProjectConfiguratorDelegate.java b/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/WebProjectConfiguratorDelegate.java
index 6d68aa5..89beed6 100644
--- a/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/WebProjectConfiguratorDelegate.java
+++ b/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/WebProjectConfiguratorDelegate.java
@@ -48,6 +48,7 @@
import org.eclipse.m2e.wtp.internal.ExtensionReader;
import org.eclipse.m2e.wtp.internal.Messages;
import org.eclipse.m2e.wtp.internal.filtering.WebResourceFilteringConfiguration;
+import org.eclipse.m2e.wtp.internal.utilities.ComponentModuleUtil;
import org.eclipse.m2e.wtp.namemapping.FileNameMapping;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
@@ -61,19 +62,21 @@
import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Configures web projects based on their maven-war-plugin configuration.
- *
+ *
* @author Igor Fedorenko
* @author Fred Bricon
*/
@SuppressWarnings("restriction")
class WebProjectConfiguratorDelegate extends AbstractProjectConfiguratorDelegate {
- //private static final Logger LOG = LoggerFactory.getLogger(WebProjectConfiguratorDelegate.class);
+ private static final Logger LOG = LoggerFactory.getLogger(WebProjectConfiguratorDelegate.class);
/**
* See http://wiki.eclipse.org/ClasspathEntriesPublishExportSupport
*/
@@ -84,7 +87,7 @@
* Name of maven property that overrides WTP context root.
*/
private static final String M2ECLIPSE_WTP_CONTEXT_ROOT = "m2eclipse.wtp.contextRoot"; //$NON-NLS-1$
-
+
@Override
protected void configure(IProject project, MavenProject mavenProject, IProgressMonitor monitor)
throws CoreException {
@@ -94,32 +97,32 @@
// make sure to update the main deployment folder
WarPluginConfiguration config = new WarPluginConfiguration(mavenProject, project);
String warSourceDirectory = config.getWarSourceDirectory();
-
+
IFolder contentFolder = project.getFolder(warSourceDirectory);
Set<Action> actions = new LinkedHashSet<>();
installJavaFacet(actions, project, facetedProject);
-
+
IVirtualComponent component = ComponentCore.createComponent(project, true);
-
+
//MNGECLIPSE-2279 get the context root from the final name of the project, or artifactId by default.
String contextRoot = getContextRoot(mavenProject, config.getWarName());
-
+
IProjectFacetVersion webFv = config.getWebFacetVersion(project);
IDataModel webModelCfg = getWebModelConfig(warSourceDirectory, contextRoot);
if(!facetedProject.hasProjectFacet(WebFacetUtils.WEB_FACET)) {
removeConflictingFacets(facetedProject, webFv, actions);
actions.add(new IFacetedProject.Action(IFacetedProject.Action.Type.INSTALL, webFv, webModelCfg));
} else {
- IProjectFacetVersion projectFacetVersion = facetedProject.getProjectFacetVersion(WebFacetUtils.WEB_FACET);
+ IProjectFacetVersion projectFacetVersion = facetedProject.getProjectFacetVersion(WebFacetUtils.WEB_FACET);
if(webFv.getVersionString() != null && !webFv.getVersionString().equals(projectFacetVersion.getVersionString())){
actions.add(new IFacetedProject.Action(IFacetedProject.Action.Type.VERSION_CHANGE, webFv, webModelCfg));
}
}
String customWebXml = config.getCustomWebXml(project);
-
+
if(!actions.isEmpty()) {
ResourceCleaner fileCleaner = new ResourceCleaner(project, contentFolder);
try {
@@ -129,19 +132,19 @@
if (customWebXml != null) {
fileCleaner.addFiles(contentFolder.getFile("WEB-INF/web.xml").getProjectRelativePath()); //$NON-NLS-1$
}
-
+
facetedProject.modify(actions, monitor);
} finally {
//Remove any unwanted MANIFEST.MF the Facet installation has created
fileCleaner.cleanUp();
}
}
-
+
//MECLIPSEWTP-41 Fix the missing moduleCoreNature
fixMissingModuleCoreNature(project, monitor);
-
+
configureDeployedName(project, config.getWarName());
-
+
// MNGECLIPSE-632 remove test sources/resources from WEB-INF/classes
removeTestFolderLinks(project, mavenProject, monitor, "/WEB-INF/classes"); //$NON-NLS-1$
@@ -151,19 +154,19 @@
if (!contextRoot.equals(J2EEProjectUtilities.getServerContextRoot(project))) {
J2EEProjectUtilities.setServerContextRoot(project, contextRoot);
}
-
+
if (customWebXml != null) {
linkFileFirst(project, customWebXml, "/WEB-INF/web.xml", monitor); //$NON-NLS-1$
}
-
+
component = ComponentCore.createComponent(project, true);
- if(component != null) {
+ if(component != null) {
IVirtualFolder rootFolder = component.getRootFolder();
IPath warPath = new Path("/").append(contentFolder.getProjectRelativePath()); //$NON-NLS-1$
boolean warPathExists = WTPProjectsUtil.hasLink(project, ROOT_PATH, warPath, monitor);
if (!warPathExists) {
- component.getRootFolder().createLink(warPath, IVirtualResource.NONE, monitor);
+ component.getRootFolder().createLink(warPath, IVirtualResource.NONE, monitor);
}
IPath currentDefaultLocation = J2EEModuleVirtualComponent.getDefaultDeploymentDescriptorFolder(rootFolder);
if (currentDefaultLocation == null) {
@@ -172,16 +175,16 @@
//MECLIPSEWTP-22 support web filtered resources. Filtered resources directory must be declared BEFORE
//the regular web source directory. First resources discovered take precedence on deployment
IPath filteredFolder = new Path("/").append(WebResourceFilteringConfiguration.getTargetFolder(mavenProject, project)); //$NON-NLS-1$
-
+
boolean useBuildDir = MavenWtpPlugin.getDefault().getMavenWtpPreferencesManager().getPreferences(project).isWebMavenArchiverUsesBuildDirectory();
- boolean useWebresourcefiltering = config.getWebResources() != null
- && config.getWebResources().length > 0
+ boolean useWebresourcefiltering = config.getWebResources() != null
+ && config.getWebResources().length > 0
|| config.isFilteringDeploymentDescriptorsEnabled();
if (useBuildDir || useWebresourcefiltering) {
-
+
if (!useBuildDir && useWebresourcefiltering) {
- mavenMarkerManager.addMarker(project, MavenWtpConstants.WTP_MARKER_CONFIGURATION_ERROR_ID,
+ mavenMarkerManager.addMarker(project, MavenWtpConstants.WTP_MARKER_CONFIGURATION_ERROR_ID,
Messages.markers_mavenarchiver_output_settings_ignored_warning, -1, IMarker.SEVERITY_WARNING);
}
if (!WTPProjectsUtil.hasLink(project, ROOT_PATH, filteredFolder, monitor)) {
@@ -194,7 +197,7 @@
addComponentExclusionPatterns(component, config);
}
WTPProjectsUtil.removeWTPClasspathContainer(project);
-
+
setModuleDependencies(project, mavenProject, monitor);
}
@@ -213,26 +216,28 @@
if (!ModuleCoreNature.isFlexibleProject(project)) {
return;
}
-
- IVirtualComponent component = ComponentCore.createComponent(project,true);
- //if the attempt to create dependencies happens before the project is actually created, abort.
- //this will be created again when the project exists.
- if(component == null){
- return;
- }
//MECLIPSEWTP-41 Fix the missing moduleCoreNature
fixMissingModuleCoreNature(project, monitor);
-
+
+ IVirtualComponent component = ComponentModuleUtil.getOrCreateComponent(project, monitor);
+ //if the attempt to create dependencies happens before the project is actually created, abort.
+ //this will be created again when the project exists.
+ if(component == null){
+ LOG.error(project.getName() + "/.settings/org.eclipse.wst.common.component is missing or invalid. "
+ + "Skipping module dependency configuration. Deployment issues may arise.");
+ return;
+ }
+
WarPluginConfiguration config = new WarPluginConfiguration(mavenProject, project);
Map<Artifact, String> deployedArtifacts = getDeployedArtifacts(mavenProject.getArtifacts(), config);
-
- List<AbstractDependencyConfigurator> depConfigurators = ExtensionReader.readDependencyConfiguratorExtensions(projectManager,
+
+ List<AbstractDependencyConfigurator> depConfigurators = ExtensionReader.readDependencyConfiguratorExtensions(projectManager,
MavenPlugin.getMavenRuntimeManager(), mavenMarkerManager);
-
+
Set<IVirtualReference> references = new LinkedHashSet<>();
List<IMavenProjectFacade> exportedDependencies = getWorkspaceDependencies(project, mavenProject);
-
+
for(IMavenProjectFacade dependency : exportedDependencies) {
String depPackaging = dependency.getPackaging();
if ("pom".equals(depPackaging) //MNGECLIPSE-744 pom dependencies shouldn't be deployed //$NON-NLS-1$
@@ -240,9 +245,9 @@
|| "zip".equals(depPackaging)) { //$NON-NLS-1$
continue;
}
-
+
preConfigureDependencyProject(dependency, monitor);
-
+
if (!ModuleCoreNature.isFlexibleProject(dependency.getProject())) {
//Projects unsupported by WTP (ex. adobe flex projects) should not be added as references
continue;
@@ -250,7 +255,7 @@
MavenProject depMavenProject = dependency.getMavenProject(monitor);
IVirtualComponent depComponent = ComponentCore.createComponent(dependency.getProject());
-
+
ArtifactKey artifactKey = ArtifactHelper.toArtifactKey(depMavenProject.getArtifact());
//Get artifact using the proper classifier
Artifact artifact = ArtifactHelper.getArtifact(mavenProject.getArtifacts(), artifactKey);
@@ -259,8 +264,8 @@
artifact = depMavenProject.getArtifact();
}
String deployedName = deployedArtifacts.get(artifact);
-
- //an artifact in mavenProject.getArtifacts() doesn't have the "optional" value as depMavenProject.getArtifact();
+
+ //an artifact in mavenProject.getArtifacts() doesn't have the "optional" value as depMavenProject.getArtifact();
if (deployedName != null) {
IVirtualReference reference = ComponentCore.createReference(component, depComponent);
IPath path = new Path("/WEB-INF/lib"); //$NON-NLS-1$
@@ -271,18 +276,18 @@
}
IVirtualReference[] oldRefs = WTPProjectsUtil.extractHardReferences(component, false);
-
+
IVirtualReference[] newRefs = references.toArray(new IVirtualReference[references.size()]);
-
+
if (WTPProjectsUtil.hasChanged(oldRefs, newRefs)){
- //Only write in the .component file if necessary
+ //Only write in the .component file if necessary
IVirtualReference[] overlayRefs = WTPProjectsUtil.extractHardReferences(component, true);
IVirtualReference[] allRefs = new IVirtualReference[overlayRefs.length + newRefs.length];
System.arraycopy(newRefs, 0, allRefs, 0, newRefs.length);
System.arraycopy(overlayRefs, 0, allRefs, newRefs.length, overlayRefs.length);
component.setReferences(allRefs);
}
-
+
//TODO why a 2nd loop???
for(IMavenProjectFacade dependency : exportedDependencies) {
MavenProject depMavenProject = dependency.getMavenProject(monitor);
@@ -296,11 +301,11 @@
}
}
}
-
+
/**
* Get the context root from a maven web project
* @param mavenProject
- * @param warName
+ * @param warName
* @return the final name of the project if it exists, or the project's artifactId.
*/
protected String getContextRoot(MavenProject mavenProject, String warName) {
@@ -309,7 +314,7 @@
String property = mavenProject.getProperties().getProperty(M2ECLIPSE_WTP_CONTEXT_ROOT);
if (StringUtils.isBlank(property)) {
String finalName = warName;
- if (StringUtils.isBlank(finalName)
+ if (StringUtils.isBlank(finalName)
|| finalName.equals(mavenProject.getArtifactId() + "-" + mavenProject.getVersion())) { //$NON-NLS-1$
contextRoot = mavenProject.getArtifactId();
} else {
@@ -325,11 +330,11 @@
@Override
public void configureClasspath(IProject project, MavenProject mavenProject, IClasspathDescriptor classpath,
IProgressMonitor monitor) throws CoreException {
-
+
WarPluginConfiguration config = new WarPluginConfiguration(mavenProject, project);
Set<Artifact> artifacts = mavenProject.getArtifacts();
Map<Artifact, String> deployedArtifacts = getDeployedArtifacts(artifacts, config);
-
+
Iterator<IClasspathEntryDescriptor> iter = classpath.getEntryDescriptors().iterator();
while (iter.hasNext()) {
IClasspathEntryDescriptor descriptor = iter.next();
@@ -337,9 +342,9 @@
if (artifact == null) {
return;
}
-
+
String deployedName = deployedArtifacts.get(artifact);
-
+
if(deployedName == null || isWorkspaceProject(artifact)) {
descriptor.setClasspathAttribute(NONDEPENDENCY_ATTRIBUTE.getName(), NONDEPENDENCY_ATTRIBUTE.getValue());
continue;
@@ -349,32 +354,32 @@
}
private boolean isWorkspaceProject(Artifact artifact) {
- IMavenProjectFacade facade = projectManager.getMavenProject(artifact.getGroupId(),
+ IMavenProjectFacade facade = projectManager.getMavenProject(artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion());
-
+
return facade != null && facade.getFullPath(artifact.getFile()) != null;
}
-
+
private Map<Artifact, String> getDeployedArtifacts(Collection<Artifact> artifacts, WarPluginConfiguration config ) {
if (artifacts == null || artifacts.isEmpty()) {
return Collections.emptyMap();
}
int size = artifacts.size();
Map<Artifact, String> artifactsMap = new LinkedHashMap<>(size);
-
+
IPackagingConfiguration opts = new PackagingConfiguration(config.getPackagingIncludes(), config.getPackagingExcludes());
FileNameMapping fileNameMapping = config.getFileNameMapping();
-
+
Set<String> names = new HashSet<>(size);
Set<String> duplicates = new HashSet<>(size);
-
+
for (Artifact artifact : artifacts) {
ArtifactHelper.fixArtifactHandler(artifact.getArtifactHandler());
String deployedName = fileNameMapping.mapFileName(artifact);
String scope = artifact.getScope();
- boolean isDeployed = (Artifact.SCOPE_COMPILE.equals(scope) || Artifact.SCOPE_RUNTIME.equals(scope))
+ boolean isDeployed = (Artifact.SCOPE_COMPILE.equals(scope) || Artifact.SCOPE_RUNTIME.equals(scope))
&& !artifact.isOptional()
&& opts.isPackaged("WEB-INF/lib/"+deployedName); //$NON-NLS-1$
if (isDeployed) {
@@ -384,7 +389,7 @@
artifactsMap.put(artifact, deployedName);
}
}
-
+
//disambiguate duplicates
for (String name : duplicates) {
for (Map.Entry<Artifact, String> entry : artifactsMap.entrySet()) {
@@ -394,8 +399,8 @@
}
}
}
-
+
return artifactsMap;
}
-
+
}
diff --git a/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/internal/utilities/ComponentModuleUtil.java b/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/internal/utilities/ComponentModuleUtil.java
new file mode 100644
index 0000000..2ca3b1b
--- /dev/null
+++ b/org.eclipse.m2e.wtp/src/org/eclipse/m2e/wtp/internal/utilities/ComponentModuleUtil.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.m2e.wtp.internal.utilities;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.wst.common.componentcore.ComponentCore;
+import org.eclipse.wst.common.componentcore.internal.StructureEdit;
+import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
+
+/**
+ * ComponentModule Utility class
+ *
+ * @author Fred Bricon
+ */
+@SuppressWarnings("restriction")
+public class ComponentModuleUtil {
+
+ private ComponentModuleUtil() {
+ }
+
+ public static IVirtualComponent getOrCreateComponent(IProject project, IProgressMonitor monitor) {
+ IVirtualComponent component = ComponentCore.createComponent(project, true);
+ if (component != null) {
+ StructureEdit core = null;
+ try {
+ core = StructureEdit.getStructureEditForRead(project);
+ if(core != null) {
+ //For some reason, we're facing a lot of issues with
+ // component.setReferences failing with an NPE because of
+ // the underlying core.getComponent() being null but not being Null-Checked
+
+ if (core.getComponent() == null){
+ //We could try to fix the missing workbench module but we're already
+ //drowning in a sea of hacks to workaround WTP quirks
+
+ //core.createWorkbenchModule(project.getName());
+ //core.saveIfNecessary(monitor);
+ return null;
+ }
+ }
+ } finally {
+ if(core != null) {
+ core.dispose();
+ }
+ }
+ }
+ return component;
+ }
+
+}