package bndtools.launch; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.launching.RuntimeClasspathEntry; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.IRuntimeClasspathEntry2; import org.eclipse.jdt.launching.IRuntimeClasspathEntryResolver; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import bndtools.Plugin; public class BndContainerRuntimeClasspathEntryResolver implements IRuntimeClasspathEntryResolver { /** * Cache of already resolved projects in container entries. Used to avoid cycles in project dependencies when * resolving classpath container entries. Counters used to know when entering/exiting to clear cache */ private static ThreadLocal<List<IJavaProject>> resolvingProjects = new ThreadLocal<>(); private static ThreadLocal<Integer> resolvingCount = new ThreadLocal<>(); @Override public IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, ILaunchConfiguration configuration) throws CoreException { IJavaProject project = entry.getJavaProject(); if (project == null) { project = JavaRuntime.getJavaProject(configuration); } return resolveRuntimeClasspathEntry(entry, project); } @Override public IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entryToResolve, IJavaProject entryProject) throws CoreException { if (entryToResolve == null || entryProject == null) { return new IRuntimeClasspathEntry[0]; } final List<IRuntimeClasspathEntry> resolvedRuntimeClasspathEntries = new ArrayList<>(); final IClasspathContainer container = JavaCore.getClasspathContainer(entryToResolve.getPath(), entryProject); if (container == null) { throw new CoreException(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Could not resolve Bnd classpath container", null)); } final IClasspathEntry[] classpathEntries = container.getClasspathEntries(); List<IJavaProject> projects = resolvingProjects.get(); Integer count = resolvingCount.get(); if (projects == null) { projects = new ArrayList<>(); resolvingProjects.set(projects); count = 0; } int intCount = count.intValue(); intCount++; resolvingCount.set(intCount); try { for (IClasspathEntry classpathEntry : classpathEntries) { if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(classpathEntry.getPath().segment(0)); final IJavaProject javaProject = JavaCore.create(project); if (!projects.contains(javaProject)) { projects.add(javaProject); final IRuntimeClasspathEntry2 defaultProjectClasspathEntry = (IRuntimeClasspathEntry2) JavaRuntime.newDefaultProjectClasspathEntry(javaProject); final IRuntimeClasspathEntry[] projectRuntimeClasspathEntries = defaultProjectClasspathEntry.getRuntimeClasspathEntries(null); for (IRuntimeClasspathEntry projectRuntimeClasspathEntry : projectRuntimeClasspathEntries) { // the only reason for this entire class is the following check, for Projects that get resolved // from our BndContainer we need to override the default behavior found here: JavaRuntime.resolveOutputLocations(IJavaProject, int) // instead of resolving all output locations we simply just return the project runtime classpath entry itself if (projectRuntimeClasspathEntry.getType() == IRuntimeClasspathEntry.PROJECT) { IResource resource = projectRuntimeClasspathEntry.getResource(); if (resource instanceof IProject) { resolvedRuntimeClasspathEntries.add(projectRuntimeClasspathEntry); } } else { IRuntimeClasspathEntry[] resolvedEntries = JavaRuntime.resolveRuntimeClasspathEntry(projectRuntimeClasspathEntry, javaProject); for (IRuntimeClasspathEntry resolvedEntry : resolvedEntries) { resolvedRuntimeClasspathEntries.add(resolvedEntry); } } } } } else { final IRuntimeClasspathEntry runtimeClasspathEntry = new RuntimeClasspathEntry(classpathEntry); if (!resolvedRuntimeClasspathEntries.contains(runtimeClasspathEntry)) { resolvedRuntimeClasspathEntries.add(runtimeClasspathEntry); } } } } finally { intCount--; if (intCount == 0) { resolvingProjects.set(null); resolvingCount.set(null); } else { resolvingCount.set(intCount); } } for (IRuntimeClasspathEntry resolvedRuntimeClasspathEntry : resolvedRuntimeClasspathEntries) { resolvedRuntimeClasspathEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES); } return resolvedRuntimeClasspathEntries.toArray(new IRuntimeClasspathEntry[0]); } @Override public IVMInstall resolveVMInstall(IClasspathEntry entry) throws CoreException { return null; } }