diff --git a/ui/org.eclipse.pde.launching/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.launching/META-INF/MANIFEST.MF index 45e93654824..421eb77e455 100644 --- a/ui/org.eclipse.pde.launching/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.launching/META-INF/MANIFEST.MF @@ -14,7 +14,10 @@ Require-Bundle: org.eclipse.jdt.junit.core;bundle-version="[3.6.0,4.0.0)", org.eclipse.pde.build;bundle-version="[3.12.300,4.0.0)", org.eclipse.pde.core;bundle-version="[3.2.0,4.0.0)", org.eclipse.jdt.debug;bundle-version="[3.2.0,4.0.0)", - org.eclipse.core.filesystem;bundle-version="[1.0.0,2.0.0)" + org.eclipse.core.filesystem;bundle-version="[1.0.0,2.0.0)", + org.eclipse.equinox.p2.core;bundle-version="[2.10.0,3.0.0)", + org.eclipse.equinox.p2.metadata;bundle-version="[2.8.0,3.0.0)", + org.eclipse.equinox.p2.repository;bundle-version="[2.8.0,3.0.0)" Bundle-Activator: org.eclipse.pde.internal.launching.PDELaunchingPlugin Export-Package: org.eclipse.pde.internal.launching;x-friends:="org.eclipse.pde.ui, org.eclipse.pde.unittest.junit", diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/ILaunchingPreferenceConstants.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/ILaunchingPreferenceConstants.java index 76b06c44a60..0f90b4803bf 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/ILaunchingPreferenceConstants.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/ILaunchingPreferenceConstants.java @@ -40,6 +40,11 @@ public interface ILaunchingPreferenceConstants { * arguments when creating a new launch configuration */ public static final String ADD_SWT_NON_DISPOSAL_REPORTING = "Preferences.Launching.addSwtNonDisposalReporting";//$NON-NLS-1$ + + /** + * Boolean preference whether to search in enabled repositories for source bundles + */ + public static final String PROP_SEARCH_REPOSITORIES_FOR_SOURCE = "Preferences.Launching.searchRepositoriesForSource";//$NON-NLS-1$ // OSGi Frameworks public static final String DEFAULT_OSGI_FRAMEOWRK = "Preference.default.osgi.framework"; //$NON-NLS-1$ diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PreferenceInitializer.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PreferenceInitializer.java index 6dfa817953a..836f9378320 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PreferenceInitializer.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PreferenceInitializer.java @@ -36,6 +36,7 @@ public void initializeDefaultPreferences() { prefs.putBoolean(ILaunchingPreferenceConstants.PROP_JUNIT_ADD_NEW_WORKSPACE_PLUGINS, false); prefs.putBoolean(ILaunchingPreferenceConstants.PROP_JUNIT_VALIDATE_LAUNCH, true); prefs.putBoolean(ILaunchingPreferenceConstants.ADD_SWT_NON_DISPOSAL_REPORTING, true); + prefs.putBoolean(ILaunchingPreferenceConstants.PROP_SEARCH_REPOSITORIES_FOR_SOURCE, false); // copy over instance scope prefs from UI plugin IEclipsePreferences oldInstancePrefs = InstanceScope.INSTANCE.getNode(IPDEConstants.UI_PLUGIN_ID); diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java index 3ab3a930fc8..dfa2500754f 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java @@ -282,4 +282,21 @@ public synchronized void dispose() { return fOSGiRuntimeVersion; } + /** + * Searches for source in P2 repositories as a fallback. + * This has lower priority than workspace and target platform sources. + * + * @param bundleId the symbolic name of the bundle + * @param typeName the qualified name of the source type + * @return source element or null + */ + Object findSourceInRepositories(String bundleId, String typeName) throws CoreException { + RepositorySourceContainer container = new RepositorySourceContainer(bundleId); + Object[] result = container.findSourceElements(typeName); + if (result != null && result.length > 0) { + return result[0]; + } + return null; + } + } diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupQuery.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupQuery.java index 5b597434788..17fc4a071ba 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupQuery.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupQuery.java @@ -311,6 +311,12 @@ private Object getSourceElement(String location, String id, String typeName, boo } } } + + // As a last resort, search in repositories for source bundles + result = fDirector.findSourceInRepositories(id, typeName); + if (result != null) { + return result; + } } return null; } diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/RepositorySourceContainer.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/RepositorySourceContainer.java new file mode 100644 index 00000000000..441997bee01 --- /dev/null +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/RepositorySourceContainer.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * 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 + *******************************************************************************/ +package org.eclipse.pde.internal.launching.sourcelookup; + +import java.io.File; +import java.net.URI; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.sourcelookup.ISourceContainerType; +import org.eclipse.debug.core.sourcelookup.containers.AbstractSourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.ExternalArchiveSourceContainer; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.IProvisioningAgentProvider; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.query.QueryUtil; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; +import org.eclipse.equinox.p2.repository.artifact.IFileArtifactRepository; +import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; +import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.internal.core.target.P2TargetUtils; +import org.eclipse.pde.internal.launching.IPDEConstants; +import org.eclipse.pde.internal.launching.PDELaunchingPlugin; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +/** + * Source container that searches for source bundles in enabled P2 repositories. + * This is used as a fallback when source cannot be found in workspace or target platform. + * + * @since 3.8 + */ +public class RepositorySourceContainer extends AbstractSourceContainer { + + /** + * Unique identifier for this source container type + */ + public static final String TYPE_ID = IPDEConstants.PLUGIN_ID + ".containerType.repository"; //$NON-NLS-1$ + + private final String fBundleId; + + /** + * Constructs a repository source container for the given bundle. + * + * @param bundleId symbolic name of the bundle to search for source + */ + public RepositorySourceContainer(String bundleId) { + fBundleId = bundleId; + } + + @Override + public String getName() { + return NLS.bind("Repository Source: {0}", fBundleId); //$NON-NLS-1$ + } + + @Override + public ISourceContainerType getType() { + return getSourceContainerType(TYPE_ID); + } + + @Override + public Object[] findSourceElements(String name) throws CoreException { + // Only search if the preference is enabled + if (!PDELaunchingPlugin.getDefault().getPreferenceManager() + .getBoolean(org.eclipse.pde.internal.launching.ILaunchingPreferenceConstants.PROP_SEARCH_REPOSITORIES_FOR_SOURCE)) { + return EMPTY; + } + + // Search for source bundle in repositories + File sourceBundle = findSourceBundle(fBundleId); + if (sourceBundle != null && sourceBundle.exists()) { + // Create an external archive source container for the source bundle + ExternalArchiveSourceContainer container = new ExternalArchiveSourceContainer( + sourceBundle.getAbsolutePath(), true); + return container.findSourceElements(name); + } + + return EMPTY; + } + + /** + * Searches enabled P2 repositories for a source bundle matching the given bundle ID. + * + * @param bundleId the symbolic name of the bundle + * @return the source bundle file, or null if not found + */ + private File findSourceBundle(String bundleId) { + try { + IProvisioningAgent agent = getProvisioningAgent(); + if (agent == null) { + return null; + } + + IMetadataRepositoryManager metadataManager = agent.getService(IMetadataRepositoryManager.class); + IArtifactRepositoryManager artifactManager = agent.getService(IArtifactRepositoryManager.class); + + if (metadataManager == null || artifactManager == null) { + return null; + } + + // Get all known repositories + URI[] metadataRepos = metadataManager.getKnownRepositories(IMetadataRepositoryManager.REPOSITORIES_ALL); + + for (URI repoUri : metadataRepos) { + try { + // Load the metadata repository + IMetadataRepository metadataRepo = metadataManager.loadRepository(repoUri, null); + + // Query for source bundles matching the bundle ID + // Source bundles typically have .source appended to the ID + String sourceId = bundleId + ".source"; //$NON-NLS-1$ + IQuery query = QueryUtil.createLatestQuery( + QueryUtil.createIUQuery(sourceId)); + IQueryResult result = metadataRepo.query(query, null); + + if (!result.isEmpty()) { + IInstallableUnit sourceIU = result.iterator().next(); + + // Get the artifact from the artifact repository + for (IArtifactKey key : sourceIU.getArtifacts()) { + File sourceFile = getArtifactFile(artifactManager, key, repoUri); + if (sourceFile != null && sourceFile.exists()) { + return sourceFile; + } + } + } + } catch (Exception e) { + // Continue searching in other repositories + PDELaunchingPlugin.log(new Status(IStatus.WARNING, IPDEConstants.PLUGIN_ID, + "Error searching repository: " + repoUri, e)); //$NON-NLS-1$ + } + } + } catch (Exception e) { + PDELaunchingPlugin.log(new Status(IStatus.ERROR, IPDEConstants.PLUGIN_ID, + "Error searching for source bundle: " + bundleId, e)); //$NON-NLS-1$ + } + + return null; + } + + /** + * Gets the provisioning agent for P2 operations. + */ + private IProvisioningAgent getProvisioningAgent() { + BundleContext context = PDELaunchingPlugin.getDefault().getBundle().getBundleContext(); + ServiceReference reference = context + .getServiceReference(IProvisioningAgentProvider.class); + if (reference != null) { + IProvisioningAgentProvider agentProvider = context.getService(reference); + if (agentProvider != null) { + try { + return agentProvider.createAgent(P2TargetUtils.AGENT_LOCATION); + } catch (Exception e) { + PDELaunchingPlugin.log(e); + } finally { + context.ungetService(reference); + } + } + } + return null; + } + + /** + * Retrieves the local file for an artifact from the repository. + */ + private File getArtifactFile(IArtifactRepositoryManager manager, IArtifactKey key, URI repoUri) { + try { + IArtifactRepository artifactRepo = manager.loadRepository(repoUri, null); + if (artifactRepo instanceof IFileArtifactRepository fileRepo) { + File location = fileRepo.getArtifactFile(key); + if (location != null && location.exists()) { + return location; + } + } + } catch (Exception e) { + // Ignore and return null + } + return null; + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java index 575ac6fd8fa..98b8010e493 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java @@ -2713,6 +2713,8 @@ public class PDEUIMessages extends NLS { public static String LaunchingPreferencePage_description; + public static String LaunchingPreferencePage_searchRepositoriesForSource; + public static String RemoveLazyLoadingDirectiveResolution_remove; public static String RemoveAutomaticModuleResolution_remove; diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties index 8b45f594b30..e5ebc465eea 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties @@ -2194,6 +2194,7 @@ LauncherSection_launcherName=Launcher Name: LauncherSection_dialogTitle=Image Selection LauncherSection_dialogMessage=Select an image: LaunchingPreferencePage_description=Settings for Plug-in launches +LaunchingPreferencePage_searchRepositoriesForSource=Search in repositories for source bundles (may be slower) RemoveLazyLoadingDirectiveResolution_remove=Remove lazy activation header RemoveAutomaticModuleResolution_remove=Remove automatic module name header ProductDefinitonWizardPage_applicationDefinition=

An Eclipse product must be associated with an application, the default entry point for the product when it is running.

diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/LaunchingPreferencePage.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/LaunchingPreferencePage.java index 6da6486e1cc..49a416cc40f 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/LaunchingPreferencePage.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/LaunchingPreferencePage.java @@ -148,6 +148,8 @@ protected boolean isFile() { private Button fAddSwtNonDisposalReporting; + private Button fSearchRepositoriesForSource; + private Text fRuntimeWorkspaceLocation; private Button fRuntimeWorkspaceLocationRadio; private Button fRuntimeWorkspacesContainerRadio; @@ -188,6 +190,11 @@ protected Control createContents(Composite parent) { fAddSwtNonDisposalReporting .setSelection(launchingStore.getBoolean(ILaunchingPreferenceConstants.ADD_SWT_NON_DISPOSAL_REPORTING)); + fSearchRepositoriesForSource = new Button(optionComp, SWT.CHECK); + fSearchRepositoriesForSource.setText(PDEUIMessages.LaunchingPreferencePage_searchRepositoriesForSource); + fSearchRepositoriesForSource + .setSelection(launchingStore.getBoolean(ILaunchingPreferenceConstants.PROP_SEARCH_REPOSITORIES_FOR_SOURCE)); + new DefaultRuntimeWorkspaceBlock().createControl(composite); fRuntimeWorkspaceLocation .setText(launchingStore.getString(ILaunchingPreferenceConstants.PROP_RUNTIME_WORKSPACE_LOCATION)); @@ -250,6 +257,8 @@ public boolean performOk() { fJunitAutoValidate.getSelection()); launchingStore.setValueOrRemove(ILaunchingPreferenceConstants.ADD_SWT_NON_DISPOSAL_REPORTING, fAddSwtNonDisposalReporting.getSelection()); + launchingStore.setValueOrRemove(ILaunchingPreferenceConstants.PROP_SEARCH_REPOSITORIES_FOR_SOURCE, + fSearchRepositoriesForSource.getSelection()); try { launchingStore.flush(); } catch (BackingStoreException e) { @@ -287,6 +296,8 @@ protected void performDefaults() { launchingStore.getDefaultBoolean(ILaunchingPreferenceConstants.PROP_JUNIT_ADD_NEW_WORKSPACE_PLUGINS)); fJunitAutoValidate.setSelection( launchingStore.getDefaultBoolean(ILaunchingPreferenceConstants.PROP_JUNIT_VALIDATE_LAUNCH)); + fSearchRepositoriesForSource.setSelection( + launchingStore.getDefaultBoolean(ILaunchingPreferenceConstants.PROP_SEARCH_REPOSITORIES_FOR_SOURCE)); } @Override