blob: 96ec2dff67ede1afaa92cca4124c82c2d529bd3c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2013 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.equinox.internal.region;
import java.io.*;
import java.util.*;
import org.eclipse.equinox.region.*;
import org.eclipse.equinox.region.RegionDigraph.FilteredRegion;
import org.osgi.framework.*;
/**
*
* Class used for reading and writing a region digraph to persistent storage.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
* Thread safe.
*/
public final class StandardRegionDigraphPersistence implements RegionDigraphPersistence {
private static final String PERSISTENT_NAME = "equinox region digraph"; //$NON-NLS-1$
private static final int PERSISTENT_VERSION = 1;
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()); //$NON-NLS-1$ //$NON-NLS-2$
Map<Region, Set<FilteredRegion>> filteredRegions = ((StandardRegionDigraph) digraph).getFilteredRegions();
try {
// write the persistent name and version
out.writeUTF(PERSISTENT_NAME);
out.writeInt(PERSISTENT_VERSION);
// 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());
}
}
} finally {
// note that the output is flushed even on exception
out.flush();
}
}
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 StandardRegionDigraph readRegionDigraph(DataInputStream in, BundleContext bundleContext, ThreadLocal<Region> threadLocal) throws IOException, InvalidSyntaxException, BundleException {
StandardRegionDigraph digraph = new StandardRegionDigraph(bundleContext, threadLocal);
// Read and check the persistent name and version
String persistentName = in.readUTF();
if (!PERSISTENT_NAME.equals(persistentName)) {
throw new IllegalArgumentException("Input stream does not represent a digraph"); //$NON-NLS-1$
}
int persistentVersion = in.readInt();
if (PERSISTENT_VERSION != persistentVersion) {
throw new IllegalArgumentException("Input stream contains a digraph with an incompatible version '" + persistentVersion + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
// read the number of regions
int numRegions = in.readInt();
for (int i = 0; i < numRegions; i++) {
readRegion(in, digraph, bundleContext);
}
// 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);
}
}
return digraph;
}
private static Region readRegion(DataInputStream in, RegionDigraph digraph, BundleContext context) 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++) {
long id = in.readLong();
if (context == null || context.getBundle(id) != null)
region.addBundle(id);
}
return region;
}
private static void readEdge(DataInputStream in, RegionDigraph digraph) throws IOException, InvalidSyntaxException, BundleException {
// read tail region name
String tailName = in.readUTF();
Region tail = digraph.getRegion(tailName);
if (tail == null)
throw new IOException("Could not find tail region: " + tailName); //$NON-NLS-1$
// read head region name
String headName = in.readUTF();
Region head = digraph.getRegion(headName);
if (head == null)
throw new IOException("Could not find head region: " + headName); //$NON-NLS-1$
// 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);
}
/**
* {@inheritDoc}
*/
public RegionDigraph load(InputStream input) throws IOException {
try {
return readRegionDigraph(new DataInputStream(input), null, null);
} 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); //$NON-NLS-1$
} catch (BundleException e) {
// This should never happen since the digraph was valid on save
// propagate as IllegalStateException
throw new IllegalStateException("Internal error creating the digraph", e); //$NON-NLS-1$
}
}
/**
* {@inheritDoc}
*/
public void save(RegionDigraph digraph, OutputStream output) throws IOException {
writeRegionDigraph(new DataOutputStream(output), digraph);
}
}