/******************************************************************************* * Copyright (c) 2012 VMWare, 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: * VMWare, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.maven; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.MojoExecution; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.m2e.core.lifecyclemapping.model.IPluginExecutionMetadata; import org.eclipse.m2e.core.project.IMavenProjectFacade; import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant; import org.eclipse.m2e.core.project.configurator.ILifecycleMappingConfiguration; import org.eclipse.m2e.core.project.configurator.MojoExecutionBuildParticipant; import org.eclipse.m2e.core.project.configurator.MojoExecutionKey; import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; import org.eclipse.m2e.jdt.IClasspathDescriptor; import org.eclipse.m2e.jdt.IClasspathManager; import org.eclipse.m2e.jdt.IJavaProjectConfigurator; import org.eclipse.m2e.jdt.internal.AbstractJavaProjectConfigurator; import org.grails.ide.eclipse.commands.GrailsCommandUtils; import org.grails.ide.eclipse.core.internal.GrailsResourceUtil; import org.grails.ide.eclipse.core.internal.classpath.GrailsClasspathContainer; import org.grails.ide.eclipse.core.internal.classpath.GrailsClasspathUtils; import org.grails.ide.eclipse.core.internal.classpath.PerProjectDependencyDataCache; import org.grails.ide.eclipse.core.internal.classpath.SourceFolderJob; import org.grails.ide.eclipse.core.internal.plugins.GrailsCore; import org.grails.ide.eclipse.core.internal.plugins.PerProjectPluginCache; import org.grails.ide.eclipse.core.launch.ClasspathLocalizer; import org.grails.ide.eclipse.core.launch.EclipsePluginClasspathEntry; import org.grails.ide.eclipse.core.launch.GrailsLaunchArgumentUtils; import org.grails.ide.eclipse.core.model.GrailsInstallManager; import org.grails.ide.eclipse.core.model.GrailsVersion; import org.grails.ide.eclipse.runtime.shared.SharedLaunchConstants; import org.springsource.ide.eclipse.commons.frameworks.core.legacyconversion.IConversionConstants; public class GrailsProjectConfigurator extends AbstractJavaProjectConfigurator implements IJavaProjectConfigurator { @Override public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { IProject project = request.getProject(); addJavaNature(project, monitor); IJavaProject javaProject = JavaCore.create(project); boolean noContainer = !GrailsClasspathUtils.hasClasspathContainer(javaProject); boolean hasOldContainer = GrailsClasspathUtils.hasOldClasspathContainer(javaProject); IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(Arrays.asList(rawClasspath)); if (hasOldContainer) { for (Iterator<IClasspathEntry> entryIter = entries.iterator(); entryIter.hasNext();) { IClasspathEntry entry = entryIter.next(); if (entry.getPath().toPortableString().equals(IConversionConstants.GRAILS_OLD_CONTAINER)) { entryIter.remove(); break; } } } if (noContainer) { entries.add(JavaCore.newContainerEntry( GrailsClasspathContainer.CLASSPATH_CONTAINER_PATH, null, null, false)); } // also remove any grails source folders that have the grails classpath attribute for (Iterator<IClasspathEntry> entryIter = entries.iterator(); entryIter.hasNext();) { IClasspathEntry entry = entryIter.next(); if (GrailsResourceUtil.isGrailsClasspathEntry(entry) && !GrailsResourceUtil.hasClasspathAttribute(entry, IClasspathManager.POMDERIVED_ATTRIBUTE)) { entryIter.remove(); } } javaProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), monitor); super.configure(request, monitor); } public void configureRawClasspath(ProjectConfigurationRequest request, IClasspathDescriptor classpath, IProgressMonitor monitor) throws CoreException { IProject project = request.getProject(); GrailsCommandUtils.ensureNaturesAndBuilders(project); IJavaProject javaProject = JavaCore.create(project); // make sure that we have a dependency file String descriptorName = GrailsClasspathUtils.getDependencyDescriptorName(project); File f = new File(descriptorName); if (!f.exists()) { throw new CoreException(new Status(IStatus.ERROR, GrailsMavenActivator.PLUGIN_ID, "Could not find the grails dependency file." + " This probably means that there is a bad dependency in the pom file.", new Exception())); } // ensure that the dependency and plugin data is forgotten GrailsCore.get().connect(project, PerProjectDependencyDataCache.class).refreshData(); GrailsCore.get().connect(project, PerProjectPluginCache.class).refreshDependencyCache(); // We now know that all of the dependency information is up to date // can't call refresh dependencies directly since that will mess up the source folders // source folders must be seen as coming from maven, not grails-ide SourceFolderJob folderJob = new SourceFolderJob(javaProject); List<IClasspathEntry> sourceEntries = folderJob.findSourceEntries(monitor); for (IClasspathEntry entry : sourceEntries) { classpath.addEntry(entry); } folderJob.fixCharSets(monitor); GrailsCommandUtils.deleteOutOfSynchPlugins(project); // we can ask GrailsClasspathContainer to refresh its dependencies. GrailsClasspathContainer container = GrailsClasspathUtils.getClasspathContainer(javaProject); // reparse classpath entries from dependencies file on next request if (container != null) { container.invalidate(); container.getClasspathEntries(); } } @Override protected void addJavaProjectOptions(Map<String, String> options, ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { // because of the use of the extension, we cannot get the source and target info from // super. must calculate it ourselves. Plugin plugin = request.getMavenProject().getPlugin("org.apache.maven.plugins:maven-compiler-plugin"); String source = null; String target = null; if (plugin != null) { Object configuration = plugin.getConfiguration(); if (configuration instanceof Xpp3Dom) { Xpp3Dom xml = (Xpp3Dom) configuration; Xpp3Dom sourceChild = xml.getChild("source"); if (sourceChild != null) { source = sourceChild.getValue(); if(source.equals("5")) { source = "1.5"; } else if(source.equals("6")) { source = "1.6"; } else if(source.equals("7")) { source = "1.7"; } } Xpp3Dom targetChild = xml.getChild("target"); if (targetChild != null) { target = targetChild.getValue(); if(target.equals("5")) { target = "1.5"; } else if(target.equals("6")) { target = "1.6"; } else if(target.equals("7")) { target = "1.7"; } } } } if (source == null) { source = "1.6"; } if (target == null) { target = "1.6"; } options.put(JavaCore.COMPILER_SOURCE, source); options.put(JavaCore.COMPILER_COMPLIANCE, source); options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, target); } @Override public boolean hasConfigurationChanged(IMavenProjectFacade newFacade, ILifecycleMappingConfiguration oldProjectConfiguration, MojoExecutionKey key, IProgressMonitor monitor) { IProject project = newFacade.getProject(); try { Xpp3Dom origOldConfiguration = oldProjectConfiguration == null ? null : oldProjectConfiguration.getMojoExecutionConfiguration(key); Xpp3Dom oldConfiguration = augmentConfiguration(project, origOldConfiguration); MojoExecution mojoExecution = newFacade.getMojoExecution(key, monitor); Xpp3Dom origNewConfigration = mojoExecution == null ? null : new Xpp3Dom(mojoExecution.getConfiguration()); Xpp3Dom newConfigration = augmentConfiguration(project, origNewConfigration); return newConfigration != null ? !newConfigration.equals(oldConfiguration) : oldConfiguration != null; } catch(CoreException ex) { return true; // assume configuration update is required } } @Override public AbstractBuildParticipant getBuildParticipant( IMavenProjectFacade projectFacade, MojoExecution execution, IPluginExecutionMetadata executionMetadata) { if ("maven-compile".equals(execution.getGoal())) { IProject project = projectFacade.getProject(); // delete old dependency info will be recreated String descriptorName = GrailsClasspathUtils.getDependencyDescriptorName(project); File f = new File(descriptorName); f.delete(); Xpp3Dom newConfiguration = augmentConfiguration(project, execution.getConfiguration()); execution.setConfiguration(newConfiguration); } return new MojoExecutionBuildParticipant(execution, false, true); } private Xpp3Dom augmentConfiguration(IProject project, Xpp3Dom orig) { if (orig == null) { return null; } Xpp3Dom configuration = new Xpp3Dom(orig); // extra classpath entry Xpp3Dom node = configuration.getChild("extraClasspathEntries"); if (node == null) { node = new Xpp3Dom("extraClasspathEntries"); configuration.addChild(node); } ClasspathLocalizer localizer = new ClasspathLocalizer(); GrailsVersion version = GrailsVersion.getEclipseGrailsVersion(project); String pluginId = GrailsLaunchArgumentUtils.getRuntimeBundleFor(version); List<String> extraCp = localizer.localizeClasspath( new EclipsePluginClasspathEntry(pluginId, null), new EclipsePluginClasspathEntry("org.grails.ide.eclipse.runtime.shared", null)); StringBuilder sb = new StringBuilder(); for (String cpEntry : extraCp) { sb.append(cpEntry.replaceAll(",", "\\,")).append(","); } node.setValue(sb.toString()); // build listeners node = configuration.getChild("grailsBuildListener"); if (node == null) { node = new Xpp3Dom("grailsBuildListener"); configuration.addChild(node); } node.setValue(SharedLaunchConstants.DependencyExtractingBuildListener_CLASS); // dependency file location node = configuration.getChild("dependencyFileLocation"); if (node == null) { node = new Xpp3Dom("dependencyFileLocation"); configuration.addChild(node); } node.setValue(GrailsClasspathUtils.getDependencyDescriptorName(project)); node = configuration.getChild("fork"); if (node == null) { node = new Xpp3Dom("fork"); configuration.addChild(node); } // must run in forked mode node.setValue("true"); return configuration; } public void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath, IProgressMonitor monitor) throws CoreException { // do nuthin } }