Copyright (c) 2015-2016 Igor Fedorenko This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: Igor Fedorenko - initial API and implementation
/******************************************************************************* * Copyright (c) 2015-2016 Igor Fedorenko * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * Igor Fedorenko - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.internal.launching.sourcelookup.advanced; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.Launch; import org.eclipse.debug.core.model.IPersistableSourceLocator; import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2; import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; import org.eclipse.jdt.internal.launching.LaunchingPlugin; public class AdvancedSourceLookupSupport { // TODO consider moving to LaunchingPlugin public static final String ID_sourceContainerResolvers = LaunchingPlugin.ID_PLUGIN + ".sourceContainerResolvers"; //$NON-NLS-1$ // TODO consider moving to LaunchingPlugin public static final String ID_workspaceProjectDescribers = LaunchingPlugin.ID_PLUGIN + ".workspaceProjectDescribers"; //$NON-NLS-1$ private static BackgroundProcessingJob backgroundJob; private static volatile WorkspaceProjectSourceContainers workspaceProjects; private static final Lock workspaceProjectsLock = new ReentrantLock(); private AdvancedSourceLookupSupport() { } public static void start() { backgroundJob = new BackgroundProcessingJob(); } public static void stop() { backgroundJob.cancel(); backgroundJob = null; workspaceProjectsLock.lock(); try { if (workspaceProjects != null) { workspaceProjects.close(); workspaceProjects = null; } } finally { workspaceProjectsLock.unlock(); } } public static void schedule(IRunnableWithProgress task) { backgroundJob.schedule(task); } public static WorkspaceProjectSourceContainers getWorkspaceJavaProjects(IProgressMonitor monitor) throws CoreException { return getWorkspaceJavaProjects0(monitor); } private static WorkspaceProjectSourceContainers getWorkspaceJavaProjects0(IProgressMonitor monitor) throws CoreException { // this is convoluted, but I could not think of a simpler implementation // when monitor==null, we are most likely on UI thread and must not block, hence immediate return if (monitor == null || workspaceProjects != null) { return workspaceProjects; } // when monitor!=null, try to get the lock but check for cancellation periodically try { while (!workspaceProjectsLock.tryLock(500, TimeUnit.MILLISECONDS)) { if (monitor.isCanceled()) { return workspaceProjects; } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // restore interrupted status return workspaceProjects; } // got the lock, do the initialization if another thread didn't do it already // note that double-check locking is okay on java 5+ with volatile fields try { if (workspaceProjects == null) { WorkspaceProjectSourceContainers _workspaceProjects = new WorkspaceProjectSourceContainers(); _workspaceProjects.initialize(monitor); // assign only fully initialized instance, otherwise monitor==null branch above may misbehave workspaceProjects = _workspaceProjects; } } finally { // release the lock in finally{} block workspaceProjectsLock.unlock(); } return workspaceProjects; } public static String getJavaagentString() { return "-javaagent:\"" + getJavaagentLocation() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ } public static String getJavaagentLocation() { return LaunchingPlugin.getFileInPlugin(new Path("lib/javaagent-shaded.jar")).getAbsolutePath(); //$NON-NLS-1$ }
Workaround a deficiency of ISourceLookupParticipant API, which does not provide access to a progress monitor.

This method can be called in three different cases:

  1. from UI thread, in which case null is return. this tells the caller to only perform fast operations (i.e. cache lookups) on this thread and submit any long-running operations as background jobs
  2. from background job with existing IProgressMonitor, in which case the monitor is returned
  3. from background job without IProgressMonitor, in which case NullProgressMonitor is returned.
/** * Workaround a deficiency of ISourceLookupParticipant API, which does not provide access to a progress monitor. * * <p> * This method can be called in three different cases: * <ol> * <li>from UI thread, in which case {@code null} is return. this tells the caller to only perform fast operations (i.e. cache lookups) on this * thread and submit any long-running operations as background jobs * <li>from background job with existing IProgressMonitor, in which case the monitor is returned * <li>from background job without IProgressMonitor, in which case {@link NullProgressMonitor} is returned. * </ol> */
public static IProgressMonitor getContextMonitor(IProgressMonitor monitor) { if (monitor == null) { Job job = Job.getJobManager().currentJob(); if (job != null) { // current implementation can perform workspace project cache initialization on system job without any user feedback // although eclipse ui remains responsive, source lookup will appear to do nothing until initialization is complete // a fix requires changes to ISourceLookupParticipant#findSourceElements API to accept user-visible progress monitor monitor = new NullProgressMonitor(); } } return monitor; }
Returns a launch object to use when launching the given launch configuration in the given mode. The returned launch object is preconfigured to use AdvancedSourceLookupDirector as the source locator.
/** * Returns a launch object to use when launching the given launch configuration in the given mode. The returned launch object is preconfigured to * use {@link AdvancedSourceLookupDirector} as the source locator. */
public static ILaunch createAdvancedLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { return new Launch(configuration, mode, createSourceLocator(AdvancedSourceLookupDirector.ID, configuration)); }
Creates and returns new IPersistableSourceLocator of the specified type and with the provided configuration.
/** * Creates and returns new {@link IPersistableSourceLocator} of the specified type and with the provided configuration. */
public static IPersistableSourceLocator createSourceLocator(String type, ILaunchConfiguration configuration) throws CoreException { ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); IPersistableSourceLocator locator = launchManager.newSourceLocator(type); String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null); if (memento == null) { locator.initializeDefaults(configuration); } else { if (locator instanceof IPersistableSourceLocator2) { ((IPersistableSourceLocator2) locator).initializeFromMemento(memento, configuration); } else { locator.initializeFromMemento(memento); } } return locator; } public static boolean isAdvancedSourcelookupEnabled() { return Platform.getPreferencesService().getBoolean(JDIDebugPlugin.getUniqueIdentifier(), JDIDebugPlugin.PREF_ENABLE_ADVANCED_SOURCELOOKUP, true, null); } }