| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.felix.resolver.test; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.felix.resolver.Logger; |
| import org.apache.felix.resolver.ResolverImpl; |
| import org.apache.felix.resolver.test.util.CandidateComparator; |
| import org.apache.felix.resolver.test.util.CapabilitySet; |
| import org.apache.felix.resolver.test.util.ClauseParser; |
| import org.apache.felix.resolver.test.util.GenericCapability; |
| import org.apache.felix.resolver.test.util.GenericRequirement; |
| import org.apache.felix.resolver.test.util.IterativeResolver; |
| import org.apache.felix.resolver.test.util.JsonReader; |
| import org.apache.felix.resolver.test.util.ResourceImpl; |
| import org.apache.felix.resolver.test.util.SimpleFilter; |
| import org.apache.felix.utils.version.VersionRange; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Version; |
| import org.osgi.resource.Capability; |
| import org.osgi.resource.Requirement; |
| import org.osgi.resource.Resource; |
| import org.osgi.resource.Wire; |
| import org.osgi.resource.Wiring; |
| import org.osgi.service.resolver.HostedCapability; |
| import org.osgi.service.resolver.ResolveContext; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| public class BigResolutionTest { |
| |
| @Test |
| @Ignore |
| public void testResolutionSpeed() throws Exception { |
| ResolverImpl resolver = new ResolverImpl(new Logger(Logger.LOG_INFO)); |
| |
| ResolveContext rc = buildResolutionContext(); |
| |
| System.out.println("Warming up..."); |
| Map<Resource, List<Wire>> wires = resolver.resolve(rc); |
| resolver.resolve(rc); |
| |
| System.out.println("Running..."); |
| RunningStat stats = new RunningStat(); |
| for (int i = 1; i <= 100; i++) { |
| System.gc(); |
| Thread.sleep(100); |
| System.gc(); |
| Thread.sleep(100); |
| long t0 = System.nanoTime(); |
| Map<Resource, List<Wire>> newWires = resolver.resolve(rc); |
| long t1 = System.nanoTime(); |
| double dt = (t1 - t0) * 1E-6; |
| System.out.println("Resolver took " + String.format("%7.2f", dt) + " ms"); |
| stats.put(dt); |
| assertEquals(wires, newWires); |
| |
| if (i % 10 == 0) { |
| System.out.println(); |
| System.out.println("Summary"); |
| System.out.println(" Min: " + String.format("%7.2f", stats.getMin()) + " ms"); |
| System.out.println(" Max: " + String.format("%7.2f", stats.getMax()) + " ms"); |
| System.out.println(" Avg: " + String.format("%7.2f", stats.getAverage()) + " ms"); |
| System.out.println(" StdDev: " + String.format("%7" + |
| ".2f", stats.getStdDev() / stats.getAverage() * 100.0) + " %"); |
| System.out.println(); |
| stats = new RunningStat(); |
| } |
| } |
| |
| } |
| |
| @Test |
| @Ignore |
| public void testIterativeResolution() throws Exception { |
| ResolveContext rc = buildResolutionContext(); |
| |
| ResolverImpl resolver = new ResolverImpl(new Logger(Logger.LOG_INFO)); |
| |
| long t0 = System.currentTimeMillis(); |
| Map<Resource, List<Wire>> wiring1 = resolver.resolve(rc); |
| long t1 = System.currentTimeMillis(); |
| System.out.println("Resolver took " + (t1 - t0) + " ms"); |
| |
| long t2 = System.currentTimeMillis(); |
| Map<Resource, List<Wire>> wiring2 = new IterativeResolver(resolver).resolve(rc); |
| long t3 = System.currentTimeMillis(); |
| System.out.println("Iterative resolver took " + (t3 - t2) + " ms"); |
| |
| checkResolutions(wiring1, wiring2); |
| } |
| |
| private ResolveContext buildResolutionContext() throws IOException, BundleException { |
| Object resolution; |
| |
| InputStream is = getClass().getClassLoader().getResourceAsStream("resolution.json"); |
| try { |
| resolution = JsonReader.read(is); |
| } finally { |
| is.close(); |
| } |
| |
| List<Resource> resources = new ArrayList<Resource>(); |
| ResourceImpl system = new ResourceImpl("system-bundle"); |
| parseCapability(system, "osgi.ee; osgi.ee=JavaSE; version=1.5"); |
| parseCapability(system, "osgi.ee; osgi.ee=JavaSE; version=1.6"); |
| parseCapability(system, "osgi.ee; osgi.ee=JavaSE; version=1.7"); |
| resources.add(system); |
| for (Object r : (Collection) ((Map) resolution).get("resources")) { |
| resources.add(parseResource(r)); |
| } |
| final List<Resource> mandatory = new ArrayList<Resource>(); |
| for (Object r : (Collection) ((Map) resolution).get("mandatory")) { |
| mandatory.add(parseResource(r)); |
| } |
| |
| final Map<String, CapabilitySet> capSets = new HashMap<String, CapabilitySet>(); |
| CapabilitySet svcSet = new CapabilitySet(Collections.singletonList("objectClass")); |
| capSets.put("osgi.service", svcSet); |
| for (Resource resource : resources) { |
| for (Capability cap : resource.getCapabilities(null)) { |
| String ns = cap.getNamespace(); |
| CapabilitySet set = capSets.get(ns); |
| if (set == null) { |
| set = new CapabilitySet(Collections.singletonList(ns)); |
| capSets.put(ns, set); |
| } |
| set.addCapability(cap); |
| } |
| } |
| |
| return new ResolveContext() { |
| @Override |
| public Collection<Resource> getMandatoryResources() { |
| return mandatory; |
| } |
| |
| @Override |
| public List<Capability> findProviders(Requirement requirement) { |
| SimpleFilter sf; |
| if (requirement.getDirectives().containsKey("filter")) { |
| sf = SimpleFilter.parse(requirement.getDirectives().get("filter")); |
| } else { |
| sf = SimpleFilter.convert(requirement.getAttributes()); |
| } |
| CapabilitySet set = capSets.get(requirement.getNamespace()); |
| List<Capability> caps = new ArrayList<Capability>(set.match(sf, true)); |
| Collections.sort(caps, new CandidateComparator()); |
| return caps; |
| } |
| |
| @Override |
| public int insertHostedCapability(List<Capability> capabilities, HostedCapability hostedCapability) { |
| capabilities.add(hostedCapability); |
| return capabilities.size() - 1; |
| } |
| |
| @Override |
| public boolean isEffective(Requirement requirement) { |
| return true; |
| } |
| |
| @Override |
| public Map<Resource, Wiring> getWirings() { |
| return Collections.emptyMap(); |
| } |
| }; |
| } |
| |
| private void checkResolutions(Map<Resource, List<Wire>> wireMap1, Map<Resource, List<Wire>> wireMap2) { |
| Set<Resource> resources; |
| resources = new HashSet<Resource>(wireMap1.keySet()); |
| resources.removeAll(wireMap2.keySet()); |
| for (Resource res : resources) { |
| System.out.println("Resource resolved in r1 and not in r2: " + res); |
| } |
| resources = new HashSet<Resource>(wireMap2.keySet()); |
| resources.removeAll(wireMap1.keySet()); |
| for (Resource res : resources) { |
| System.out.println("Resource resolved in r2 and not in r1: " + res); |
| } |
| resources = new HashSet<Resource>(wireMap2.keySet()); |
| resources.retainAll(wireMap1.keySet()); |
| for (Resource resource : resources) { |
| Set<Wire> wires1 = new HashSet<Wire>(wireMap1.get(resource)); |
| Set<Wire> wires2 = new HashSet<Wire>(wireMap2.get(resource)); |
| wires1.removeAll(wireMap2.get(resource)); |
| wires2.removeAll(wireMap1.get(resource)); |
| if (!wires1.isEmpty() || !wires2.isEmpty()) { |
| System.out.println("Different wiring for resource: " + resource); |
| } |
| for (Wire wire : wires1) { |
| System.out.println("\tR1: " + wire); |
| } |
| for (Wire wire : wires2) { |
| System.out.println("\tR2: " + wire); |
| } |
| } |
| if (!wireMap1.equals(wireMap2)) { |
| throw new RuntimeException("Different wiring"); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static Resource parseResource(Object resource) throws BundleException { |
| ResourceImpl res = new ResourceImpl(); |
| Collection<String> caps = (Collection<String>) ((Map) resource).get("capabilities"); |
| if (caps != null) { |
| for (String s : caps) { |
| parseCapability(res, s); |
| } |
| } |
| Collection<String> reqs = (Collection<String>) ((Map) resource).get("requirements"); |
| if (reqs != null) { |
| for (String s : reqs) { |
| parseRequirement(res, s); |
| } |
| } |
| return res; |
| } |
| |
| private static void parseRequirement(ResourceImpl res, String s) throws BundleException { |
| List<ClauseParser.ParsedHeaderClause> clauses = ClauseParser.parseStandardHeader(s); |
| normalizeRequirementClauses(clauses); |
| for (ClauseParser.ParsedHeaderClause clause : clauses) { |
| for (String path : clause.paths) { |
| GenericRequirement requirement = new GenericRequirement(res, path); |
| for (Map.Entry<String, String> dir : clause.dirs.entrySet()) { |
| requirement.addDirective(dir.getKey(), dir.getValue()); |
| } |
| for (Map.Entry<String, Object> attr : clause.attrs.entrySet()) { |
| requirement.addAttribute(attr.getKey(), attr.getValue()); |
| } |
| res.addRequirement(requirement); |
| } |
| } |
| } |
| |
| private static void parseCapability(ResourceImpl res, String s) throws BundleException { |
| List<ClauseParser.ParsedHeaderClause> clauses = ClauseParser.parseStandardHeader(s); |
| normalizeCapabilityClauses(clauses); |
| for (ClauseParser.ParsedHeaderClause clause : clauses) { |
| for (String path : clause.paths) { |
| GenericCapability capability = new GenericCapability(res, path); |
| for (Map.Entry<String, String> dir : clause.dirs.entrySet()) { |
| capability.addDirective(dir.getKey(), dir.getValue()); |
| } |
| for (Map.Entry<String, Object> attr : clause.attrs.entrySet()) { |
| capability.addAttribute(attr.getKey(), attr.getValue()); |
| } |
| res.addCapability(capability); |
| } |
| } |
| } |
| |
| private static void normalizeRequirementClauses( |
| List<ClauseParser.ParsedHeaderClause> clauses) |
| throws BundleException { |
| |
| // Convert attributes into specified types. |
| for (ClauseParser.ParsedHeaderClause clause : clauses) |
| { |
| for (Map.Entry<String, Object> entry : clause.attrs.entrySet()) |
| { |
| String key = entry.getKey(); |
| Object val = entry.getValue(); |
| String type = clause.types.get(key); |
| if ("Version".equals(type) || "version".equals(key)) |
| { |
| clause.attrs.put( |
| key, |
| VersionRange.parseVersionRange(val.toString().trim())); |
| } |
| } |
| } |
| } |
| |
| private static void normalizeCapabilityClauses( |
| List<ClauseParser.ParsedHeaderClause> clauses) |
| throws BundleException |
| { |
| // Convert attributes into specified types. |
| for (ClauseParser.ParsedHeaderClause clause : clauses) |
| { |
| for (Map.Entry<String, String> entry : clause.types.entrySet()) |
| { |
| String type = entry.getValue(); |
| if (!type.equals("String")) |
| { |
| if (type.equals("Double")) |
| { |
| clause.attrs.put( |
| entry.getKey(), |
| new Double(clause.attrs.get(entry.getKey()).toString().trim())); |
| } |
| else if (type.equals("Version")) |
| { |
| clause.attrs.put( |
| entry.getKey(), |
| new Version(clause.attrs.get(entry.getKey()).toString().trim())); |
| } |
| else if (type.equals("Long")) |
| { |
| clause.attrs.put( |
| entry.getKey(), |
| new Long(clause.attrs.get(entry.getKey()).toString().trim())); |
| } |
| else if (type.startsWith("List")) |
| { |
| int startIdx = type.indexOf('<'); |
| int endIdx = type.indexOf('>'); |
| if (((startIdx > 0) && (endIdx <= startIdx)) |
| || ((startIdx < 0) && (endIdx > 0))) |
| { |
| throw new BundleException( |
| "Invalid Provide-Capability attribute list type for '" |
| + entry.getKey() |
| + "' : " |
| + type); |
| } |
| |
| String listType = "String"; |
| if (endIdx > startIdx) |
| { |
| listType = type.substring(startIdx + 1, endIdx).trim(); |
| } |
| |
| List<String> tokens = ClauseParser.parseDelimitedString( |
| clause.attrs.get(entry.getKey()).toString(), ",", false); |
| List<Object> values = new ArrayList<Object>(tokens.size()); |
| for (String token : tokens) |
| { |
| if (listType.equals("String")) |
| { |
| values.add(token); |
| } |
| else if (listType.equals("Double")) |
| { |
| values.add(new Double(token.trim())); |
| } |
| else if (listType.equals("Version")) |
| { |
| values.add(new Version(token.trim())); |
| } |
| else if (listType.equals("Long")) |
| { |
| values.add(new Long(token.trim())); |
| } |
| else |
| { |
| throw new BundleException( |
| "Unknown Provide-Capability attribute list type for '" |
| + entry.getKey() |
| + "' : " |
| + type); |
| } |
| } |
| clause.attrs.put( |
| entry.getKey(), |
| values); |
| } |
| else |
| { |
| throw new BundleException( |
| "Unknown Provide-Capability attribute type for '" |
| + entry.getKey() |
| + "' : " |
| + type); |
| } |
| } |
| } |
| } |
| } |
| |
| public static class RunningStat { |
| |
| private int count = 0; |
| private double min = Double.MAX_VALUE; |
| private double max = 0.0; |
| private double average = 0.0; |
| private double pwrSumAvg = 0.0; |
| private double stdDev = 0.0; |
| |
| /** |
| * Incoming new values used to calculate the running statistics |
| * |
| * @param value the new value |
| */ |
| public void put(double value) { |
| |
| count++; |
| average += (value - average) / count; |
| pwrSumAvg += (value * value - pwrSumAvg) / count; |
| stdDev = Math.sqrt((pwrSumAvg * count - count * average * average) / (count - 1)); |
| min = Math.min(min, value); |
| max = Math.max(max, value); |
| |
| } |
| |
| public double getMin() { |
| return min; |
| } |
| |
| public double getMax() { |
| return max; |
| } |
| |
| public double getAverage() { |
| |
| return average; |
| } |
| |
| public double getStdDev() { |
| |
| return Double.isNaN(stdDev) ? 0.0 : stdDev; |
| } |
| |
| } |
| } |