/******************************************************************************* * 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.springframework.ide.eclipse.maven.internal.core; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.ui.RefreshTab; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.m2e.actions.MavenLaunchConstants; import org.eclipse.m2e.core.internal.IMavenConstants; import org.springframework.ide.eclipse.maven.MavenCorePlugin; /** * @author Christian Dupuis */ @SuppressWarnings("restriction") public class MavenClasspathUpdateJob extends WorkspaceJob { /** Internal cache of scheduled and <b>unfinished</b> update jobs */ private static final Queue<IJavaProject> SCHEDULED_PROJECTS = new ConcurrentLinkedQueue<IJavaProject>(); /** The {@link IJavaProject} this jobs should refresh the class path container for */ private final IJavaProject javaProject; /** * Private constructor to create an instance * @param javaProject the {@link IJavaProject} the class path container should be updated for * @param types the change types happened to the manifest */ private MavenClasspathUpdateJob(IJavaProject javaProject) { super("Updating Maven dependencies for project '" + javaProject.getElementName() + "'"); this.javaProject = javaProject; } /** * Returns the internal {@link IJavaProject} */ public IJavaProject getJavaProject() { return javaProject; } /** * Runs the job in the context of the workspace. Simply delegates refreshing of the class path container to * {@link ClasspathUtils#updateClasspathContainer(IJavaProject, IProgressMonitor)}. */ @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { if (!javaProject.getProject().isOpen() || monitor.isCanceled()) { return Status.CANCEL_STATUS; } try { IContainer basedir = findPomXmlBasedir(javaProject.getProject()); if (basedir == null) { return Status.CANCEL_STATUS; } ILaunchConfiguration configuration = createLaunchConfiguration(basedir, "eclipse:clean eclipse:eclipse"); if (configuration != null) { ILaunch launch = configuration.launch(ILaunchManager.RUN_MODE, new SubProgressMonitor(monitor, 75)); DebugPlugin.getDefault().addDebugEventListener( new MavenProcessListener(launch.getProcesses()[0], javaProject.getProject())); } } catch (Exception e) { return Status.CANCEL_STATUS; } return new Status(IStatus.OK, MavenCorePlugin.PLUGIN_ID, "Updated Maven dependencies"); } private IContainer findPomXmlBasedir(IContainer dir) { if (dir == null) { return null; } try { // loop upwards through the parents as long as we do not cross the project boundary while (dir.exists() && dir.getProject() != null && dir.getProject() != dir) { // see if pom.xml exists if (dir.getType() == IResource.FOLDER) { IFolder folder = (IFolder) dir; if (folder.findMember(IMavenConstants.POM_FILE_NAME) != null) { return folder; } } else if (dir.getType() == IResource.FILE) { if (((IFile) dir).getName().equals(IMavenConstants.POM_FILE_NAME)) { return dir.getParent(); } } dir = dir.getParent(); } } catch (Exception e) { return dir; } return dir; } private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, String goal) { try { ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType launchConfigurationType = launchManager .getLaunchConfigurationType(MavenLaunchConstants.LAUNCH_CONFIGURATION_TYPE_ID); ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(basedir, // "Updating Maven dependencies for '" + basedir.getName() + "'"); workingCopy.setAttribute(MavenLaunchConstants.ATTR_POM_DIR, basedir.getLocation().toOSString()); workingCopy.setAttribute(MavenLaunchConstants.ATTR_GOALS, goal); workingCopy.setAttribute(MavenLaunchConstants.ATTR_WORKSPACE_RESOLUTION, true); workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_SCOPE, "${project}"); workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_RECURSIVE, true); String vmArguments = workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, ""); vmArguments += "-Declipse.workspace=\"${workspace_loc}\""; workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, vmArguments); IPath path = getJREContainerPath(basedir); if (path != null) { workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, path .toPortableString()); } return workingCopy; } catch (CoreException ex) { MavenCorePlugin.getDefault().getLog().log( new Status(IStatus.ERROR, MavenCorePlugin.PLUGIN_ID, "Error occured", ex)); } return null; } private IPath getJREContainerPath(IContainer basedir) throws CoreException { IProject project = basedir.getProject(); if (project != null && project.hasNature(JavaCore.NATURE_ID)) { IJavaProject javaProject = JavaCore.create(project); IClasspathEntry[] entries = javaProject.getRawClasspath(); for (int i = 0; i < entries.length; i++) { IClasspathEntry entry = entries[i]; if (JavaRuntime.JRE_CONTAINER.equals(entry.getPath().segment(0))) { return entry.getPath(); } } } return null; } /** * Helper method to schedule a new {@link MavenClasspathUpdateJob}. * @param javaProject the {@link IJavaProject} the class path container should be updated for * @param types the change types of the manifest */ public static void scheduleClasspathContainerUpdateJob(IJavaProject javaProject) { if (javaProject != null && !SCHEDULED_PROJECTS.contains(javaProject)) { newClasspathContainerUpdateJob(javaProject); } } public static void scheduleClasspathContainerUpdateJob(IProject oroject) { scheduleClasspathContainerUpdateJob(JavaCore.create(oroject)); } /** * Creates a new instance of {@link MavenClasspathUpdateJob} and configures required properties and schedules it to * the workbench. */ private static MavenClasspathUpdateJob newClasspathContainerUpdateJob(IJavaProject javaProject) { MavenClasspathUpdateJob job = new MavenClasspathUpdateJob(javaProject); job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); job.setPriority(Job.BUILD); job.addJobChangeListener(new DuplicateJobListener()); job.schedule(); return job; } /** * Internal {@link IJobChangeListener} to detect duplicates in the scheduled list of {@link MavenClasspathUpdateJob * Jobs}. */ private static class DuplicateJobListener extends JobChangeAdapter implements IJobChangeListener { @Override public void done(IJobChangeEvent event) { SCHEDULED_PROJECTS.remove(((MavenClasspathUpdateJob) event.getJob()).getJavaProject()); } @Override public void scheduled(IJobChangeEvent event) { SCHEDULED_PROJECTS.add(((MavenClasspathUpdateJob) event.getJob()).getJavaProject()); } } private class MavenProcessListener implements IDebugEventSetListener { private final IProject project; private final IProcess newProcess; public MavenProcessListener(IProcess process, IProject project) { this.project = project; this.newProcess = process; } public void handleDebugEvents(DebugEvent[] events) { if (events != null && project != null) { int size = events.length; for (int i = 0; i < size; i++) { if (newProcess != null && newProcess.equals(events[i].getSource()) && events[i].getKind() == DebugEvent.TERMINATE) { DebugPlugin.getDefault().removeDebugEventListener(this); Job job = new Job("refresh project") { @Override protected IStatus run(IProgressMonitor monitor) { try { project.refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (CoreException e) { } return Status.OK_STATUS; } }; job.setSystem(true); job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); job.setPriority(Job.INTERACTIVE); job.schedule(); } } } } } }