| /******************************************************************************* |
| * 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); |
| } |
| } |