summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2011-03-15 09:56:03 (EDT)
committer Glyn Normington2011-03-15 09:56:03 (EDT)
commit24427705786b14ab37eedffaa6fe3afbfd926c8c (patch)
tree84c61aadfffcb7a2d5e47af40fb4910002be75ab
parent70224176c5354138cb62bbbf00b5a16a6c595971 (diff)
downloadorg.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
-rw-r--r--org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraph.java9
-rw-r--r--org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/RegionDigraphPersistence.java43
-rw-r--r--org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraph.java6
-rw-r--r--org.eclipse.virgo.kernel.osgi/src/main/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPersistence.java220
-rw-r--r--org.eclipse.virgo.kernel.osgi/src/test/java/org/eclipse/virgo/kernel/osgi/region/internal/StandardRegionDigraphPeristenceTests.java167
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
--- /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
--- /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
--- /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());
+ }
+ }
+}