| author | Thomas Watson | 2011-03-15 09:56:03 (EDT) |
|---|---|---|
| committer | Glyn Normington | 2011-03-15 09:56:03 (EDT) |
| commit | 24427705786b14ab37eedffaa6fe3afbfd926c8c (patch) (side-by-side diff) | |
| tree | 84c61aadfffcb7a2d5e47af40fb4910002be75ab | |
| parent | 70224176c5354138cb62bbbf00b5a16a6c595971 (diff) | |
| download | org.eclipse.virgo.kernel-24427705786b14ab37eedffaa6fe3afbfd926c8c.zip org.eclipse.virgo.kernel-24427705786b14ab37eedffaa6fe3afbfd926c8c.tar.gz org.eclipse.virgo.kernel-24427705786b14ab37eedffaa6fe3afbfd926c8c.tar.bz2 | |
Add RegionDigraphPersistence to save and load RegionDigraphs
5 files changed, 444 insertions, 1 deletions
diff --git a/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraph.java b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraph.java index 77ccc19..3bb7c26 100644 --- a/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraph.java +++ b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraph.java @@ -140,7 +140,7 @@ public interface RegionDigraph extends Iterable<Region> { * @return a {@link Set} of {@link FilteredRegion FilteredRegions} of head regions and region filters */ Set<FilteredRegion> getEdges(Region tailRegion); - + /** * Visit the subgraph connected to the given region. * @@ -149,4 +149,11 @@ public interface RegionDigraph extends Iterable<Region> { */ void visitSubgraph(Region startingRegion, RegionDigraphVisitor visitor); + /** + * Gets a {@link RegionDigraphPersistence} object which can be used to save and load a {@link RegionDigraph} to and + * from persistent storage. + * + * @return a {@link RegionDigraphPersistence} object. + */ + RegionDigraphPersistence getRegionDigraphPersistence(); } diff --git a/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraphPersistence.java b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraphPersistence.java new file mode 100644 index 0000000..6ff9934 --- a/dev/null +++ b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraphPersistence.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.virgo.kernel.osgi.region; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A region digraph persistence is used to persist the state of a {@link RegionDigraph}. + */ +public interface RegionDigraphPersistence { + + /** + * Creates a new digraph and reads the content of the digraph from the provided input. The provided input must have + * been persisted using the {@link #load(InputStream)} method. + * <p /> + * The specified stream remains open after this method returns. + * + * @param input + * @return + * @throws IOException if error occurs reading the digraph. + */ + RegionDigraph load(InputStream input) throws IOException; + + /** + * Writes the specified {@link RegionDigraph} to the provided output in a formate suitable for using the + * {@link #load(InputStream)} method. + * + * @param output + * @throws IOException if error occurs writing the digraph. + */ + void save(RegionDigraph digraph, OutputStream output) throws IOException; +} diff --git a/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraph.java b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraph.java index 92adc27..2906f8f 100644 --- a/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraph.java +++ b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraph.java @@ -22,6 +22,7 @@ import java.util.Set; import org.eclipse.virgo.kernel.osgi.region.Region; import org.eclipse.virgo.kernel.osgi.region.RegionDigraph; +import org.eclipse.virgo.kernel.osgi.region.RegionDigraphPersistence; import org.eclipse.virgo.kernel.osgi.region.RegionDigraphVisitor; import org.eclipse.virgo.kernel.osgi.region.RegionFilter; import org.eclipse.virgo.kernel.osgi.region.RegionFilterBuilder; @@ -326,4 +327,9 @@ public final class StandardRegionDigraph implements RegionDigraph { } return result; } + + @Override + public RegionDigraphPersistence getRegionDigraphPersistence() { + return new StandardRegionDigraphPersistence(systemBundleContext, threadLocal); + } } diff --git a/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPersistence.java b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPersistence.java new file mode 100644 index 0000000..85355b6 --- a/dev/null +++ b/org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPersistence.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.virgo.kernel.osgi.region.internal; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.eclipse.virgo.kernel.osgi.region.Region; +import org.eclipse.virgo.kernel.osgi.region.RegionDigraph; +import org.eclipse.virgo.kernel.osgi.region.RegionDigraph.FilteredRegion; +import org.eclipse.virgo.kernel.osgi.region.RegionDigraphPersistence; +import org.eclipse.virgo.kernel.osgi.region.RegionFilter; +import org.eclipse.virgo.kernel.osgi.region.RegionFilterBuilder; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.InvalidSyntaxException; + +/** + * + * Class used for reading and writing a region digraph to persistent storage. + * <p /> + * + * <strong>Concurrent Semantics</strong><br /> + * Thread safe. + */ +final class StandardRegionDigraphPersistence implements RegionDigraphPersistence { + + static void writeRegionDigraph(DataOutputStream out, RegionDigraph digraph) throws IOException { + if (!(digraph instanceof StandardRegionDigraph)) + throw new IllegalArgumentException("Only digraphs of type '" + StandardRegionDigraph.class.getName() + "' are allowed: " + + digraph.getClass().getName()); + Map<Region, Set<FilteredRegion>> filteredRegions = ((StandardRegionDigraph) digraph).getFilteredRegions(); + + boolean exception = false; + try { + // write the number of regions + out.writeInt(filteredRegions.size()); + // write each region + for (Region region : filteredRegions.keySet()) { + writeRegion(out, region); + } + // write each edge + // write number of tail regions + out.writeInt(filteredRegions.size()); + for (Map.Entry<Region, Set<FilteredRegion>> edges : filteredRegions.entrySet()) { + // write the number of edges for this tail + out.writeInt(edges.getValue().size()); + for (FilteredRegion edge : edges.getValue()) { + writeEdge(out, edges.getKey(), edge.getFilter(), edge.getRegion()); + } + } + } catch (IOException e) { + exception = true; + throw e; + } finally { + try { + out.close(); + } catch (IOException e) { + if (!exception) + throw e; + } + } + } + + private static void writeRegion(DataOutputStream out, Region region) throws IOException { + // write region name + out.writeUTF(region.getName()); + + Set<Long> ids = region.getBundleIds(); + // write number of bundles + out.writeInt(ids.size()); + for (Long id : ids) { + // write each bundle id + out.writeLong(id); + } + } + + private static void writeEdge(DataOutputStream out, Region tail, RegionFilter filter, Region head) throws IOException { + // write tail region name + out.writeUTF(tail.getName()); + // write head region name + out.writeUTF(head.getName()); + // save the sharing policy + Map<String, Collection<String>> policy = filter.getSharingPolicy(); + // write the number of name spaces + out.writeInt(policy.size()); + // write each name space policy + for (Map.Entry<String, Collection<String>> namespace : policy.entrySet()) { + // write the name space name + out.writeUTF(namespace.getKey()); + Collection<String> filters = namespace.getValue(); + // write the number of filters + out.writeInt(filters.size()); + for (String filterSpec : filters) { + // write each filter + out.writeUTF(filterSpec); + } + } + } + + static RegionDigraph readRegionDigraph(DataInputStream in, BundleContext systemBundleContext, ThreadLocal<Region> threadLocal) + throws IOException, InvalidSyntaxException, BundleException { + RegionDigraph digraph = new StandardRegionDigraph(systemBundleContext, threadLocal); + boolean exception = false; + try { + // read the number of regions + int numRegions = in.readInt(); + for (int i = 0; i < numRegions; i++) { + readRegion(in, digraph); + } + // read each edge + // read number of tail regions + int numTails = in.readInt(); + for (int i = 0; i < numTails; i++) { + // read the number of edges for this tail + int numEdges = in.readInt(); + for (int j = 0; j < numEdges; j++) { + readEdge(in, digraph); + } + } + } catch (IOException e) { + exception = true; + throw e; + } catch (InvalidSyntaxException e) { + exception = true; + throw e; + } catch (BundleException e) { + exception = true; + throw e; + } finally { + try { + in.close(); + } catch (IOException e) { + if (!exception) + throw e; + } + } + return digraph; + } + + private static Region readRegion(DataInputStream in, RegionDigraph digraph) throws IOException, BundleException { + // read region name + String name = in.readUTF(); + Region region = digraph.createRegion(name); + + // read number of bundles + int numIds = in.readInt(); + for (int i = 0; i < numIds; i++) { + region.addBundle(in.readLong()); + } + return region; + } + + private static void readEdge(DataInputStream in, RegionDigraph digraph) throws IOException, InvalidSyntaxException, BundleException { + // read tail region name + Region tail = digraph.getRegion(in.readUTF()); + // read head region name + Region head = digraph.getRegion(in.readUTF()); + // read the sharing policy + RegionFilterBuilder builder = digraph.createRegionFilterBuilder(); + // read the number of name spaces + int numSpaces = in.readInt(); + // read each name space policy + for (int i = 0; i < numSpaces; i++) { + // read the name space name + String namespace = in.readUTF(); + // read the number of filters + int numFilters = in.readInt(); + for (int j = 0; j < numFilters; j++) { + String filter = in.readUTF(); + builder.allow(namespace, filter); + } + } + digraph.connect(tail, builder.build(), head); + } + + private final BundleContext context; + + private final ThreadLocal<Region> threadLocal; + + public StandardRegionDigraphPersistence(BundleContext context, ThreadLocal<Region> threadLocal) { + this.context = context; + this.threadLocal = threadLocal; + } + + @Override + public RegionDigraph load(InputStream input) throws IOException { + try { + return readRegionDigraph(new DataInputStream(input), context, threadLocal); + } catch (InvalidSyntaxException e) { + // This should never happen since the filters were valid on save + // propagate as IllegalStateException + throw new IllegalStateException("Internal error reading a filter", e); + } catch (BundleException e) { + // This should never happen since the digraph was valid on save + // propagate as IO ex + throw new IllegalStateException("Internal error creating the digraph", e); + } + } + + @Override + public void save(RegionDigraph digraph, OutputStream output) throws IOException { + writeRegionDigraph(new DataOutputStream(output), digraph); + } +} diff --git a/org.eclipse.virgo.kernel.osgi/src/test/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPeristenceTests.java b/org.eclipse.virgo.kernel.osgi/src/test/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPeristenceTests.java new file mode 100644 index 0000000..459a709 --- a/dev/null +++ b/org.eclipse.virgo.kernel.osgi/src/test/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPeristenceTests.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * This file is part of the Virgo Web Server. + * + * Copyright (c) 2011 VMware Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SpringSource, a division of VMware - initial API and implementation and/or initial documentation + *******************************************************************************/ + +package org.eclipse.virgo.kernel.osgi.region.internal; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.virgo.kernel.osgi.region.Region; +import org.eclipse.virgo.kernel.osgi.region.RegionDigraph; +import org.eclipse.virgo.kernel.osgi.region.RegionDigraph.FilteredRegion; +import org.eclipse.virgo.kernel.osgi.region.RegionFilter; +import org.eclipse.virgo.kernel.osgi.region.RegionFilterBuilder; +import org.eclipse.virgo.teststubs.osgi.framework.StubBundle; +import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.Version; + +import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream; + +public class StandardRegionDigraphPeristenceTests { + + private RegionDigraph digraph; + + private StubBundleContext systemBundleContext; + + private ThreadLocal<Region> threadLocal; + + private static final String BOOT_REGION = "boot"; + + private static final Collection<String> regionNames = Arrays.asList("r0", "r1", "r2", "r3"); + + private static final Map<String, Set<Bundle>> regionBundles = new HashMap<String, Set<Bundle>>(); + + @Before + public void setUp() throws Exception { + long nextId = 0; + StubBundle stubSystemBundle = new StubBundle(nextId++, "osgi.framework", new Version("0"), "loc"); + systemBundleContext = new StubBundleContext(); + systemBundleContext.addInstalledBundle(stubSystemBundle); + threadLocal = new ThreadLocal<Region>(); + this.digraph = new StandardRegionDigraph(systemBundleContext, threadLocal); + Region boot = digraph.createRegion(BOOT_REGION); + boot.addBundle(stubSystemBundle); + + for (String regionName : regionNames) { + Region region = digraph.createRegion(regionName); + for (int i = 0; i < 10; i++) { + String bsn = region.getName() + "." + i; + region.addBundle(new StubBundle(nextId++, bsn, new Version("0"), bsn)); + } + } + } + + @After + public void tearDown() throws Exception { + + } + + @Test + public void testBasic() throws IOException, InvalidSyntaxException, BundleException { + doTest(); + } + + @Test + public void testConnection() throws InvalidSyntaxException, BundleException, IOException { + Region tail = null; + // create a connection between each region + for (Region head : digraph) { + if (tail != null) { + String name = head.getName(); + tail.connectRegion(head, createFilter(name + "A", name + "B", name + "C")); + } + tail = head; + } + doTest(); + } + + private void doTest() throws IOException, InvalidSyntaxException, BundleException { + ByteOutputStream memOut = new ByteOutputStream(); + StandardRegionDigraphPersistence.writeRegionDigraph(new DataOutputStream(memOut), digraph); + + ByteArrayInputStream memIn = new ByteArrayInputStream(memOut.getBytes()); + RegionDigraph copy = StandardRegionDigraphPersistence.readRegionDigraph(new DataInputStream(memIn), systemBundleContext, threadLocal); + assertEquals(digraph, copy); + } + + private RegionFilter createFilter(String... input) throws InvalidSyntaxException { + RegionFilterBuilder builder = digraph.createRegionFilterBuilder(); + for (String param : input) { + builder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(bundle-symbolic-name=" + param + ")"); + builder.allow(RegionFilter.VISIBLE_HOST_NAMESPACE, "(" + RegionFilter.VISIBLE_HOST_NAMESPACE + "=" + param + ")"); + builder.allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=" + param + ")"); + builder.allow(RegionFilter.VISIBLE_REQUIRE_NAMESPACE, "(" + RegionFilter.VISIBLE_REQUIRE_NAMESPACE + "=" + param + ")"); + builder.allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(" + Constants.OBJECTCLASS + "=" + param + ")"); + } + return builder.build(); + } + + static void assertEquals(RegionDigraph d1, RegionDigraph d2) { + int rCnt1 = countRegions(d1); + int rCnt2 = countRegions(d2); + Assert.assertEquals(rCnt1, rCnt2); + for (Region r1 : d1) { + Region r2 = d2.getRegion(r1.getName()); + assertEquals(r1, r2); + } + } + + static int countRegions(RegionDigraph digraph) { + int result = 0; + for (Region region : digraph) { + result++; + } + return result; + } + + static void assertEquals(Region r1, Region r2) { + Assert.assertNotNull(r1); + Assert.assertNotNull(r2); + Assert.assertEquals("Wrong name", r1.getName(), r2.getName()); + Set<Long> r1IDs = r1.getBundleIds(); + Set<Long> r2IDs = r2.getBundleIds(); + Assert.assertEquals(r1IDs.size(), r2IDs.size()); + for (Long id : r1IDs) { + Assert.assertTrue("Missing id: " + id, r2IDs.contains(id)); + } + assertEquals(r1.getEdges(), r2.getEdges()); + } + + static void assertEquals(Set<FilteredRegion> edges1, Set<FilteredRegion> edges2) { + Assert.assertEquals(edges1.size(), edges2.size()); + Map<String, RegionFilter> edges2Map = new HashMap<String, RegionFilter>(); + for (FilteredRegion edge2 : edges2) { + edges2Map.put(edge2.getRegion().getName(), edge2.getFilter()); + } + for (FilteredRegion edge1 : edges1) { + RegionFilter filter2 = edges2Map.get(edge1.getRegion().getName()); + Assert.assertNotNull("No filter found: " + edge1.getRegion().getName(), filter2); + Assert.assertEquals(edge1.getFilter().getSharingPolicy(), filter2.getSharingPolicy()); + } + } +} |

