blob: 4cc4c9afdc7d07272072493d00d75ea7d4478e35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Sonatype, 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;
import static org.eclipse.m2e.wtp.internal.StringUtils.joinAsString;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.project.MavenProject;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.j2ee.classpathdep.IClasspathDependencyConstants;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.MavenPluginActivator;
import org.eclipse.m2e.core.internal.markers.IMavenMarkerManager;
import org.eclipse.m2e.core.internal.markers.SourceLocation;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.IMavenProjectRegistry;
import org.eclipse.m2e.core.project.MavenProjectUtils;
import org.eclipse.m2e.jdt.IClasspathDescriptor;
import org.eclipse.m2e.wtp.internal.Messages;
import org.eclipse.m2e.wtp.internal.utilities.DebugUtilities;
import org.eclipse.m2e.wtp.internal.webfragment.WebFragmentUtil;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.internal.StructureEdit;
import org.eclipse.wst.common.componentcore.internal.WorkbenchComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualReference;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Base class to configure JavaEE projects
*
* @author Igor Fedorenko
* @author Fred Bricon
*
* @provisional This class has been added as part of a work in progress.
* It is not guaranteed to work or remain the same in future releases.
* For more information contact <a href="mailto:m2e-wtp-dev@eclipse.org">m2e-wtp-dev@eclipse.org</a>.
*
*/
abstract class AbstractProjectConfiguratorDelegate implements IProjectConfiguratorDelegate {
private static final Logger LOG = LoggerFactory.getLogger(AbstractProjectConfiguratorDelegate.class);
static final IClasspathAttribute NONDEPENDENCY_ATTRIBUTE = JavaCore.newClasspathAttribute(
IClasspathDependencyConstants.CLASSPATH_COMPONENT_NON_DEPENDENCY, ""); //$NON-NLS-1$
protected static final IPath ROOT_PATH = new Path("/"); //$NON-NLS-1$
protected final IMavenProjectRegistry projectManager;
protected final IMavenMarkerManager mavenMarkerManager;
AbstractProjectConfiguratorDelegate() {
this.projectManager = MavenPlugin.getMavenProjectRegistry();
this.mavenMarkerManager = MavenPluginActivator.getDefault().getMavenMarkerManager();
}
@Override
public void configureProject(IProject project, MavenProject mavenProject, IProgressMonitor monitor) throws MarkedException {
try {
mavenMarkerManager.deleteMarkers(project,MavenWtpConstants.WTP_MARKER_CONFIGURATION_ERROR_ID);
configure(project, mavenProject, monitor);
} catch (CoreException cex) {
//TODO Filter out constraint violations
mavenMarkerManager.addErrorMarkers(project, MavenWtpConstants.WTP_MARKER_CONFIGURATION_ERROR_ID, cex);
throw new MarkedException(NLS.bind(Messages.AbstractProjectConfiguratorDelegate_Unable_To_Configure_Project,project.getName()), cex);
}
}
protected abstract void configure(IProject project, MavenProject mavenProject, IProgressMonitor monitor) throws CoreException;
protected List<IMavenProjectFacade> getWorkspaceDependencies(IProject project, MavenProject mavenProject) {
Set<IProject> projects = new HashSet<>();
List<IMavenProjectFacade> dependencies = new ArrayList<>();
Set<Artifact> artifacts = mavenProject.getArtifacts();
for(Artifact artifact : artifacts) {
IMavenProjectFacade dependency = projectManager.getMavenProject(artifact.getGroupId(), artifact.getArtifactId(),
artifact.getVersion());
if((Artifact.SCOPE_COMPILE.equals(artifact.getScope())
|| Artifact.SCOPE_RUNTIME.equals(artifact.getScope())) //MNGECLIPSE-1578 Runtime dependencies should be deployed
&& dependency != null && !dependency.getProject().equals(project) && dependency.getFullPath(artifact.getFile()) != null
&& projects.add(dependency.getProject())) {
dependencies.add(dependency);
}
}
return dependencies;
}
protected void configureWtpUtil(IMavenProjectFacade facade, IProgressMonitor monitor) throws CoreException {
// Adding utility facet on JEE projects is not allowed
IProject project = facade.getProject();
MavenProject mavenProject = facade.getMavenProject();
if( !WTPProjectsUtil.isJavaProject(facade)
|| WTPProjectsUtil.isJavaEEProject(project)
|| WebFragmentUtil.isQualifiedAsWebFragment(facade)) {
return;
}
//MECLIPSEWTP-66 delete extra MANIFEST.MF
IPath[] sourceRoots = MavenProjectUtils.getSourceLocations(project, mavenProject.getCompileSourceRoots());
IPath[] resourceRoots = MavenProjectUtils.getResourceLocations(project, mavenProject.getResources());
//MECLIPSEWTP-182 check if the Java Project configurator has been successfully run before doing anything :
if (!checkJavaConfiguration(project, sourceRoots, resourceRoots)) {
LOG.warn(NLS.bind(Messages.AbstractProjectConfiguratorDelegate_Error_Inconsistent_Java_Configuration, project.getName()));
return;
}
boolean isDebugEnabled = DebugUtilities.isDebugEnabled();
if (isDebugEnabled) {
DebugUtilities.debug(DebugUtilities.dumpProjectState("Before configuration ",project)); //$NON-NLS-1$
}
// 2 - check if the manifest already exists, and its parent folder
IFacetedProject facetedProject = ProjectFacetsManager.create(project, true, monitor);
Set<Action> actions = new LinkedHashSet<>();
installJavaFacet(actions, project, facetedProject);
if(!facetedProject.hasProjectFacet(WTPProjectsUtil.UTILITY_FACET)) {
actions.add(new IFacetedProject.Action(IFacetedProject.Action.Type.INSTALL, WTPProjectsUtil.UTILITY_10, null));
} else if(!facetedProject.hasProjectFacet(WTPProjectsUtil.UTILITY_10)) {
actions.add(new IFacetedProject.Action(IFacetedProject.Action.Type.VERSION_CHANGE, WTPProjectsUtil.UTILITY_10,
null));
}
if (!actions.isEmpty()) {
ResourceCleaner fileCleaner = new ResourceCleaner(project);
try {
addFoldersToClean(fileCleaner, facade);
facetedProject.modify(actions, monitor);
} finally {
//Remove any unwanted MANIFEST.MF the Facet installation has created
fileCleaner.cleanUp();
}
}
fixMissingModuleCoreNature(project, monitor);
if (isDebugEnabled) {
DebugUtilities.debug(DebugUtilities.dumpProjectState("after configuration ",project)); //$NON-NLS-1$
}
//MNGECLIPSE-904 remove tests folder links for utility jars
removeTestFolderLinks(project, mavenProject, monitor, "/"); //$NON-NLS-1$
//Remove "library unavailable at runtime" warning.
if (isDebugEnabled) {
DebugUtilities.debug(DebugUtilities.dumpProjectState("after removing test folders ",project)); //$NON-NLS-1$
}
setNonDependencyAttributeToContainer(project, monitor);
WTPProjectsUtil.removeWTPClasspathContainer(project);
}
/**
* Checks the maven source folders are correctly added to the project classpath
*/
private boolean checkJavaConfiguration(IProject project, IPath[] sourceRoots, IPath[] resourceRoots) throws JavaModelException {
IJavaProject javaProject = JavaCore.create(project);
if (javaProject == null) {
return false;
}
IClasspathEntry[] cpEntries = javaProject.getRawClasspath();
if (cpEntries == null) {
return false;
}
Set<IPath> currentPaths = new HashSet<>();
for (IClasspathEntry entry : cpEntries) {
if (IClasspathEntry.CPE_SOURCE == entry.getEntryKind()){
currentPaths.add(entry.getPath().makeRelativeTo(project.getFullPath()));
}
}
for(IPath mavenSource : sourceRoots) {
if (mavenSource != null && !mavenSource.isEmpty()) {
IFolder sourceFolder = project.getFolder(mavenSource);
if (sourceFolder.exists() && !currentPaths.contains(mavenSource)) {
return false;
}
}
}
for(IPath mavenSource : resourceRoots) {
if (mavenSource != null && !mavenSource.isEmpty()) {
IFolder resourceFolder = project.getFolder(mavenSource);
if (resourceFolder.exists() && !currentPaths.contains(mavenSource)) {
return false;
}
}
}
return true;
}
/**
* Add the ModuleCoreNature to a project, if necessary.
*
* @param project An accessible project.
* @param monitor A progress monitor to track the time to completion
* @throws CoreException if the ModuleCoreNature cannot be added
*/
protected void fixMissingModuleCoreNature(IProject project, IProgressMonitor monitor) throws CoreException {
WTPProjectsUtil.fixMissingModuleCoreNature(project, monitor);
}
protected void installJavaFacet(Set<Action> actions, IProject project, IFacetedProject facetedProject) {
WTPProjectsUtil.installJavaFacet(actions, project, facetedProject);
}
protected void removeTestFolderLinks(IProject project, MavenProject mavenProject, IProgressMonitor monitor,
String folder) throws CoreException {
WTPProjectsUtil.removeTestFolderLinks(project, mavenProject, monitor, folder);
}
protected void addContainerAttribute(IProject project, IClasspathAttribute attribute, IProgressMonitor monitor)
throws JavaModelException {
updateContainerAttributes(project, attribute, null, monitor);
}
protected void setNonDependencyAttributeToContainer(IProject project, IProgressMonitor monitor) throws JavaModelException {
WTPProjectsUtil.updateContainerAttributes(project, NONDEPENDENCY_ATTRIBUTE, IClasspathDependencyConstants.CLASSPATH_COMPONENT_DEPENDENCY, monitor);
}
protected void updateContainerAttributes(IProject project, IClasspathAttribute attributeToAdd, String attributeToDelete, IProgressMonitor monitor)
throws JavaModelException {
WTPProjectsUtil.updateContainerAttributes(project, attributeToAdd, attributeToDelete, monitor);
}
/**
* @param dependencyMavenProjectFacade
* @param monitor
* @return
* @throws CoreException
*/
protected IProject preConfigureDependencyProject(IMavenProjectFacade dependencyMavenProjectFacade, IProgressMonitor monitor) throws CoreException {
IProject dependency = dependencyMavenProjectFacade.getProject();
String depPackaging = dependencyMavenProjectFacade.getPackaging();
//jee dependency has not been configured yet - i.e. it has no JEE facet-
if(!JEEPackaging.isJEEPackaging(depPackaging)) {
// XXX Probably should create a UtilProjectConfiguratorDelegate
configureWtpUtil(dependencyMavenProjectFacade, monitor);
}
return dependency;
}
@SuppressWarnings("restriction")
protected void configureDeployedName(IProject project, String deployedFileName) {
IVirtualComponent projectComponent = ComponentCore.createComponent(project);
if(projectComponent != null && !deployedFileName.equals(projectComponent.getDeployedName())){//MNGECLIPSE-2331 : Seems projectComponent.getDeployedName() can be null
StructureEdit moduleCore = null;
try {
moduleCore = StructureEdit.getStructureEditForWrite(project);
if (moduleCore != null){
WorkbenchComponent component = moduleCore.getComponent();
if (component != null) {
component.setName(deployedFileName);
moduleCore.saveIfNecessary(null);
}
}
} finally {
if (moduleCore != null) {
moduleCore.dispose();
}
}
}
}
/**
* Link a project's file to a specific deployment destination. Existing links will be deleted beforehand.
* @param project
* @param sourceFile the existing file to deploy
* @param targetRuntimePath the target runtime/deployment location of the file
* @param monitor
* @throws CoreException
*/
protected void linkFileFirst(IProject project, String sourceFile, String targetRuntimePath, IProgressMonitor monitor) throws CoreException {
IPath runtimePath = new Path(targetRuntimePath);
//We first delete any existing links
WTPProjectsUtil.deleteLinks(project, runtimePath, monitor);
if (sourceFile != null) {
//Create the new link
WTPProjectsUtil.insertLinkFirst(project, new Path(sourceFile), new Path(targetRuntimePath), monitor);
}
}
@Deprecated
protected boolean hasChanged(IVirtualReference[] existingRefs, IVirtualReference[] refArray) {
return WTPProjectsUtil.hasChanged(existingRefs, refArray);
}
@Override
public void configureClasspath(IProject project, MavenProject mavenProject, IClasspathDescriptor classpath,
IProgressMonitor monitor) throws CoreException {
// do nothing
}
@Override
public void setModuleDependencies(IProject project, MavenProject mavenProject, IProgressMonitor monitor)
throws CoreException {
// do nothing
}
protected void addFoldersToClean(ResourceCleaner fileCleaner, IMavenProjectFacade facade) {
for (IPath p : facade.getCompileSourceLocations()) {
if (p != null) {
fileCleaner.addFiles(p.append("META-INF/MANIFEST.MF")); //$NON-NLS-1$
fileCleaner.addFolder(p);
}
}
for (IPath p : facade.getResourceLocations()) {
if (p != null) {
fileCleaner.addFiles(p.append("META-INF/MANIFEST.MF")); //$NON-NLS-1$
fileCleaner.addFolder(p);
}
}
// add default resource folder
IPath defaultResource = new Path("src/main/resources"); //$NON-NLS-1$
fileCleaner.addFiles(defaultResource.append("META-INF/MANIFEST.MF")); //$NON-NLS-1$
fileCleaner.addFolder(defaultResource);
for (IPath p : facade.getTestCompileSourceLocations()) {
if (p != null) fileCleaner.addFolder(p);
}
for (IPath p : facade.getTestResourceLocations()) {
if (p != null) fileCleaner.addFolder(p);
}
}
/**
* Add inclusion/exclusion patterns to .component metadata. WTP server adapters can use that information to
* include/exclude resources from deployment accordingly. This is currently implemented in the JBoss AS server adapter.
* @throws CoreException
*/
protected void addComponentExclusionPatterns(IVirtualComponent component, IMavenPackageFilter filter) {
String[] warSourceIncludes = filter.getSourceIncludes();
String[] packagingIncludes = filter.getPackagingIncludes();
String[] warSourceExcludes = filter.getSourceExcludes();
String[] packagingExcludes = filter.getPackagingExcludes();
if (warSourceIncludes.length > 0 && packagingIncludes.length >0) {
IResource pomFile = component.getProject().getFile(IMavenConstants.POM_FILE_NAME);
// We might get bad pattern overlapping (**/* + **/*.html would return everything,
// when maven would only package html files) .
// So we arbitrary (kinda) keep the packaging patterns only. But this can lead to other funny discrepancies
// things like **/pages/** + **/*.html should return only html files from the pages directory, but here, will return
// every html files.
SourceLocation sourceLocation = filter.getSourceLocation();
if (sourceLocation != null) {
mavenMarkerManager.addMarker(pomFile,
MavenWtpConstants.WTP_MARKER_CONFIGURATION_ERROR_ID,
NLS.bind(Messages.markers_inclusion_patterns_problem, filter.getSourceIncludeParameterName()),
sourceLocation.getLineNumber(),
IMarker.SEVERITY_WARNING);
}
warSourceIncludes = null;
}
String componentInclusions = joinAsString(warSourceIncludes, packagingIncludes);
String componentExclusions = joinAsString(warSourceExcludes, packagingExcludes);
Properties props = component.getMetaProperties();
if (!componentInclusions.equals(props.getProperty(MavenWtpConstants.COMPONENT_INCLUSION_PATTERNS, ""))) { //$NON-NLS-1$
component.setMetaProperty(MavenWtpConstants.COMPONENT_INCLUSION_PATTERNS, componentInclusions);
}
if (!componentExclusions.equals(props.getProperty(MavenWtpConstants.COMPONENT_EXCLUSION_PATTERNS, ""))) { //$NON-NLS-1$
component.setMetaProperty(MavenWtpConstants.COMPONENT_EXCLUSION_PATTERNS, componentExclusions);
}
}
}