package biz.aQute.resolve; import static test.lib.Utils.createRepo; import java.io.File; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.mockito.Mockito; import org.osgi.framework.Version; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; import org.osgi.resource.Wire; import org.osgi.service.log.LogService; import org.osgi.service.repository.Repository; import org.osgi.service.resolver.ResolutionException; import org.osgi.service.resolver.Resolver; import aQute.bnd.build.Run; import aQute.bnd.build.Workspace; import aQute.bnd.build.model.BndEditModel; import aQute.bnd.build.model.EE; import aQute.bnd.build.model.clauses.ExportedPackage; import aQute.bnd.deployer.repository.FixedIndexedRepo; import aQute.bnd.header.Parameters; import aQute.bnd.osgi.Domain; import aQute.bnd.osgi.Processor; import aQute.bnd.osgi.repository.ResourcesRepository; import aQute.bnd.osgi.resource.CapReqBuilder; import aQute.bnd.osgi.resource.ResourceBuilder; import aQute.lib.io.IO; import aQute.libg.reporter.ReporterAdapter; import junit.framework.TestCase; import test.lib.MockRegistry; @SuppressWarnings({ "restriction", "deprecation" }) public class ResolveTest extends TestCase { private static final LogService log = new LogReporter(new ReporterAdapter(System.out)); /** * Observed at the OSGi Community Event. When specifying the following: * -resolve.effective: active;skip:="osgi.service" OSGi service capabilities * are skipped but other active-time requirements are used. This is correct, * but it only works if the property is defined directly inside the bndrun * being resolved. If the property is inherited from a parent bndrun then it * no longer applies... * <p> * Tried this but seems to work in all combinations as expected */ public void testIncludeBndrun() throws Exception { assertInclude("intop.bndrun", "top"); assertInclude("ininclude.bndrun", "include"); assertInclude("inworkspace.bndrun", "workspace"); } /* * Create a BndrunResolveContext with a skip of 'workspace' in the * workspace, and then use different files that get -resolve.effective from * the include file or the bndrun file. */ private void assertInclude(String file, String value) throws Exception { LogService log = Mockito.mock(LogService.class); File f = IO.getFile("testdata/resolve/includebndrun/" + file); File wsf = IO.getFile("testdata/ws"); Workspace ws = Workspace.getWorkspace(wsf); ws.setProperty("-resolve.effective", "active;skip:='workspace'"); Run run = Run.createRun(ws, f); BndrunResolveContext context = new BndrunResolveContext(run, run, ws, log); context.init(); Map<String,Set<String>> effectiveSet = context.getEffectiveSet(); assertNotNull(effectiveSet.get("active")); assertTrue(effectiveSet.get("active").contains(value)); } /** * Missing default version */ public void testDefaultVersionsForJava() throws Exception { Run run = Run.createRun(null, IO.getFile("testdata/defltversions/run.bndrun")); try (ProjectResolver pr = new ProjectResolver(run);) { Map<Resource,List<Wire>> resolve = pr.resolve(); assertTrue(pr.check()); assertNotNull(resolve); assertTrue(resolve.size() > 0); System.out.println(resolve); } } /** * The enRoute base guard resolved but is missing bundles, the runbundles do * not run */ public void testenRouteGuard() throws Exception { MockRegistry registry = new MockRegistry(); Repository repo = createRepo(IO.getFile("testdata/enroute/index.xml")); registry.addPlugin(repo); List<Requirement> reqs = CapReqBuilder.getRequirementsFrom( new Parameters("osgi.wiring.package;filter:='(osgi.wiring.package=org.osgi.service.async)'")); Collection<Capability> pack = repo.findProviders(reqs).get(reqs.get(0)); assertEquals(2, pack.size()); ResourceBuilder b = new ResourceBuilder(); File guard = IO.getFile("testdata/enroute/osgi.enroute.base.guard.jar"); Domain manifest = Domain.domain(guard); b.addManifest(manifest); Repository resourceRepository = new ResourcesRepository(b.build()); registry.addPlugin(resourceRepository); Processor model = new Processor(); model.setRunfw("org.eclipse.osgi"); model.setRunblacklist( "osgi.identity;filter:='(osgi.identity=osgi.enroute.base.api)',osgi.identity;filter:='(osgi.identity=osgi.cmpn)',osgi.identity;filter:='(osgi.identity=osgi.core)"); model.setRunRequires("osgi.identity;filter:='(osgi.identity=osgi.enroute.base.guard)'"); model.setRunee("JavaSE-1.8"); try { BndrunResolveContext context = new BndrunResolveContext(model, null, registry, log); Resolver resolver = new BndResolver(new ResolverLogger(4)); Map<Resource,List<Wire>> resolved = resolver.resolve(context); Set<Resource> resources = resolved.keySet(); } catch (ResolutionException e) { String msg = e.getMessage().replaceAll("\\[caused by:", "\n->"); System.out.println(msg); fail(msg); } } /** * Test if we can augment * * @throws Exception */ public void testResolveWithAugments() throws Exception { // Add requirement assertAugmentResolve( "org.apache.felix.gogo.shell;capability:='foo;foo=gogo';requirement:='foo;filter:=\"(foo=*)\"'", "foo;filter:='(foo=gogo)'", null); // Default effective assertAugmentResolve("org.apache.felix.gogo.shell;capability:='foo;foo=gogo'", "foo;filter:='(foo=*)'", null); // Wildcard name assertAugmentResolve("*.shell;capability:='foo;foo=gogo'", "foo;filter:='(foo=gogo)'", null); assertAugmentResolve("org.apache.felix.gogo.*;capability:='foo;foo=gogo'", "foo;filter:='(foo=*)'", null); assertAugmentResolveFails("gogo.*;capability:='foo;foo=gogo'", "foo;filter:='(foo=*)'", null); // Version range assertAugmentResolve("org.apache.felix.gogo.*;version='[0,1)';capability:='foo;foo=gogo'", "foo;filter:='(foo=*)'", null); assertAugmentResolveFails("org.apache.felix.gogo.*;version='[1,2)';capability:='foo;foo=gogo'", "foo;filter:='(foo=*)'", null); // Effective assertAugmentResolve("org.apache.felix.gogo.shell;capability:='foo;foo=gogo;effective:=foo'", "foo;filter:='(foo=gogo)';effective:=foo", "foo"); assertAugmentResolveFails("org.apache.felix.gogo.shell;capability:='foo;foo=gogo;effective:=bar'", "foo;filter:='(foo=*)';effective:=foo", "foo"); } private static void assertAugmentResolveFails(String augment, String require, String effective) throws Exception { try { assertAugmentResolve(augment, require, effective); fail("Failed to fail augment=" + augment + ", require=" + require + ", effective=" + effective); } catch (AssertionError | ResolutionException e) { // Yup, expected } } private static void assertAugmentResolve(String augment, String require, String effective) throws Exception { MockRegistry registry = new MockRegistry(); registry.addPlugin(createRepo(IO.getFile("testdata/repo3.index.xml"))); Processor model = new Processor(); model.setRunfw("org.apache.felix.framework"); model.setProperty("-augment", augment); model.setRunRequires(require); if (effective != null) model.setProperty("-resolve.effective", effective); BndrunResolveContext context = new BndrunResolveContext(model, null, registry, log); Resolver resolver = new BndResolver(new ResolverLogger(4)); Map<Resource,List<Wire>> resolved = resolver.resolve(context); Set<Resource> resources = resolved.keySet(); Resource resource = getResource(resources, "org.apache.felix.gogo.runtime", "0.10"); assertNotNull(resource); } /** * Test minimal setup * * @throws URISyntaxException * @throws MalformedURLException */ public void testMinimalSetup() throws MalformedURLException, URISyntaxException { File index = IO.getFile("testdata/repo3.index.xml"); FixedIndexedRepo fir = new FixedIndexedRepo(); fir.setLocations(index.toURI().toString()); Processor model = new Processor(); model.setProperty("-runfw", "org.apache.felix.framework"); model.setProperty("-runrequires", "osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)'"); BndrunResolveContext context = new BndrunResolveContext(model, null, model, log); context.setLevel(0); context.addRepository(fir); context.init(); Resolver resolver = new BndResolver(new ResolverLogger(4)); try { Map<Resource,List<Wire>> resolved = resolver.resolve(context); Set<Resource> resources = resolved.keySet(); Resource shell = getResource(resources, "org.apache.felix.gogo.shell", "0.10.0"); assertNotNull(shell); } catch (ResolutionException e) { e.printStackTrace(); fail("Resolve failed"); } } /** * Test if we can resolve with a distro * * @throws ResolutionException */ public void testResolveWithDistro() throws ResolutionException { MockRegistry registry = new MockRegistry(); registry.addPlugin(createRepo(IO.getFile("testdata/repo3.index.xml"))); BndEditModel model = new BndEditModel(); model.setDistro(Arrays.asList("testdata/distro.jar;version=file")); List<Requirement> requires = new ArrayList<Requirement>(); CapReqBuilder capReq = CapReqBuilder.createBundleRequirement("org.apache.felix.gogo.shell", "[0,1)"); requires.add(capReq.buildSyntheticRequirement()); model.setRunRequires(requires); BndrunResolveContext context = new BndrunResolveContext(model, registry, log); context.setLevel(0); context.init(); Resolver resolver = new BndResolver(new ResolverLogger(4)); Map<Resource,List<Wire>> resolved = resolver.resolve(context); Set<Resource> resources = resolved.keySet(); Resource shell = getResource(resources, "org.apache.felix.gogo.shell", "0.10.0"); assertNotNull(shell); } /** * This is a basic test of resolving. This test is paired with * {@link #testResolveWithDistro()}. If you change the resources, make sure * this is done in the same way. The {@link #testResolveWithDistro()} has a * negative check while this one checks positive. */ public void testSimpleResolve() { MockRegistry registry = new MockRegistry(); registry.addPlugin(createRepo(IO.getFile("testdata/repo3.index.xml"))); BndEditModel model = new BndEditModel(); model.setRunFw("org.apache.felix.framework"); List<Requirement> requires = new ArrayList<Requirement>(); CapReqBuilder capReq = CapReqBuilder.createBundleRequirement("org.apache.felix.gogo.shell", "[0,1)"); requires.add(capReq.buildSyntheticRequirement()); model.setRunRequires(requires); BndrunResolveContext context = new BndrunResolveContext(model, registry, log); Resolver resolver = new BndResolver(new ResolverLogger(4)); try { Map<Resource,List<Wire>> resolved = resolver.resolve(context); Set<Resource> resources = resolved.keySet(); Resource resource = getResource(resources, "org.apache.felix.gogo.runtime", "0.10"); assertNotNull(resource); } catch (ResolutionException e) { fail("Resolve failed"); } } /** * Check if we can resolve against capabilities defined on the -provided */ // public static void testResolveWithProfile() throws Exception { // Resolver resolver = new BndResolver(new ResolverLogger(4)); // MockRegistry registry = new MockRegistry(); // registry.addPlugin(createRepo(IO.getFile("testdata/repo3.index.xml"))); // BndEditModel model = new BndEditModel(); // // // // // Provided capabilities // // // // model.setRunFw("org.apache.felix.framework"); // model.setGenericString(Constants.DISTRO, // "testdata/repo1/org.apache.felix.gogo.runtime-0.10.0.jar;version=file"); // // // // // We require gogo, but now the gogo runtime is on the runpath // // so should be excluded // // // // Requirement erq = // CapReqBuilder.createPackageRequirement("org.apache.felix.gogo.api", // "0.10.0") // .buildSyntheticRequirement(); // // model.setRunRequires(Arrays.asList(erq)); // // BndrunResolveContext context = new BndrunResolveContext(model, registry, // log); // context.setLevel(4); // context.init(); // // Map<Resource,List<Wire>> resolved = resolver.resolve(context); // List<Wire> wires = resolved.get(context.getInputResource()); // assertNotNull(wires); // boolean found = false; // for ( Wire wire : wires ) { // if (equals(wire.getRequirement(), erq)) { // found = true; // assertTrue(wire.getProvider().equals(context.getSystemResource())); // } // } // assertTrue("System resource not found for wire", found); // // Set<Resource> resources = resolved.keySet(); // Resource resource = getResource(resources, // "org.apache.felix.gogo.runtime", "0.10"); // assertNull(resource); // // } // private static boolean equals(Requirement a, Requirement b) { // if ( a== b) // return true; // // if ( a == null || b == null) // return false; // // if ( a.equals(b)) // return true; // // if ( !a.getNamespace().equals(b.getNamespace())) // return false; // // return a.getDirectives().equals(b.getDirectives()) && // a.getAttributes().equals(b.getAttributes()); // } private static Resource getResource(Set<Resource> resources, String bsn, String versionString) { for (Resource resource : resources) { List<Capability> identities = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE); if (identities != null && identities.size() == 1) { Capability idCap = identities.get(0); Object id = idCap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE); Object version = idCap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE); if (bsn.equals(id)) { if (versionString == null) { return resource; } Version requested = Version.parseVersion(versionString); Version current; if (version instanceof Version) { current = (Version) version; } else { current = Version.parseVersion((String) version); } if (requested.equals(current)) { return resource; } } } } return null; } /** * Simple test that resolves a requirement * * @throws ResolutionException */ public void testMultipleOptionsNotDuplicated() throws ResolutionException { // Resolve against repo 5 MockRegistry registry = new MockRegistry(); registry.addPlugin(createRepo(IO.getFile("testdata/repo5/index.xml"), "Test-5")); // Set up a simple Java 7 Felix requirement as per Issue #971 BndEditModel runModel = new BndEditModel(); runModel.setRunFw("org.apache.felix.framework;version='4.2.1'"); runModel.setEE(EE.JavaSE_1_7); runModel.setSystemPackages(Collections.singletonList(new ExportedPackage("org.w3c.dom.traversal", null))); runModel.setGenericString("-resolve.effective", "active"); // Require the log service, GoGo shell and GoGo commands List<Requirement> requirements = new ArrayList<Requirement>(); requirements.add(new CapReqBuilder("osgi.identity") .addDirective("filter", "(osgi.identity=org.apache.felix.log)").buildSyntheticRequirement()); requirements.add(new CapReqBuilder("osgi.identity") .addDirective("filter", "(osgi.identity=org.apache.felix.gogo.shell)").buildSyntheticRequirement()); requirements.add(new CapReqBuilder("osgi.identity") .addDirective("filter", "(osgi.identity=org.apache.felix.gogo.command)").buildSyntheticRequirement()); runModel.setRunRequires(requirements); // Resolve the bndrun BndrunResolveContext context = new BndrunResolveContext(runModel, registry, log); Resolver resolver = new BndResolver(new org.apache.felix.resolver.Logger(4)); Collection<Resource> resolvedResources = new ResolveProcess() .resolveRequired(runModel, registry, resolver, Collections.<ResolutionCallback> emptyList(), log) .keySet(); Map<String,Resource> mandatoryResourcesBySymbolicName = new HashMap<String,Resource>(); for (Resource r : resolvedResources) { Capability cap = r.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0); // We shouldn't have more than one match for each symbolic name for // this resolve String symbolicName = (String) cap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE); assertNull("Multiple results for " + symbolicName, mandatoryResourcesBySymbolicName.put(symbolicName, r)); } assertEquals(4, resolvedResources.size()); } }