Java tutorial
/******************************************************************************* * Copyright (c) 2011 Knowledge Computing Corp. * 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: * Karl M. Davis (Knowledge Computing Corp.) - initial API and implementation *******************************************************************************/ package org.jboss.tools.maven.apt; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.eclipse.core.resources.IFile; 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.jdt.apt.core.util.AptConfig; import org.eclipse.jdt.apt.core.util.IFactoryPath; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.project.MavenProject; import org.eclipse.m2e.core.project.IMavenProjectFacade; import org.eclipse.m2e.core.project.MavenProjectChangedEvent; import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; import org.eclipse.m2e.jdt.IClasspathDescriptor; import org.eclipse.m2e.jdt.IClasspathEntryDescriptor; import org.eclipse.m2e.jdt.IJavaProjectConfigurator; import org.jboss.tools.maven.apt.AnnotationServiceLocator.ServiceEntry; /** * <p> * This {@link AbstractProjectConfigurator} implementation will set the APT configuration for an Eclipse Java project. * </p> * <p> * Please note that the <code>maven-compiler-plugin</code> (at least as of version 2.3.2) will automatically perform * annotation processing and generate annotation sources. This processing will include all annotation processors in the * project's compilation classpath. * </p> * <p> * However, there are a couple of problems that prevent the <code>maven-compiler-plugin</code>'s annotation processing * from being sufficient when run within m2eclipse: * </p> * <ul> * <li>The generated annotation sources are not added to the Maven project's source folders (nor should they be) and are * thus not found by m2eclipse.</li> * <li>Due to contention between Eclipse's JDT compilation and <code>maven-compiler-plugin</code> compilation, the Java * compiler used by Eclipse may not recognize when the generated annotation sources/classes are out of date.</li> * </ul> * <p> * The {@link AptProjectConfigurator} works around those limitations by configuring Eclipse's built-in annotation * processing: APT. Unfortunately, the APT configuration will not allow for libraries, such as m2eclipse's * "Maven Dependencies" to be used in the search path for annotation processors. Instead, the * {@link AptProjectConfigurator} adds all of the project's <code>.jar</code> dependencies to the annotation processor * search path. * </p> */ public final class AptProjectConfigurator extends AbstractProjectConfigurator implements IJavaProjectConfigurator { /** * The <code>groupId</code> of the <a href="http://maven.apache.org/plugins/maven-compiler-plugin/">Maven Compiler * Plugin</a>. */ private static final String COMPILER_PLUGIN_GROUP_ID = "org.apache.maven.plugins"; /** * The <code>artifactId</code> of the <a href="http://maven.apache.org/plugins/maven-compiler-plugin/">Maven Compiler * Plugin</a>. */ private static final String COMPILER_PLUGIN_ARTIFACT_ID = "maven-compiler-plugin"; /** * The name of the configuration element that holds the compiler argument configuration if the * <a href="http://maven.apache.org/plugins/maven-compiler-plugin/">Maven Compiler Plugin</a>. */ private static final String COMPILER_ARGUMENT_ELEMENT = "compilerArgument"; /** * The name of the <a href="http://maven.apache.org/plugins/maven-compiler-plugin/">Maven Compiler Plugin</a>'s * "compile" goal. */ private static final String GOAL_COMPILE = "compile"; /** * The {@link IMarker#setAttribute(String, Object)} key used for all {@link IMarker}s created by * {@link AptProjectConfigurator}, to identify the reason it was created. */ private static final String MARKER_ATTRIB_TYPE = AptProjectConfigurator.class.getName() + ".type"; /** * The {@link IMarker#setAttribute(String, Object)} value used to identify "update your project" markers. * * @see #MARKER_ATTRIB_TYPE */ private static final String MARKER_ATTRIB_TYPE_UPDATE = "updateProject"; private static final Logger log = LoggerFactory.getLogger(AptProjectConfigurator.class); /** * {@inheritDoc} */ @Override public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { // This method may be called with null parameters to ensure its API is correct. We // can ignore such calls. if (request == null || monitor == null) return; // Clear any old "update your project" markers IResource pomResource = findPomResource(request.getMavenProjectFacade()); clearUpdateWarnings(pomResource); // Get the objects needed for APT configuration IMavenProjectFacade mavenProjectFacade = request.getMavenProjectFacade(); IProject eclipseProject = mavenProjectFacade.getProject(); MavenSession mavenSession = request.getMavenSession(); // Configure APT configureAptForProject(eclipseProject, mavenSession, mavenProjectFacade, monitor); } /** * {@inheritDoc} */ @Override public void mavenProjectChanged(MavenProjectChangedEvent event, IProgressMonitor monitor) throws CoreException { super.mavenProjectChanged(event, monitor); /* * I'm not sure we want to re-configure the Eclipse project every time the POM * changes-- might end up overwriting the user's manual changes to the Eclipse * project. If we do decide to do that, we'll need to create a new MavenSession, * as done in ProjectConfigurationManager.createMavenSession(...). */ // configureAptForProject(eclipseProject, mavenSession, mavenProjectFacade, monitor); /* * Additionally, it doesn't seem that applying an update warning here works correctly: the * mavenProjectChanged(...) event seems to be fired after every configure(...) event. This * causes any markers to be removed and then added again every time APT is configured. */ // IResource pomResource = findPomResource(event.getMavenProject()); // applyUpdateWarning(pomResource); } /** * {@inheritDoc} */ public void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath, IProgressMonitor monitor) { /* * Implementations of this method are supposed to configure the Maven project * classpath: the "Maven Dependencies" container. We don't have any need to do * that here. */ } /** * {@inheritDoc} */ public void configureRawClasspath(ProjectConfigurationRequest request, IClasspathDescriptor classpath, IProgressMonitor monitor) throws CoreException { /* * We need to prevent/recover from the JavaProjectConfigurator removing the * generated annotation sources directory from the classpath: it will be added * when we configure the Eclipse APT preferences and then removed when the * JavaProjectConfigurator runs. */ // Get the various project references we'll need IProject eclipseProject = request.getProject(); MavenProject mavenProject = request.getMavenProject(); IMavenProjectFacade projectFacade = request.getMavenProjectFacade(); // If this isn't a Java project, we have nothing to do if (!eclipseProject.hasNature(JavaCore.NATURE_ID)) return; //If APT is not enabled, nothing to do either IJavaProject javaProject = JavaCore.create(eclipseProject); if (!AptConfig.isEnabled(javaProject)) { return; } // If this project has no valid compiler plugin config, we have nothing to do File generatedSourcesDirectory = getGeneratedSourcesDirectory(request.getMavenSession(), projectFacade, monitor); if (generatedSourcesDirectory == null) return; // Get the generated annotation sources directory as an IFolder File generatedSourcesRelativeDirectory = convertToProjectRelativePath(eclipseProject, generatedSourcesDirectory); String generatedSourcesRelativeDirectoryPath = generatedSourcesRelativeDirectory.getPath(); IFolder generatedSourcesFolder = eclipseProject.getFolder(generatedSourcesRelativeDirectoryPath); // Get the output folder to use as an IPath File outputFile = new File(mavenProject.getBuild().getOutputDirectory()); File outputRelativeFile = convertToProjectRelativePath(eclipseProject, outputFile); IFolder outputFolder = eclipseProject.getFolder(outputRelativeFile.getPath()); IPath outputPath = outputFolder.getFullPath(); // Create the includes & excludes specifiers IPath[] includes = new IPath[] {}; IPath[] excludes = new IPath[] {}; // If the source folder exists and is non-nested, add it if (generatedSourcesFolder != null && generatedSourcesFolder.exists() && generatedSourcesFolder.getProject().equals(eclipseProject)) { IClasspathEntryDescriptor cped = getEnclosingEntryDescriptor(classpath, generatedSourcesFolder.getFullPath()); if (cped == null) { classpath.addSourceEntry(generatedSourcesFolder.getFullPath(), outputPath, includes, excludes, false); } } else { if (generatedSourcesFolder != null) { classpath.removeEntry(generatedSourcesFolder.getFullPath()); } } } /** * Returns the {@link IClasspathEntryDescriptor} in the specified {@link IClasspathDescriptor} that is a prefix of the * specified {@link IPath}. * * @param classpath the {@link IClasspathDescriptor} to be searched for a matching {@link IClasspathEntryDescriptor} * @param path the {@link IPath} to find a matching {@link IClasspathEntryDescriptor} for * @return the {@link IClasspathEntryDescriptor} in the specified {@link IClasspathDescriptor} that is a prefix of the * specified {@link IPath} */ private static IClasspathEntryDescriptor getEnclosingEntryDescriptor(IClasspathDescriptor classpath, IPath path) { for (IClasspathEntryDescriptor cped : classpath.getEntryDescriptors()) { if (cped.getPath().isPrefixOf(path)) { return cped; } } return null; } /** * Configures APT for the specified Maven project. * * @param eclipseProject an {@link IProject} reference to the Eclipse project being configured * @param mavenProject {@link IMavenProjectFacade} reference to the Maven project being configured * @param monitor the {@link IProgressMonitor} to use * @throws CoreException Any {@link CoreException}s thrown will be passed through. */ private void configureAptForProject(IProject eclipseProject, MavenSession mavenSession, IMavenProjectFacade mavenProjectFacade, IProgressMonitor monitor) throws CoreException { IJavaProject javaProject = JavaCore.create(eclipseProject); File generatedSourcesDirectory = getGeneratedSourcesDirectory(mavenSession, mavenProjectFacade, monitor); // If this isn't a Java project, we have nothing to do if (!eclipseProject.hasNature(JavaCore.NATURE_ID)) return; // If this project has no valid compiler plugin config, we have nothing to do if (generatedSourcesDirectory == null) return; // Get the project's dependencies List<Artifact> artifacts = getProjectArtifacts(mavenProjectFacade); List<File> resolvedJarArtifacts = filterToResolvedJars(artifacts); // Inspect the dependencies to see if any contain APT processors boolean isAnnotationProcessingEnabled = isAnnotationProcessingEnabled(mavenSession, mavenProjectFacade, monitor) //Will be ignored when org.bsc.maven:maven-processor-plugin is used && containsAptProcessors(resolvedJarArtifacts); // Enable/Disable APT (depends on whether APT processors were found) AptConfig.setEnabled(javaProject, isAnnotationProcessingEnabled); //If no annotation processor were found, we should leave. if (!isAnnotationProcessingEnabled) { return; } // Configure APT output path File generatedSourcesRelativeDirectory = convertToProjectRelativePath(eclipseProject, generatedSourcesDirectory); String generatedSourcesRelativeDirectoryPath = generatedSourcesRelativeDirectory.getPath(); AptConfig.setGenSrcDir(javaProject, generatedSourcesRelativeDirectoryPath); /* * Add all of the compile-scoped JAR artifacts to a new IFactoryPath (in * addition to the workspace's default entries). * * Please note that--until JDT-APT supports project factory path entries * (as opposed to just JARs)--this will be a bit wonky. Specifically, any * project dependencies will be excluded, but their transitive JAR * dependencies will be included. * * Also note: we add the artifacts in reverse order as * IFactoryPath.addExternalJar(File) adds items to the top of the factory * list. */ List<File> resolvedJarArtifactsInReverseOrder = new ArrayList<File>(resolvedJarArtifacts); Collections.reverse(resolvedJarArtifactsInReverseOrder); IFactoryPath factoryPath = AptConfig.getDefaultFactoryPath(javaProject); for (File resolvedJarArtifact : resolvedJarArtifactsInReverseOrder) { factoryPath.addExternalJar(resolvedJarArtifact); } // Apply that IFactoryPath to the project AptConfig.setFactoryPath(javaProject, factoryPath); // Apply and processor option found on the compiler plugins Map<String, String> processorOptions = getProcessorOptions(mavenSession, mavenProjectFacade, monitor); if (!processorOptions.isEmpty()) { AptConfig.setProcessorOptions(processorOptions, javaProject); } } /** * Check to see if there are any compiler plugins defined in the project that will cause the * annotation processor to run. * * Specifically we look for places where it is explicitly disabled since on is the default option. * * @param mavenSession the {@link MavenSession} being used * @param mavenProjectFacade the {@link IMavenProjectFacade} of the project to get the * annotation processor options configuration parameters from * @param monitor the {@link IProgressMonitor} for this operation * @return true if configured compiler plugins will cause an annotation processor to run, else return false. * @throws CoreException Any {@link CoreException}s encountered will be passed through. */ private boolean isAnnotationProcessingEnabled(MavenSession mavenSession, IMavenProjectFacade mavenProjectFacade, IProgressMonitor monitor) throws CoreException { boolean processorWillRun = false; // defaults to false if no compiler configured in the project for (MojoExecution mojoExecution : mavenProjectFacade.getMojoExecutions(COMPILER_PLUGIN_GROUP_ID, COMPILER_PLUGIN_ARTIFACT_ID, monitor, GOAL_COMPILE)) { String compilerArgument = maven.getMojoParameterValue(mavenSession, mojoExecution, COMPILER_ARGUMENT_ELEMENT, String.class); String proc = maven.getMojoParameterValue(mavenSession, mojoExecution, "proc", String.class); // the annotation processor will run unless -proc:none is given in the compilerArgument if (compilerArgument != null) { processorWillRun = !compilerArgument.contains("-proc:none"); } // the annotation processor will run unless none is given in the proc config element if (proc != null) { processorWillRun = !"none".equals(proc.trim().toLowerCase()); } // the annotation processor will run if neither of the two config options is given if (proc == null && compilerArgument == null) { processorWillRun = true; } // if the processor will run there is no point looking at other compiler plugin configs if (processorWillRun) { break; } } return processorWillRun; } /** * @param mavenProjectFacade the {@link IMavenProjectFacade} to get the {@link Artifact}s for * @return an ordered {@link List} of the specified {@link IMavenProjectFacade}'s {@link Artifact}s */ private static List<Artifact> getProjectArtifacts(IMavenProjectFacade mavenProjectFacade) { /* * This method essentially wraps org.apache.maven.project.MavenProject.getArtifacts(), * returning a List instead of a Set to indicate that ordering is maintained and important. * The set being "wrapped" is actually a LinkedHashSet, which does guarantee a consistent * insertion & iteration order. */ Set<Artifact> unorderedArtifacts = mavenProjectFacade.getMavenProject().getArtifacts(); List<Artifact> orderedArtifacts = new ArrayList<Artifact>(unorderedArtifacts.size()); for (Artifact artifact : unorderedArtifacts) orderedArtifacts.add(artifact); return orderedArtifacts; } /** * Returns the <code>-A</code> processor options found in the <code>compilerArgument</code> * configuration element of the * {@link #COMPILER_PLUGIN_ARTIFACT_ID} plugin, or an empty map if the {@link #GOAL_COMPILE} is * not being executed for this project. * * @param mavenSession the {@link MavenSession} being used * @param mavenProjectFacade the {@link IMavenProjectFacade} of the project to get the * annotation processor options configuration parameters from * @param monitor the {@link IProgressMonitor} for this operation * @return a map of <code>-A</code> processor options found in the <code>compilerArgument</code> * configuration element of the {@link #COMPILER_PLUGIN_ARTIFACT_ID} plugin, or an * empty map if the {@link #GOAL_COMPILE} is not being executed for this project * @throws CoreException Any {@link CoreException}s encountered will be passed through. */ private Map<String, String> getProcessorOptions(MavenSession mavenSession, IMavenProjectFacade mavenProjectFacade, IProgressMonitor monitor) throws CoreException { Map<String, String> ret = new HashMap<String, String>(); for (MojoExecution mojoExecution : mavenProjectFacade.getMojoExecutions(COMPILER_PLUGIN_GROUP_ID, COMPILER_PLUGIN_ARTIFACT_ID, monitor, GOAL_COMPILE)) { String compilerArgument = maven.getMojoParameterValue(mavenSession, mojoExecution, COMPILER_ARGUMENT_ELEMENT, String.class); if (compilerArgument != null) { ret.putAll(extractProcessorOptionsFromCompilerArgument(compilerArgument)); } } return ret; } /** * @param compilerArgument * @return */ private static Map<String, String> extractProcessorOptionsFromCompilerArgument(String compilerArgument) { Map<String, String> ret = new HashMap<String, String>(); Pattern fullOptionPattern = Pattern.compile("-A([^ \\t\"']+)"); Matcher matcher = fullOptionPattern.matcher(compilerArgument); int start = 0; while (matcher.find(start)) { String argument = matcher.group(1); final String key; final String value; int optionalEqualsIndex = argument.indexOf('='); if (optionalEqualsIndex != -1) { key = argument.substring(0, optionalEqualsIndex); value = argument.substring(optionalEqualsIndex + 1, argument.length()); } else { key = argument; value = ""; } ret.put(key, value); start = matcher.end(); } return ret; } /** * Returns the <code>generatedSourcesDirectory</code> configuration parameter of the * {@link #COMPILER_PLUGIN_ARTIFACT_ID} plugin, or <code>null</code> if the {@link #GOAL_COMPILE} is not being * executed for this project. * * @param mavenSession the {@link MavenSession} being used * @param mavenProjectFacade the {@link IMavenProjectFacade} of the project to get the * <code>generatedSourcesDirectory</code> configuration parameter from * @param monitor the {@link IProgressMonitor} for this operation * @return the <code>generatedSourcesDirectory</code> configuration parameter of the * {@link #COMPILER_PLUGIN_ARTIFACT_ID} plugin, or <code>null</code> if the {@link #GOAL_COMPILE} is not being * executed for this project * @throws CoreException Any {@link CoreException}s encountered will be passed through. */ private File getGeneratedSourcesDirectory(MavenSession mavenSession, IMavenProjectFacade mavenProjectFacade, IProgressMonitor monitor) throws CoreException { for (MojoExecution mojoExecution : mavenProjectFacade.getMojoExecutions(COMPILER_PLUGIN_GROUP_ID, COMPILER_PLUGIN_ARTIFACT_ID, monitor, GOAL_COMPILE)) { File generatedSourcesDirectory = maven.getMojoParameterValue(mavenSession, mojoExecution, "generatedSourcesDirectory", File.class); if (generatedSourcesDirectory != null) return generatedSourcesDirectory; } return null; } /** * <p> * Filters the specified {@link Artifact}s to those that match the following criteria: * </p> * <ul> * <li>{@link Artifact#isResolved()} is <code>true</code></li> * <li>{@link Artifact#getArtifactHandler().getExtension()} equals "jar"</li> * <li>{@link Artifact#getScope()} equals {@link Artifact#SCOPE_COMPILE}</li> * <li>{@link Artifact#getFile()} returns a {@link File} where {@link File#isFile()} is <code>true</code></li> * </ul> * * @param artifacts the {@link Set} of {@link Artifact}s to filter * @return the actual JAR {@link File}s available from the specified {@link Artifact}s */ private static List<File> filterToResolvedJars(List<Artifact> artifacts) { List<File> resolvedJarArtifacts = new ArrayList<File>(); ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_COMPILE); for (Artifact artifact : artifacts) { // Ensure that this Artifact should be included if (!artifact.isResolved()) continue; if (artifact.getArtifactHandler() == null || !"jar".equalsIgnoreCase(artifact.getArtifactHandler().getExtension())) continue; if (!filter.include(artifact)) continue; // Ensure that the Artifact resolves to a File that we can use File artifactJarFile = artifact.getFile(); if (!artifactJarFile.isFile()) continue; resolvedJarArtifacts.add(artifactJarFile); } return resolvedJarArtifacts; } /** * Returns <code>true</code> if any of the specified JARs contain a Java 5 or Java 6 annotation processor, * <code>false</code> if none of them do. * * @param resolvedJarArtifacts the JAR {@link File}s to inspect for annotation processors * @return <code>true</code> if any of the specified JARs contain a Java 5 or Java 6 annotation processor, * <code>false</code> if none of them do */ private static boolean containsAptProcessors(Collection<File> resolvedJarArtifacts) { // Read through all JARs, checking for any APT service entries try { for (File resolvedJarArtifact : resolvedJarArtifacts) { Set<ServiceEntry> aptServiceEntries = AnnotationServiceLocator .getAptServiceEntries(resolvedJarArtifact); if (!aptServiceEntries.isEmpty()) return true; } } catch (IOException e) { MavenJdtAptPlugin.createErrorStatus(e, "Error while reading artifact JARs."); } // No service entries were found return false; } /** * Converts the specified relative or absolute {@link File} to a {@link File} that is relative to the base directory * of the specified {@link IProject}. * * @param project the {@link IProject} whose base directory the returned {@link File} should be relative to * @param fileToConvert the relative or absolute {@link File} to convert * @return a {@link File} that is relative to the base directory of the specified {@link IProject} */ private File convertToProjectRelativePath(IProject project, File fileToConvert) { // Get an absolute version of the specified file File absoluteFile = fileToConvert.getAbsoluteFile(); String absoluteFilePath = absoluteFile.getAbsolutePath(); // Get a File for the absolute path to the project's directory File projectBasedirFile = project.getLocation().toFile().getAbsoluteFile(); String projectBasedirFilePath = projectBasedirFile.getAbsolutePath(); // Compute the relative path if (absoluteFile.equals(projectBasedirFile)) { return new File("."); } else if (absoluteFilePath.startsWith(projectBasedirFilePath)) { String projectRelativePath = absoluteFilePath.substring(projectBasedirFilePath.length() + 1); return new File(projectRelativePath); } else { return absoluteFile; } } /** * Returns the {@link IResource} for the specified {@link IMavenProjectFacade}'s POM. * * @param mavenProjectFacade the {@link IMavenProjectFacade} to get the POM for * @return the {@link IResource} for the specified {@link IMavenProjectFacade}'s POM */ private static IResource findPomResource(IMavenProjectFacade mavenProjectFacade) { IFile pomFile = mavenProjectFacade.getPom(); IResource pomResource = pomFile; return pomResource; } /** * Returns the update warning {@link IMarker}s attached to the specified {@link IResource}. * * @param resource the {@link IResource} to find the update warning {@link IMarker}s of * @return the update warning {@link IMarker}s attached to the specified {@link IResource} * @throws CoreException Any {@link CoreException}s encountered will be passed through. */ private static List<IMarker> findUpdateWarnings(IResource resource) throws CoreException { IMarker[] problemMarkers = resource.findMarkers(IMarker.PROBLEM, false, IResource.DEPTH_ZERO); List<IMarker> updateWarningMarkers = new ArrayList<IMarker>(); for (IMarker problemMarker : problemMarkers) if (MARKER_ATTRIB_TYPE_UPDATE.equals(problemMarker.getAttribute(MARKER_ATTRIB_TYPE))) updateWarningMarkers.add(problemMarker); return updateWarningMarkers; } /** * Applies an update warning {@link IMarker} to the specified {@link IResource}. If one is already present, this * method does nothing. * * @param resource the {@link IResource} to apply the update warning {@link IMarker} to * @throws CoreException Any {@link CoreException}s encountered will be passed through. */ @SuppressWarnings("unused") private static void applyUpdateWarning(IResource resource) throws CoreException { // No need to add the same marker twice to the same resource if (!findUpdateWarnings(resource).isEmpty()) return; IMarker warningMarker = resource.createMarker(IMarker.PROBLEM); warningMarker.setAttribute(MARKER_ATTRIB_TYPE, MARKER_ATTRIB_TYPE_UPDATE); warningMarker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); warningMarker.setAttribute(IMarker.MESSAGE, "The Maven POM has changed, but the Eclipse project's APT configuration was not updated." + " Run \"Maven > Update Project Configuration\" to resolve this."); log.debug("applied warning marker"); } /** * Clears all update warning {@link IMarker}s attached to the specified {@link IResource}. * * @param resource the {@link IResource} to clear update warning {@link IMarker}s from * @throws CoreException Any {@link CoreException}s encountered will be passed through. */ private static void clearUpdateWarnings(IResource resource) throws CoreException { List<IMarker> warningMarkers = findUpdateWarnings(resource); for (IMarker warningMarker : warningMarkers) warningMarker.delete(); log.debug("cleared warning markers"); } }