| /******************************************************************************* |
| * Copyright (c) 2020 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.tests.container; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicReference; |
| import org.eclipse.osgi.container.Module; |
| import org.eclipse.osgi.container.Module.State; |
| import org.eclipse.osgi.container.ModuleCapability; |
| import org.eclipse.osgi.container.ModuleContainer; |
| import org.eclipse.osgi.container.ModuleRevision; |
| import org.eclipse.osgi.container.ModuleRevisionBuilder; |
| import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory; |
| import org.eclipse.osgi.report.resolution.ResolutionReport; |
| import org.eclipse.osgi.tests.container.dummys.DummyContainerAdaptor; |
| import org.junit.Test; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.hooks.resolver.ResolverHook; |
| import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; |
| import org.osgi.framework.namespace.PackageNamespace; |
| import org.osgi.framework.wiring.BundleCapability; |
| import org.osgi.framework.wiring.BundleRequirement; |
| import org.osgi.framework.wiring.BundleRevision; |
| |
| public class ModuleContainerUsageTest extends AbstractTest { |
| static final String EE_INDEX = "internal.ee.index"; |
| |
| @Test |
| public void testMultipleExecutionEnvironments() throws BundleException { |
| |
| final List<BundleCapability> executionEnvironments = Collections.synchronizedList(new ArrayList<>()); |
| |
| /** |
| * A resolver hook that filters packages based on the required EE of a module |
| */ |
| ResolverHook resolverHook = new ResolverHook() { |
| |
| @Override |
| public void filterSingletonCollisions(BundleCapability singleton, |
| Collection<BundleCapability> collisionCandidates) { |
| // don't care about possible collisions |
| collisionCandidates.clear(); |
| } |
| |
| @Override |
| public void filterResolvable(Collection<BundleRevision> candidates) { |
| // don't stop any revision from resolving |
| } |
| |
| @Override |
| public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) { |
| if (PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) { |
| // only filter based on EE for package requirements |
| filterBasedOnEE( |
| (AtomicReference<Long>) ((ModuleRevision) requirement.getRevision()).getRevisionInfo(), |
| candidates); |
| } |
| } |
| |
| private void filterBasedOnEE(AtomicReference<Long> revisionInfo, |
| Collection<BundleCapability> candidates) { |
| Long eeIndex = revisionInfo.get(); |
| // if the eeIndex is set for the revision then check if the package is |
| // available in the specified EE |
| if (eeIndex != null) { |
| Iterator<BundleCapability> iCandidates = candidates.iterator(); |
| while (iCandidates.hasNext()) { |
| ModuleCapability candidate = (ModuleCapability) iCandidates.next(); |
| // only check the ee index for the packages from the system module (id=0) |
| if (candidate.getRevision().getRevisions().getModule().getId() == 0) { |
| Object candidateEEIndex = candidate.getAttributes().get(EE_INDEX); |
| if (candidateEEIndex != null && !eeIndex.equals(candidateEEIndex)) { |
| iCandidates.remove(); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void end() { |
| // do nothing special at the end of resolution |
| } |
| }; |
| DummyContainerAdaptor adaptor = createDummyAdaptor(resolverHook); |
| ModuleContainer container = adaptor.getContainer(); |
| |
| // Notice that there are duplicate packages exported for each EE_INDEX |
| StringBuilder systemPackages = new StringBuilder(); |
| systemPackages.append("test.pkg1; " + EE_INDEX + ":Long=1"); |
| systemPackages.append(",test.pkg1; test.pkg2; " + EE_INDEX + ":Long=2"); |
| systemPackages.append(",test.pkg1; test.pkg2; test.pkg3; " + EE_INDEX + ":Long=3"); |
| systemPackages.append(",test.pkg1; test.pkg2; test.pkg3; test.pkg4; " + EE_INDEX + ":Long=4"); |
| systemPackages.append(",test.pkg1; test.pkg2; test.pkg3; test.pkg4; test.pkg5; " + EE_INDEX + ":Long=5"); |
| |
| // Notice that each defined EE capability has an EE_INDEX defined that matches |
| // the set of packages |
| // above for the specified EE_INDEX |
| StringBuilder systemEEs = new StringBuilder(); |
| systemEEs.append( |
| "osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5\"; " + EE_INDEX |
| + ":Long=1"); |
| systemEEs.append( |
| ",osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6\"; " + EE_INDEX |
| + ":Long=2"); |
| systemEEs.append( |
| ",osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7\"; " |
| + EE_INDEX + ":Long=3"); |
| systemEEs.append( |
| ",osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8\"; " |
| + EE_INDEX + ":Long=4"); |
| systemEEs.append( |
| ",osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 9.0\"; " |
| + EE_INDEX + ":Long=5"); |
| |
| ModuleRevisionBuilder systemModuleBuilder = OSGiManifestBuilderFactory.createBuilder(getSystemManifest(), |
| Constants.SYSTEM_BUNDLE_SYMBOLICNAME, systemPackages.toString(), systemEEs.toString()); |
| Module systemBundle = container.install(null, Constants.SYSTEM_BUNDLE_LOCATION, systemModuleBuilder, |
| new AtomicReference<Long>()); |
| executionEnvironments.addAll(systemBundle.getCurrentRevision() |
| .getDeclaredCapabilities(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE)); |
| |
| Module m1 = installModule(container, "m1", "1.0", "test.pkg1", "JavaSE-1.5", |
| executionEnvironments); |
| Module m2 = installModule(container, "m2", "1.0", "test.pkg1,test.pkg2", "JavaSE-1.5", |
| executionEnvironments); |
| |
| Module m3 = installModule(container, "m3", "1.0", "test.pkg1,test.pkg2,test.pkg3", "JavaSE-1.7", |
| executionEnvironments); |
| Module m4 = installModule(container, "m4", "1.0", "test.pkg1,test.pkg2,test.pkg3,test.pkg4", "JavaSE-1.7", |
| executionEnvironments); |
| |
| ResolutionReport report = container.resolve(Arrays.asList(m1, m2, m3, m4), false); |
| assertEquals("Wrong state for m1.", State.RESOLVED, m1.getState()); |
| assertEquals("Wrong state for m2.", State.INSTALLED, m2.getState()); |
| assertEquals("Wrong state for m3.", State.RESOLVED, m3.getState()); |
| assertEquals("Wrong state for m4.", State.INSTALLED, m4.getState()); |
| |
| String resolutionErrorM2 = report.getResolutionReportMessage(m2.getCurrentRevision()); |
| assertNotNull("Expected resolution message", resolutionErrorM2); |
| assertTrue("Wrong resolution message.", resolutionErrorM2.contains("test.pkg2")); |
| |
| String resolutionErrorM4 = report.getResolutionReportMessage(m4.getCurrentRevision()); |
| assertNotNull("Expected resolution message", resolutionErrorM4); |
| assertTrue("Wrong resolution message.", resolutionErrorM4.contains("test.pkg4")); |
| } |
| |
| private Map<String, String> getSystemManifest() { |
| Map<String, String> result = new HashMap<>(); |
| result.put(Constants.BUNDLE_MANIFESTVERSION, "2"); |
| result.put(Constants.BUNDLE_SYMBOLICNAME, "org.eclipse.osgi"); |
| result.put(Constants.BUNDLE_VERSION, "1.0"); |
| result.put(Constants.EXPORT_PACKAGE, "org.osgi.framework; version=1.10"); |
| return result; |
| } |
| |
| Module installModule(ModuleContainer container, String bsn, String version, String imports, String eeReqs, |
| List<BundleCapability> availableEEs) throws BundleException { |
| ModuleRevisionBuilder builder = createModuleRevisionBuilder(bsn, version, imports, eeReqs); |
| Module m = container.install(null, bsn, builder, new AtomicReference<Long>()); |
| // Use a revision info that holds the EE_INDEX of the bundle based |
| // on the EE capability that matches the modules EE requirement |
| Collection<BundleRequirement> eeBundleReqs = m.getCurrentRevision() |
| .getDeclaredRequirements(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE); |
| |
| eeBundleReqs.stream().findFirst().ifPresent((r) -> { |
| // notice that we depend on the available EEs |
| // to be ordered from least to greatest |
| for (BundleCapability ee : availableEEs) { |
| if (r.matches(ee)) { |
| ((AtomicReference<Long>) m.getCurrentRevision().getRevisionInfo()) |
| .set((Long) ee.getAttributes().get(EE_INDEX)); |
| return; |
| } |
| } |
| }); |
| return m; |
| } |
| |
| @SuppressWarnings("deprecation") |
| ModuleRevisionBuilder createModuleRevisionBuilder(String bsn, String version, String imports, String eeReq) |
| throws BundleException { |
| Map<String, String> manifest = new HashMap<>(); |
| manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); |
| manifest.put(Constants.BUNDLE_SYMBOLICNAME, bsn); |
| manifest.put(Constants.BUNDLE_VERSION, version); |
| manifest.put(Constants.IMPORT_PACKAGE, imports); |
| manifest.put(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, eeReq); |
| return OSGiManifestBuilderFactory.createBuilder(manifest); |
| } |
| } |