diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.java index a9ffafcfcb4..61227d89d7c 100755 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2021 IBM Corporation and others. + * Copyright (c) 2009, 2023 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -80,6 +80,8 @@ public class Messages extends NLS { public static String RemoteTargetHandle_invalid_URI; public static String RemoteTargetHandle_malformed_URL; public static String RemoteTargetHandle_ioproblem; + public static String VirtualArtifactRepository_0; + public static String VirtualArtifactRepository_1; static { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.properties b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.properties index 2465fc78c5b..0ca65c26be8 100755 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.properties +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/Messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2009, 2021 IBM Corporation and others. +# Copyright (c) 2009, 2023 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -73,4 +73,6 @@ TargetRefrenceLocationFactory_Unsupported_Type=Type {0} is not supported by this TargetRefrenceLocationFactory_Parsing_Failed=Parsing location content failed: {0} RemoteTargetHandle_invalid_URI=Invalid URI: {0} RemoteTargetHandle_malformed_URL=URI {0} can not be converted to an URL: {1} -RemoteTargetHandle_ioproblem=Reading URI {0} failed: {1} \ No newline at end of file +RemoteTargetHandle_ioproblem=Reading URI {0} failed: {1} +VirtualArtifactRepository_0=Target bundle is not resolved: {0} +VirtualArtifactRepository_1=Artifact location not found for descriptor: {0} \ No newline at end of file diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java index 15175d3d8ef..185af648461 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010, 2018 EclipseSource Inc. and others. + * Copyright (c) 2010, 2023 EclipseSource Inc. and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -1078,11 +1078,24 @@ private void resolveWithPlanner(ITargetDefinition target, IProgressMonitor monit request.setInstallableUnitProfileProperty(unit, PROP_INSTALLED_IU, Boolean.toString(true)); } - ProvisioningContext context = new ProvisioningContext(getAgent()); + List extraArtifactRepositories = new ArrayList<>(); + List extraMetadataRepositories = new ArrayList<>(); + getAdditionalProvisionIUs(target, extraArtifactRepositories, extraMetadataRepositories); + ProvisioningContext context = new ProvisioningContext(getAgent()) { + @Override + public IQueryable getArtifactRepositories(IProgressMonitor monitor) { + return QueryUtil.compoundQueryable(super.getArtifactRepositories(monitor), + (query, ignore) -> query.perform(extraArtifactRepositories.iterator())); + } + @Override + public IQueryable getMetadata(IProgressMonitor monitor) { + return QueryUtil.compoundQueryable(super.getMetadata(monitor), + QueryUtil.compoundQueryable(extraMetadataRepositories)); + } + }; context.setProperty(ProvisioningContext.FOLLOW_REPOSITORY_REFERENCES, Boolean.toString(true)); context.setMetadataRepositories(getMetadataRepositories(target).toArray(URI[]::new)); context.setArtifactRepositories(getArtifactRepositories(target).toArray(URI[]::new)); - context.setExtraInstallableUnits(getAdditionalProvisionIUs(target)); IProvisioningPlan plan = planner.getProvisioningPlan(request, context, subMonitor.split(20)); IStatus status = plan.getStatus(); @@ -1553,8 +1566,9 @@ private Collection getMetadataRepositories(ITargetDefinition target) throws return result; } - private List getAdditionalProvisionIUs(ITargetDefinition target) throws CoreException { - List result = new ArrayList<>(); + private void getAdditionalProvisionIUs(ITargetDefinition target, + Collection extraArtifactRepositories, + Collection extraMetadataRepositories) throws CoreException { ITargetLocation[] containers = target.getTargetLocations(); if (containers != null) { for (ITargetLocation container : containers) { @@ -1564,17 +1578,20 @@ private List getAdditionalProvisionIUs(ITargetDefinition targe } if (container instanceof TargetReferenceBundleContainer targetRefContainer) { ITargetDefinition referencedTargetDefinition = targetRefContainer.getTargetDefinition(); - result.addAll(getAdditionalProvisionIUs(referencedTargetDefinition)); + getAdditionalProvisionIUs(referencedTargetDefinition, extraArtifactRepositories, + extraMetadataRepositories); continue; } if (!container.isResolved()) { container.resolve(target, new NullProgressMonitor()); } - InstallableUnitGenerator.generateInstallableUnits(container.getBundles(), container.getFeatures()) - .forEach(result::add); + extraArtifactRepositories.add(new VirtualArtifactRepository(getAgent(), container)); + List installableUnits = InstallableUnitGenerator // + .generateInstallableUnits(container.getBundles(), container.getFeatures()) // + .toList(); + extraMetadataRepositories.add(new VirtualMetadataRepository(getAgent(), installableUnits)); } } - return result; } private static final String NATIVE_ARTIFACTS = "nativeArtifacts"; //$NON-NLS-1$ diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/VirtualArtifactRepository.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/VirtualArtifactRepository.java new file mode 100644 index 00000000000..1ded9d62767 --- /dev/null +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/VirtualArtifactRepository.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2023 Patrick Ziegler 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: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.core.target; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.equinox.frameworkadmin.BundleInfo; +import org.eclipse.equinox.internal.p2.metadata.expression.QueryResult; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.ProvisionException; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; +import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAction; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.query.IQueryable; +import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRequest; +import org.eclipse.equinox.p2.repository.artifact.spi.AbstractArtifactRepository; +import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.target.ITargetLocation; +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.ifeature.IFeature; +import org.eclipse.pde.internal.core.ifeature.IFeatureModel; + +import aQute.bnd.osgi.Jar; +import aQute.bnd.osgi.WriteResource; + +/** + * In-Memory representation of a artifact repository based on a non-IU target + * location. This repository is used during the planner resolution of an IU + * target location to supply artifacts from other (non-IU) locations. + */ +@SuppressWarnings("restriction") +public class VirtualArtifactRepository extends AbstractArtifactRepository { + private static final String NAME = "Non-IU Artifact Repository @ "; //$NON-NLS-1$ + private static final String DESCRIPTION = """ + In-Memory repository created for a single Non-IU repository, used + during the planner resolution of a real IU repository. + """; // $NON-NLS-1$ //$NON-NLS-1$ + private static final String MEMORY_PREFIX = "memory://"; //$NON-NLS-1$ + // BundleInfo or IFeatureModel + private final Map artifacts = new HashMap<>(); + + public VirtualArtifactRepository(IProvisioningAgent agent, ITargetLocation targetLocation) { + super(agent, NAME + getLocationSafe(targetLocation), targetLocation.getType(), null, + URI.create(MEMORY_PREFIX + UUID.randomUUID()), DESCRIPTION + '\n' + targetLocation.serialize(), null, + null); + Assert.isTrue(targetLocation.isResolved()); + for (TargetBundle targetBundle : targetLocation.getBundles()) { + if (!targetBundle.getStatus().isOK()) { + PDECore.log(Status.warning(NLS.bind(Messages.VirtualArtifactRepository_0, targetBundle))); + continue; + } + BundleInfo bundleInfo = targetBundle.getBundleInfo(); + IArtifactKey artifactKey = BundlesAction.createBundleArtifactKey(bundleInfo.getSymbolicName(), + bundleInfo.getVersion()); + IArtifactDescriptor artifactDesriptor = new ArtifactDescriptor(artifactKey); + artifacts.put(artifactDesriptor, bundleInfo); + } + for (TargetFeature targetFeature : targetLocation.getFeatures()) { + IArtifactKey artifactKey = FeaturesAction.createFeatureArtifactKey(targetFeature.getId(), + targetFeature.getVersion()); + IArtifactDescriptor artifactDesriptor = new ArtifactDescriptor(artifactKey); + artifacts.put(artifactDesriptor, targetFeature.getFeatureModel()); + } + } + + private static String getLocationSafe(ITargetLocation targetLocation) { + try { + return targetLocation.getLocation(false); + } catch (CoreException e) { + return ""; //$NON-NLS-1$ + } + } + + @Override + public IStatus getRawArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) { + Object artifactModel = artifacts.get(descriptor); + if (artifactModel == null) { + return Status.error(NLS.bind(Messages.VirtualArtifactRepository_1, descriptor)); + } + try (InputStream is = getArtifactInputStream(artifactModel)) { + is.transferTo(destination); + return Status.OK_STATUS; + } catch (Exception e) { + return Status.error(e.getLocalizedMessage(), e); + } + } + + private InputStream getArtifactInputStream(Object artifactModel) throws Exception { + if (artifactModel instanceof BundleInfo bundleInfo) { + URI location = bundleInfo.getLocation(); + if (location == null) { + throw new FileNotFoundException(bundleInfo.getSymbolicName()); + } + return location.toURL().openStream(); + } else if (artifactModel instanceof IFeatureModel featureModel) { + String installLocation = featureModel.getInstallLocation(); + if (installLocation != null) { + File featureJar = new File(installLocation); + if (featureJar.isFile()) { + return new FileInputStream(featureJar); + } + } + IFeature feature = featureModel.getFeature(); + // Generate in-memory feature.xml + ByteArrayOutputStream featureContent = new ByteArrayOutputStream(); + try (PrintWriter writer = new PrintWriter(featureContent)) { + feature.write("", writer); //$NON-NLS-1$ + } + // Generate in-memory feature jar (with only the feature.xml) + try (Jar jar = new Jar(feature.getId() + '_' + feature.getVersion())) { + jar.putResource("feature.xml", new WriteResource() { //$NON-NLS-1$ + @Override + public void write(OutputStream out) throws Exception { + out.write(featureContent.toByteArray()); + } + + @Override + public long lastModified() { + return 0L; + } + }); + ByteArrayOutputStream featureJar = new ByteArrayOutputStream(); + jar.write(featureJar); + return new ByteArrayInputStream(featureJar.toByteArray()); + } + } + throw new IllegalArgumentException(artifactModel.toString()); + } + + @Override + public IQueryable descriptorQueryable() { + return (query, monitor) -> query.perform(artifacts.keySet().iterator()); + } + + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + return new QueryResult<>(artifacts.keySet().stream().map(IArtifactDescriptor::getArtifactKey).iterator()); + } + + @Override + public boolean contains(IArtifactDescriptor descriptor) { + return artifacts.containsKey(descriptor); + } + + @Override + public boolean contains(IArtifactKey key) { + return artifacts.keySet().stream().anyMatch(descriptor -> key.equals(descriptor.getArtifactKey())); + } + + @Override + public IStatus getArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) { + return getRawArtifact(descriptor, destination, monitor); + } + + @Override + public IArtifactDescriptor[] getArtifactDescriptors(IArtifactKey key) { + return artifacts.keySet().stream() // + .filter(descriptor -> key.equals(descriptor.getArtifactKey())) // + .toArray(IArtifactDescriptor[]::new); + } + + @Override + public IStatus getArtifacts(IArtifactRequest[] requests, IProgressMonitor monitor) { + MultiStatus multiStatus = new MultiStatus(getClass(), IStatus.INFO, "Perform Artifact Requests"); //$NON-NLS-1$ + SubMonitor subMonitor = SubMonitor.convert(monitor, requests.length); + boolean isOk = true; + for (IArtifactRequest request : requests) { + request.perform(this, subMonitor.split(1)); + multiStatus.add(request.getResult()); + isOk &= request.getResult().isOK(); + } + return isOk ? Status.OK_STATUS : multiStatus; + } + + @Override + public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException { + throw new ProvisionException("Artifact repository must not be modified!"); //$NON-NLS-1$ + } + +} diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/VirtualMetadataRepository.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/VirtualMetadataRepository.java new file mode 100644 index 00000000000..b5dcc0e2218 --- /dev/null +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/VirtualMetadataRepository.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2023 Patrick Ziegler 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: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.core.target; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +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.repository.IRepositoryReference; +import org.eclipse.equinox.p2.repository.metadata.spi.AbstractMetadataRepository; + +/** + * In-Memory representation of a metadata repository based on a non-IU target + * location. This repository is used during the planner resolution of an IU + * target location to supply the metadata from other (non-IU) locations. + */ +public class VirtualMetadataRepository extends AbstractMetadataRepository { + private final List installableUnits; + + public VirtualMetadataRepository(IProvisioningAgent agent, List installableUnits) { + super(agent); + this.installableUnits = List.copyOf(installableUnits); + } + + + @Override + public Collection getReferences() { + return Collections.emptySet(); + } + + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + return query.perform(installableUnits.iterator()); + } + + @Override + public void initialize(RepositoryState state) { + // nothing to do + } +} diff --git a/ui/org.eclipse.pde.ui.tests/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.ui.tests/META-INF/MANIFEST.MF index cdb72b61dfc..9955a310935 100644 --- a/ui/org.eclipse.pde.ui.tests/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.ui.tests/META-INF/MANIFEST.MF @@ -30,6 +30,7 @@ Require-Bundle: org.eclipse.pde.ui, org.eclipse.equinox.p2.repository, org.eclipse.equinox.p2.metadata, org.eclipse.equinox.p2.engine, + org.eclipse.equinox.p2.publisher.eclipse, org.eclipse.ui.forms, org.eclipse.ui.workbench.texteditor, org.eclipse.jface.text, diff --git a/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/IUBundleContainerTests.java b/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/IUBundleContainerTests.java index c97b2e16727..930e2db596f 100644 --- a/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/IUBundleContainerTests.java +++ b/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/IUBundleContainerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2018 IBM Corporation and others. + * Copyright (c) 2009, 2023 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -17,16 +17,22 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.jar.JarFile; import javax.xml.parsers.DocumentBuilder; @@ -36,9 +42,15 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.frameworkadmin.BundleInfo; +import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; +import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAction; import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.query.QueryUtil; +import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; +import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; import org.eclipse.pde.core.plugin.IPluginModelBase; @@ -47,11 +59,13 @@ import org.eclipse.pde.core.target.ITargetLocation; import org.eclipse.pde.core.target.ITargetPlatformService; import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.core.target.DirectoryBundleContainer; import org.eclipse.pde.internal.core.target.IUBundleContainer; import org.eclipse.pde.internal.core.target.P2TargetUtils; import org.eclipse.pde.internal.core.target.TargetDefinition; import org.eclipse.pde.internal.core.target.TargetDefinitionPersistenceHelper; import org.eclipse.pde.internal.core.target.TargetPersistence38Helper; +import org.eclipse.pde.internal.core.target.VirtualArtifactRepository; import org.eclipse.pde.ui.tests.PDETestsPlugin; import org.junit.Test; import org.w3c.dom.Document; @@ -182,6 +196,42 @@ public void testResolveSingleBundle() throws Exception { doResolutionTest(new String[]{"bundle.a1"}, bundles); } + /** + * Tests whether the in-memory artifact repository is correctly created from + * a non-IU target location. + */ + @Test + public void testResolveVirtualArtifactRepository() throws Exception { + IArtifactRepository repo = createVirtualArtifactRepository(); + // Bundles + assertTrue(repo.contains(BundlesAction.createBundleArtifactKey("bundle.a1", "1.0.0"))); + assertTrue(repo.contains(BundlesAction.createBundleArtifactKey("bundle.a2", "1.0.0"))); + assertTrue(repo.contains(BundlesAction.createBundleArtifactKey("bundle.a3", "1.0.0"))); + assertTrue(repo.contains(BundlesAction.createBundleArtifactKey("bundle.b1", "1.0.0"))); + assertTrue(repo.contains(BundlesAction.createBundleArtifactKey("bundle.b2", "1.0.0"))); + assertTrue(repo.contains(BundlesAction.createBundleArtifactKey("bundle.b3", "1.0.0"))); + // Features + assertTrue(repo.contains(FeaturesAction.createFeatureArtifactKey("feature.a", "1.0.0"))); + assertTrue(repo.contains(FeaturesAction.createFeatureArtifactKey("feature.b", "1.0.0"))); + } + + /** + * Tests whether the artifacts can be downloaded from the in-memory + * repository. + */ + @Test + public void testGetVirtualRepositoryArtifact() throws Exception { + IArtifactRepository repo = createVirtualArtifactRepository(); + // Bundles + IArtifactKey artifactKey = BundlesAction.createBundleArtifactKey("bundle.a1", "1.0.0"); + IArtifactDescriptor artifactDesriptor = new ArtifactDescriptor(artifactKey); + assertEquals(IStatus.OK, repo.getArtifact(artifactDesriptor, new ByteArrayOutputStream(), null).getCode()); + // Features + artifactKey = FeaturesAction.createFeatureArtifactKey("feature.a", "1.0.0"); + artifactDesriptor = new ArtifactDescriptor(artifactKey); + assertEquals(IStatus.OK, repo.getArtifact(artifactDesriptor, new ByteArrayOutputStream(), null).getCode()); + } + /** * Tests that contents should be equal. */ @@ -327,9 +377,58 @@ public void testExternalModelManagerPreferences() throws Exception { } /** - * Creates an IU bundle container with the specified IUs from the test repository. + * Creates an in-memory artifact repository over {@code test/sites/site.a.b} * - * @param unitIds identifiers of IU's to add to the container + * @return in-memory artifact repository + */ + protected VirtualArtifactRepository createVirtualArtifactRepository() throws Exception { + File repoFolder = new File(getURI("/tests/sites/site.a.b")); + // DirectoryBundleContainer expects features to be unpacked + File features = new File(repoFolder, "features"); + for (File jarFile : features.listFiles()) { + if (jarFile.isDirectory()) { + continue; + } + String jarFileName = jarFile.getName(); + String jarFileBaseName = jarFileName.substring(0, jarFileName.length() - ".jar".length()); + + File outDir = new File(features, jarFileBaseName); + outDir.mkdir(); + outDir.deleteOnExit(); + + try (JarFile jar = new JarFile(jarFile)) { + jar.stream().forEach(jarEntry -> { + File outFile = new File(outDir, jarEntry.getName()); + outFile.deleteOnExit(); + if (outFile.isDirectory()) { + outFile.mkdir(); + return; + } + try (InputStream is = jar.getInputStream(jarEntry)) { + try (OutputStream os = new FileOutputStream(outFile)) { + is.transferTo(os); + } + } catch (IOException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + }); + } + } + // + ITargetLocation container = new DirectoryBundleContainer(repoFolder.getAbsolutePath()); + ITargetDefinition definition = getNewTarget(); + definition.setTargetLocations(new ITargetLocation[] { container }); + container.resolve(definition, null); + return new VirtualArtifactRepository(null, container); + } + + /** + * Creates an IU bundle container with the specified IUs from the test + * repository. + * + * @param unitIds + * identifiers of IU's to add to the container * @return bundle container */ protected IUBundleContainer createContainer(String[] unitIds) throws Exception {