blob: 7bcdb0e4927ae2252009f1d10bcfd240163502cb [file] [log] [blame]
/*******************************************************************************
* 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.
*
* <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 {@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 {@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);
}
}