Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add osgi-repository target location type #692

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ui/org.eclipse.pde.core/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Export-Package:
org.eclipse.pde.unittest.junit",
org.eclipse.pde.internal.core.variables;x-internal:=true
Import-Package: aQute.bnd.build;version="[4.4.0,5.0.0)",
aQute.bnd.header;version="2.5.0",
aQute.bnd.osgi;version="[5.5.0,6.0.0)",
aQute.bnd.osgi.repository;version="[3.0.0,4.0.0)",
aQute.bnd.osgi.resource;version="[4.3.0,5.0.0)",
Expand Down
8 changes: 8 additions & 0 deletions ui/org.eclipse.pde.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -437,4 +437,12 @@
class="org.eclipse.pde.internal.core.LocalMavenPluginSourcePathLocator"
complexity="low"/>
</extension>
<extension
point="org.eclipse.pde.core.targetLocations">
<targetLocation
canUpdate="true"
locationFactory="org.eclipse.pde.internal.core.target.RepositoryLocationFactory"
type="Repository">
</targetLocation>
</extension>
</plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich and others.
*
* 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.internal.core.target;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.repository.CacheManager;
import org.eclipse.pde.core.target.ITargetDefinition;
import org.eclipse.pde.core.target.TargetBundle;
import org.eclipse.pde.core.target.TargetFeature;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.util.PDEXmlProcessorFactory;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import aQute.bnd.osgi.repository.ResourcesRepository;
import aQute.bnd.osgi.repository.XMLResourceParser;
import aQute.bnd.osgi.resource.ResourceUtils;
import aQute.bnd.osgi.resource.ResourceUtils.ContentCapability;

@SuppressWarnings("restriction")
public class RepositoryBundleContainer extends AbstractBundleContainer {
public static final String ATTRIBUTE_URI = "uri"; //$NON-NLS-1$

public static final String ELEMENT_REQUIRE = "require"; //$NON-NLS-1$

public static final String TYPE = "Repository"; //$NON-NLS-1$

private final String uri;

private final Collection<Requirement> requirements;

public RepositoryBundleContainer(String uri, Collection<Requirement> requirements) {
this.uri = uri;
this.requirements = requirements;
}

@Override
protected TargetBundle[] resolveBundles(ITargetDefinition definition, IProgressMonitor monitor)
throws CoreException {
ResourcesRepository repository = getRepository(monitor);
Map<Requirement, Collection<Capability>> providers = repository.findProviders(getRequirements());
List<TargetBundle> bundles = new ArrayList<>();
List<ContentCapability> contentCapabilities = providers.values().stream().flatMap(Collection::stream)
.map(Capability::getResource).distinct().map(ResourceUtils::getContentCapability)
.filter(Objects::nonNull).toList();
CacheManager cacheManager = getCacheManager();
for (ContentCapability content : contentCapabilities) {
URI url = content.url();
try {
File file;
if (cacheManager != null) {
file = cacheManager.createCacheFromFile(url, monitor);
} else {
file = new File(FileLocator.toFileURL(url.toURL()).toURI());
}
bundles.add(new TargetBundle(file));
} catch (IOException | URISyntaxException e) {
throw new CoreException(Status.error("Can't fetch bundle from " + url, e));
}
}
return bundles.toArray(TargetBundle[]::new);
}

public String getUri() {
return uri;
}

public Collection<Requirement> getRequirements() {
return requirements;
}

public ResourcesRepository getRepository(IProgressMonitor monitor) throws CoreException {
String location = getLocation(true);
try {
URI base = new URI(location);
try {
CacheManager cacheManager = getCacheManager();
if (cacheManager != null) {
File file = cacheManager.createCacheFromFile(base, monitor);
return new ResourcesRepository(XMLResourceParser.getResources(file, base));
}
return new ResourcesRepository(XMLResourceParser.getResources(base));
} catch (Exception e) {
if (e instanceof CoreException core) {
throw core;
}
if (e instanceof RuntimeException runtime) {
throw runtime;
}
throw new CoreException(Status.error("Loading repository from " + location + " failed: " + e, e));
}
} catch (URISyntaxException e) {
throw new CoreException(Status.error("Invalid URI: " + location, e));
}
}

private CacheManager getCacheManager() throws CoreException {
return P2TargetUtils.getAgent().getService(CacheManager.class);
}

@Override
protected TargetFeature[] resolveFeatures(ITargetDefinition definition, IProgressMonitor monitor)
throws CoreException {
return new TargetFeature[0];
}

@Override
public String getType() {
return TYPE;
}

@Override
public String getLocation(boolean resolve) throws CoreException {
if (resolve) {
return resolveVariables(uri);
}
return uri;
}

@Override
public String serialize() {
try {
DocumentBuilder docBuilder = PDEXmlProcessorFactory.createDocumentBuilderWithErrorOnDOCTYPE();
Document document = docBuilder.newDocument();
Element containerElement = document.createElement(TargetDefinitionPersistenceHelper.LOCATION);
containerElement.setAttribute(TargetDefinitionPersistenceHelper.ATTR_LOCATION_TYPE, TYPE);
containerElement.setAttribute(ATTRIBUTE_URI, getUri());
for (Requirement requirement : requirements) {
Element requireElement = document.createElement(ELEMENT_REQUIRE);
requireElement.setTextContent(requirement.toString());
containerElement.appendChild(requireElement);
}
document.appendChild(containerElement);
StreamResult result = new StreamResult(new StringWriter());
TransformerFactory f = PDEXmlProcessorFactory.createTransformerFactoryWithErrorOnDOCTYPE();
Transformer transformer = f.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //$NON-NLS-1$
transformer.transform(new DOMSource(document), result);
String xml = result.getWriter().toString();
return xml;
} catch (Exception e) {
PDECore.log(e);
return null;
}
}

public void reload() {
fResolutionStatus = null;
fBundles = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich and others.
*
* 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.internal.core.target;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.IntStream;

import javax.xml.parsers.DocumentBuilder;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.target.ITargetLocation;
import org.eclipse.pde.core.target.ITargetLocationFactory;
import org.eclipse.pde.internal.core.util.PDEXmlProcessorFactory;
import org.osgi.resource.Requirement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.resource.CapReqBuilder;

public class RepositoryLocationFactory implements ITargetLocationFactory {

@Override
public ITargetLocation getTargetLocation(String type, String serializedXML) throws CoreException {
if (!RepositoryBundleContainer.TYPE.equals(type)) {
throw new CoreException(
Status.error(NLS.bind(Messages.TargetRefrenceLocationFactory_Unsupported_Type, type)));
}
try {
DocumentBuilder docBuilder = PDEXmlProcessorFactory.createDocumentBuilderWithErrorOnDOCTYPE();
Document document = docBuilder
.parse(new ByteArrayInputStream(serializedXML.getBytes(StandardCharsets.UTF_8)));
Element location = document.getDocumentElement();
NodeList childNodes = location.getChildNodes();
List<Requirement> requirements = IntStream.range(0, childNodes.getLength()).mapToObj(childNodes::item)
.filter(Element.class::isInstance).map(Element.class::cast)
.filter(element -> element.getNodeName()
.equalsIgnoreCase(RepositoryBundleContainer.ELEMENT_REQUIRE))
.flatMap(element -> {
String textContent = element.getTextContent();
Parameters parameters = new Parameters(textContent);
return CapReqBuilder.getRequirementsFrom(parameters).stream();
}).toList();
return new RepositoryBundleContainer(
location.getAttribute(RepositoryBundleContainer.ATTRIBUTE_URI), requirements);
} catch (Exception e) {
throw new CoreException(
Status.error(NLS.bind(Messages.TargetRefrenceLocationFactory_Parsing_Failed, e.getMessage()), e));
}
}

}
4 changes: 3 additions & 1 deletion ui/org.eclipse.pde.ui/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ Require-Bundle:
biz.aQute.bndlib;bundle-version="6.3.1"
Import-Package: org.eclipse.jdt.debug.ui.console,
org.eclipse.ui.internal.genericeditor,
org.osgi.service.event;version="[1.4,2.0.0)"
org.osgi.service.event;version="[1.4,2.0.0)",
org.osgi.service.repository;version="1.1.0",
org.osgi.util.promise;version="1.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: org.eclipse.pde.ui
Binary file added ui/org.eclipse.pde.ui/icons/dview16/memory_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions ui/org.eclipse.pde.ui/plugin.properties
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ projectConfigurator.label.bundle = Eclipse Plugin/OSGi Bundle
projectConfigurator.label.feature = Eclipse Feature
create.module.info.label.pde= Create module-info.java

locationProvider.reference.name = Target File
locationProvider.reference.description = Add a reference to another target file

locationProvider.description = Add a reference to another target file
locationProvider.name = Target File
locationProvider.repository.name = OSGi Repository
locationProvider.repository.description = Add content from an OSGi Repository according to the Repository Service Specification
39 changes: 37 additions & 2 deletions ui/org.eclipse.pde.ui/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2230,6 +2230,32 @@
type="org.eclipse.jface.viewers.ITreeContentProvider">
</adapter>
</factory>
<factory
adaptableType="org.eclipse.pde.internal.core.target.RepositoryBundleContainer"
class="org.eclipse.pde.internal.ui.shared.target.RepositoryBundleContainerAdapterFactory">
<adapter
type="org.eclipse.pde.ui.target.ITargetLocationHandler">
</adapter>
<adapter
type="org.eclipse.jface.viewers.ILabelProvider">
</adapter>
<adapter
type="org.eclipse.jface.viewers.ITreeContentProvider">
</adapter>
</factory>
<factory
adaptableType="org.eclipse.pde.internal.ui.shared.target.RepositoryBundleContainerAdapterFactory$RequirementNode"
class="org.eclipse.pde.internal.ui.shared.target.RepositoryBundleContainerAdapterFactory">
<adapter
type="org.eclipse.pde.ui.target.ITargetLocationHandler">
</adapter>
<adapter
type="org.eclipse.jface.viewers.ILabelProvider">
</adapter>
<adapter
type="org.eclipse.jface.viewers.ITreeContentProvider">
</adapter>
</factory>
</extension>
<extension
point="org.eclipse.pde.ui.targetLocationProvisioners">
Expand All @@ -2246,9 +2272,18 @@
class="org.eclipse.pde.internal.ui.shared.target.TargetReferenceLocationWizard"
icon="icons/obj16/target_profile_xml_obj.png"
id="org.eclipse.pde.ui.TargetReferenceProvisioner"
name="%locationProvider.name">
name="%locationProvider.reference.name">
<description>
%locationProvider.reference.description
</description>
</locationProvider>
<locationProvider
class="org.eclipse.pde.internal.ui.shared.target.RepositoryLocationWizard"
icon="icons/dview16/memory_view.png"
id="org.eclipse.pde.ui.TargetReferenceProvisioner"
name="%locationProvider.repository.name">
<description>
%locationProvider.description
%locationProvider.repository.description
</description>
</locationProvider>
</extension>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.pde.internal.core.PDEPreferencesManager;
import org.eclipse.pde.internal.ui.launcher.PDELogFileProvider;
import org.eclipse.pde.internal.ui.shared.target.RepositoryBundleContainerAdapterFactory;
import org.eclipse.pde.internal.ui.shared.target.TargetReferenceBundleContainerAdapterFactory;
import org.eclipse.pde.internal.ui.shared.target.TargetStatus;
import org.eclipse.pde.internal.ui.util.SWTUtil;
Expand Down Expand Up @@ -233,6 +234,7 @@ public void stop(BundleContext context) throws Exception {
Utilities.shutdown();
super.stop(context);
TargetReferenceBundleContainerAdapterFactory.LABEL_PROVIDER.dispose();
RepositoryBundleContainerAdapterFactory.LABEL_PROVIDER.dispose();
}

public PDELabelProvider getLabelProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class PDEPluginImages {

private static final String PATH_OBJ = ICONS_PATH + "obj16/"; //$NON-NLS-1$
private static final String PATH_VIEW = ICONS_PATH + "view16/"; //$NON-NLS-1$
private static final String PATH_DVIEW = ICONS_PATH + "dview16/"; //$NON-NLS-1$
private static final String PATH_LCL = ICONS_PATH + "elcl16/"; //$NON-NLS-1$
private static final String PATH_LCL_DISABLED = ICONS_PATH + "dlcl16/"; //$NON-NLS-1$
private static final String PATH_TOOL = ICONS_PATH + "etool16/"; //$NON-NLS-1$
Expand Down Expand Up @@ -316,6 +317,7 @@ public class PDEPluginImages {
* View
*/
public static final ImageDescriptor DESC_ARGUMENT_TAB = create(PATH_VIEW, "variable_tab.png"); //$NON-NLS-1$
public static final ImageDescriptor DESC_TARGET_REPO = create(PATH_DVIEW, "memory_view.png"); //$NON-NLS-1$

private static ImageDescriptor create(String prefix, String name) {
return ImageDescriptor.createFromURL(makeImageURL(prefix, name));
Expand Down
Loading
Loading