/*******************************************************************************
* Copyright (c) 2013 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
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Jan S. Rellermeyer, IBM Research - initial API and implementation
*******************************************************************************/
package org.eclipse.concierge;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.concierge.BundleImpl.Revision;
import org.eclipse.concierge.ConciergeCollections.MultiMap;
import org.eclipse.concierge.ConciergeCollections.ParseResult;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.Version;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
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;
public class Resources {
static Wire createWire(final Capability cap, final Requirement req) {
return cap instanceof BundleCapability
&& req instanceof BundleRequirement ? new ConciergeBundleWire(
(BundleCapability) cap, (BundleRequirement) req)
: new ConciergeWire(cap, req);
}
static Wiring createWiring() {
return null;
}
static abstract class GenericReqCap implements Requirement, Capability {
private final String namespace;
private final Map<String, String> directives;
private final Map<String, Object> attributes;
protected GenericReqCap(final String str) throws BundleException {
final String[] literals = Utils.splitString(str, ';');
this.namespace = literals[0].trim();
final ParseResult parseResult = Utils.parseLiterals(literals, 1);
this.directives = parseResult.getDirectives() == null ? Collections
.<String, String> emptyMap() : Collections
.unmodifiableMap(parseResult.getDirectives());
this.attributes = parseResult.getAttributes() == null ? Collections
.<String, Object> emptyMap() : Collections
.unmodifiableMap(parseResult.getAttributes());
}
public GenericReqCap(final String namespace,
final Map<String, String> directives,
final Map<String, Object> attributes) {
this.namespace = namespace;
this.directives = directives == null || directives.isEmpty() ? Collections
.<String, String> emptyMap() : Collections
.unmodifiableMap(directives);
this.attributes = attributes == null || attributes.isEmpty() ? Collections
.<String, Object> emptyMap() : Collections
.unmodifiableMap(attributes);
}
public final String getNamespace() {
return namespace;
}
public final Map<String, String> getDirectives() {
return directives;
}
public final Map<String, Object> getAttributes() {
return attributes;
}
@Override
public String toString() {
if (directives.isEmpty() && attributes.isEmpty()) {
return namespace;
}
String result = namespace + "; ";
for (final String key : directives.keySet()) {
result = result + key + ":=" + directives.get(key) + ", ";
}
for (final String key : attributes.keySet()) {
result = result + key + "=" + attributes.get(key) + ", ";
}
return result.substring(0, result.length() - 2);
}
}
public static class BundleCapabilityImpl extends GenericReqCap implements
BundleCapability {
private final BundleRevision revision;
private final String prettyPrint;
private final String[] includes;
private final String[] excludes;
private final boolean hasExcludes;
BundleCapabilityImpl(final BundleRevision revision, final String str)
throws BundleException {
super(str);
this.revision = revision;
this.prettyPrint = null;
final String excludeStr = getDirectives().get(
PackageNamespace.CAPABILITY_EXCLUDE_DIRECTIVE);
if (excludeStr == null) {
// TODO: couldn't we stop here???
excludes = new String[1];
excludes[0] = "";
hasExcludes = false;
} else {
excludes = Utils.splitString(Utils.unQuote(excludeStr), ',');
hasExcludes = true;
}
final String includeStr = getDirectives().get(
PackageNamespace.CAPABILITY_INCLUDE_DIRECTIVE);
if (includeStr == null) {
includes = new String[1];
includes[0] = "*";
} else {
includes = Utils.splitString(Utils.unQuote(excludeStr), ',');
}
}
public BundleCapabilityImpl(final BundleRevision revision,
final String namespace, final Map<String, String> directives,
final Map<String, Object> attributes, final String prettyPrint) {
super(namespace, directives, attributes);
this.revision = revision;
this.prettyPrint = prettyPrint;
final String excludeStr = getDirectives().get(
PackageNamespace.CAPABILITY_EXCLUDE_DIRECTIVE);
if (excludeStr == null) {
// TODO: couldn't we stop here???
excludes = new String[1];
excludes[0] = "";
hasExcludes = false;
} else {
excludes = Utils.splitString(Utils.unQuote(excludeStr), ',');
hasExcludes = true;
}
final String includeStr = getDirectives().get(
PackageNamespace.CAPABILITY_INCLUDE_DIRECTIVE);
if (includeStr == null) {
includes = new String[1];
includes[0] = "*";
} else {
includes = Utils.splitString(Utils.unQuote(includeStr), ',');
}
if (PackageNamespace.PACKAGE_NAMESPACE.equals(namespace)) {
if (attributes
.get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE) == null) {
attributes.put(
PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE,
Version.emptyVersion);
}
}
}
public BundleRevision getRevision() {
return revision;
}
public BundleRevision getResource() {
return revision;
}
boolean filter(final String name) {
boolean matched = false;
for (int i = 0; i < includes.length; i++) {
if (RFC1960Filter.stringCompare(includes[i].toCharArray(), 0,
name.toCharArray(), 0) == 0) {
matched = true;
break;
}
}
if (!matched) {
return false;
}
matched = false;
for (int i = 0; i < excludes.length; i++) {
if (RFC1960Filter.stringCompare(name.toCharArray(), 0,
excludes[i].toCharArray(), 0) == 0) {
matched = true;
break;
}
}
return !matched;
}
boolean hasExcludes() {
return hasExcludes;
}
@Override
public String toString() {
return prettyPrint == null ? "BundleCapability {"
+ super.toString() + "}" : prettyPrint;
}
}
public static class BundleRequirementImpl extends GenericReqCap implements
BundleRequirement {
private final BundleRevision revision;
private final String prettyPrint;
public BundleRequirementImpl(final BundleRevision revision,
final String str) throws BundleException {
super(str);
this.revision = revision;
this.prettyPrint = null;
}
public BundleRequirementImpl(final BundleRevision revision,
final String namespace, final Map<String, String> directives,
final Map<String, Object> attributes, final String prettyPrint) {
super(namespace, directives, attributes);
this.revision = revision;
this.prettyPrint = "BundleRequirement{" + prettyPrint + "}";
}
public BundleRevision getRevision() {
return revision;
}
public BundleRevision getResource() {
return revision;
}
public boolean matches(final BundleCapability capability) {
return Concierge.matches(this, capability);
}
@Override
public String toString() {
return prettyPrint == null ? "BundleRequirement {"
+ super.toString() + "}" : prettyPrint;
}
}
static class HostedBundleCapability implements HostedCapability,
BundleCapability {
private final BundleRevision host;
private final BundleCapability cap;
HostedBundleCapability(final BundleRevision host, final Capability cap) {
this.host = host;
this.cap = (BundleCapability) cap;
}
public String getNamespace() {
return cap.getNamespace();
}
public Map<String, String> getDirectives() {
return cap.getDirectives();
}
public Map<String, Object> getAttributes() {
return cap.getAttributes();
}
public BundleRevision getResource() {
return host;
}
public Capability getDeclaredCapability() {
return cap;
}
public BundleRevision getRevision() {
return host;
}
public String toString() {
return "HostedCapability{host=" + host + ", cap=" + cap + ")";
}
}
private static abstract class AbstractWireImpl<C extends Capability, R extends Requirement> implements Wire {
protected final C capability;
protected final R requirement;
protected AbstractWireImpl(final C capability, final R requirement) {
this.capability = capability;
this.requirement = requirement;
}
public C getCapability() {
if (capability instanceof HostedCapability) {
@SuppressWarnings("unchecked")
final C declared = (C) ((HostedCapability) capability)
.getDeclaredCapability();
return declared;
}
return capability;
}
public R getRequirement() {
return requirement;
}
public boolean equals(final Object o) {
if (o instanceof AbstractWireImpl) {
return o == this;
}
if (o instanceof Wire) {
final Wire w = (Wire) o;
return w.getRequirer().equals(requirement.getResource())
&& w.getRequirement().equals(requirement)
&& w.getProvider().equals(capability.getResource())
&& w.getCapability().equals(capability);
}
return false;
}
@Override
public String toString() {
return "{" + requirement + "->" + capability + "}";
}
}
static class ConciergeWire extends
AbstractWireImpl<Capability, Requirement> {
protected ConciergeWire(final Capability capability,
final Requirement requirement) {
super(capability, requirement);
}
public Resource getProvider() {
return capability.getResource();
}
public Resource getRequirer() {
return requirement.getResource();
}
}
static class ConciergeBundleWire extends
AbstractWireImpl<BundleCapability, BundleRequirement> implements
BundleWire {
ConciergeBundleWiring providerWiring;
ConciergeBundleWiring requirerWiring;
protected ConciergeBundleWire(final BundleCapability capability,
final BundleRequirement requirement) {
super(capability, requirement);
}
public BundleWiring getProviderWiring() {
if (providerWiring == null) {
providerWiring = (ConciergeBundleWiring) getProvider()
.getWiring();
}
return providerWiring;
}
public BundleWiring getRequirerWiring() {
// final BundleWiring wiring = getRequirer().getWiring();
// return wiring != null && wiring.isCurrent() ? wiring : null;
return requirerWiring;
}
public BundleRevision getProvider() {
return capability.getResource();
}
public BundleRevision getRequirer() {
return requirement.getResource();
}
}
static class ConciergeWiring implements Wiring {
private final Resource resource;
private final MultiMap<String, Capability> capabilities = new MultiMap<String, Capability>();
private final MultiMap<String, Requirement> requirements = new MultiMap<String, Requirement>();
private final MultiMap<String, Wire> providedWires = new MultiMap<String, Wire>();
private final MultiMap<String, Wire> requiredWires = new MultiMap<String, Wire>();
ConciergeWiring(final Resource resource, final List<Wire> wires) {
this.resource = resource;
for (final Wire wire : wires) {
addWire(wire);
}
}
private void addWire(final Wire wire) {
if (wire.getProvider() == resource) {
final Capability cap = wire.getCapability();
capabilities.insertUnique(cap.getNamespace(), cap);
providedWires.insert(cap.getNamespace(), wire);
} else {
final Requirement req = wire.getRequirement();
requirements.insertUnique(req.getNamespace(), req);
requiredWires.insert(req.getNamespace(), wire);
}
}
public List<Capability> getResourceCapabilities(final String namespace) {
return namespace == null ? capabilities.getAllValues()
: capabilities.lookup(namespace);
}
public List<Requirement> getResourceRequirements(final String namespace) {
return namespace == null ? requirements.getAllValues()
: requirements.lookup(namespace);
}
public List<Wire> getProvidedResourceWires(final String namespace) {
return namespace == null ? providedWires.getAllValues()
: providedWires.lookup(namespace);
}
public List<Wire> getRequiredResourceWires(final String namespace) {
return namespace == null ? requiredWires.getAllValues()
: requiredWires.lookup(namespace);
}
public Resource getResource() {
return resource;
}
}
static class ConciergeBundleWiring implements BundleWiring {
protected final BundleRevision revision;
private final MultiMap<String, BundleCapability> capabilities = new MultiMap<String, BundleCapability>();
private final MultiMap<String, BundleRequirement> requirements = new MultiMap<String, BundleRequirement>();
private final Comparator<BundleWire> provComp = new Comparator<BundleWire>() {
public int compare(final BundleWire w1, final BundleWire w2) {
final BundleCapability cap1 = w1.getCapability();
final BundleCapability cap2 = w2.getCapability();
assert cap1.getNamespace().equals(cap2.getNamespace());
final List<Capability> caps = revision.getCapabilities(cap1
.getNamespace());
return caps.indexOf(cap1) - caps.indexOf(cap2);
}
};
private final Comparator<BundleWire> reqComp = new Comparator<BundleWire>() {
public int compare(final BundleWire w1, final BundleWire w2) {
final BundleRequirement req1 = w1.getRequirement();
final BundleRequirement req2 = w2.getRequirement();
assert req1.getNamespace().equals(req2.getNamespace());
final List<Requirement> reqs = revision.getRequirements(req1
.getNamespace());
return reqs.indexOf(req1) - reqs.indexOf(req2);
}
};
private final MultiMap<String, BundleWire> providedWires = new MultiMap<String, BundleWire>(
provComp);
private final MultiMap<String, BundleWire> requiredWires = new MultiMap<String, BundleWire>(
reqComp);
final HashSet<BundleRevision> inUseSet = new HashSet<BundleRevision>();
ConciergeBundleWiring(final BundleRevision revision,
final List<Wire> wires) {
this.revision = revision;
final HashSet<Requirement> reqCache = new HashSet<Requirement>();
if (wires != null) {
for (final Wire wire : wires) {
addWire((BundleWire) wire);
if (wire.getRequirer() == revision) {
reqCache.add(wire.getRequirement());
}
}
}
for (final BundleCapability cap : revision
.getDeclaredCapabilities(null)) {
final String effective = cap.getDirectives().get(
Namespace.CAPABILITY_EFFECTIVE_DIRECTIVE);
if (effective == null
|| Namespace.EFFECTIVE_RESOLVE.equals(effective)) {
capabilities.insert(cap.getNamespace(), cap);
}
}
if (revision.getTypes() == BundleRevision.TYPE_FRAGMENT) {
capabilities.remove(IdentityNamespace.IDENTITY_NAMESPACE);
}
for (final BundleRequirement req : revision
.getDeclaredRequirements(null)) {
final String effective = req.getDirectives().get(
Namespace.REQUIREMENT_EFFECTIVE_DIRECTIVE);
final boolean optional = Namespace.RESOLUTION_OPTIONAL
.equals(req.getDirectives().get(
Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE));
if (effective == null
|| Namespace.EFFECTIVE_RESOLVE.equals(effective)
|| PackageNamespace.RESOLUTION_DYNAMIC
.equals(req
.getDirectives()
.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
if (!optional || reqCache.contains(req)) {
requirements.insert(req.getNamespace(), req);
}
}
}
}
void addWire(final BundleWire wire) {
final Capability cap = wire.getCapability();
final Requirement req = wire.getRequirement();
if (wire.getProvider() == revision) {
providedWires.insert(cap.getNamespace(), wire);
inUseSet.add(wire.getRequirer());
((ConciergeBundleWire) wire).providerWiring = this;
} else {
requiredWires.insert(req.getNamespace(), wire);
if (HostNamespace.HOST_NAMESPACE.equals(wire.getRequirement()
.getNamespace())) {
inUseSet.add(wire.getProvider());
}
((ConciergeBundleWire) wire).requirerWiring = this;
}
}
HashMap<String, BundleWire> getPackageImportWires() {
final List<BundleWire> list = getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE);
final HashMap<String, BundleWire> result = new HashMap<String, BundleWire>();
if (list != null) {
for (final BundleWire wire : list) {
result.put((String) wire.getCapability().getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE), wire);
}
}
return result;
}
List<BundleWire> getRequireBundleWires() {
return getRequiredWires(BundleNamespace.BUNDLE_NAMESPACE);
}
public Bundle getBundle() {
return revision.getBundle();
}
/**
* @see org.osgi.framework.wiring.BundleWiring#isCurrent()
*/
public boolean isCurrent() {
// always current if it is the system bundle
if (revision.getBundle().getBundleId() == 0) {
return true;
}
return ((AbstractBundle) revision.getBundle()).currentRevision == revision
&& revision.getWiring() == this;
}
void cleanup() {
for (final BundleWire requiredWire : requiredWires.getAllValues()) {
final ConciergeBundleWiring bw = ((ConciergeBundleWire) requiredWire).providerWiring;
if (bw != null) {
bw.inUseSet.remove(revision);
}
}
for (final BundleWire hostWire : providedWires
.lookup(HostNamespace.HOST_NAMESPACE)) {
final ConciergeBundleWiring bw = ((ConciergeBundleWire) hostWire).requirerWiring;
if (bw != null) {
bw.inUseSet.remove(revision);
}
}
}
public boolean isInUse() {
return isCurrent() || !inUseSet.isEmpty();
}
public List<BundleCapability> getCapabilities(final String namespace) {
if (!isInUse()) {
return null;
}
return namespace == null ? capabilities.getAllValues()
: capabilities.lookup(namespace);
}
public List<BundleRequirement> getRequirements(final String namespace) {
if (!isInUse()) {
return null;
}
return namespace == null ? requirements.getAllValues()
: requirements.lookup(namespace);
}
public List<BundleWire> getProvidedWires(final String namespace) {
if (!isInUse()) {
return null;
}
return namespace == null ? providedWires.getAllValues()
: providedWires.lookup(namespace);
}
public List<BundleWire> getRequiredWires(final String namespace) {
if (!isInUse()) {
return null;
}
return namespace == null ? requiredWires.getAllValues()
: requiredWires.lookup(namespace);
}
public BundleRevision getRevision() {
return revision;
}
public ClassLoader getClassLoader() {
if (!isInUse()) {
return null;
}
if (revision instanceof Revision) {
return ((Revision) revision).classloader;
} else {
return null;
}
}
/**
* @see org.osgi.framework.wiring.BundleWiring#findEntries(java.lang.String,
* java.lang.String, int)
*/
public List<URL> findEntries(final String path,
final String filePattern, final int options) {
if (!isInUse()) {
return null;
}
Enumeration<URL> result = null;
if (revision instanceof Revision) {
final Revision rev = (Revision) revision;
result = rev.findEntries(path, filePattern,
options == FINDENTRIES_RECURSE);
}
return result == null ? Collections.<URL> emptyList() : Collections
.unmodifiableList(Collections.list(result));
}
/**
* @see org.osgi.framework.wiring.BundleWiring#listResources(java.lang.String,
* java.lang.String, int)
*/
public Collection<String> listResources(final String path,
final String filePattern, final int options) {
if (!isInUse()) {
return null;
}
return Collections
.unmodifiableSet(((Revision) revision).classloader
.listResources(path, filePattern, options,
new HashSet<String>()));
}
public List<Capability> getResourceCapabilities(final String namespace) {
final List<BundleCapability> bcaps = getCapabilities(namespace);
return bcaps == null ? null : new ArrayList<Capability>(bcaps);
}
public List<Requirement> getResourceRequirements(final String namespace) {
final List<BundleRequirement> breqs = getRequirements(namespace);
return breqs == null ? null : new ArrayList<Requirement>(breqs);
}
public List<Wire> getProvidedResourceWires(final String namespace) {
final List<BundleWire> bwires = getProvidedWires(namespace);
return bwires == null ? null : new ArrayList<Wire>(bwires);
}
public List<Wire> getRequiredResourceWires(final String namespace) {
final List<BundleWire> bwires = getRequiredWires(namespace);
return bwires == null ? null : new ArrayList<Wire>(bwires);
}
public BundleRevision getResource() {
return revision;
}
void addCapability(final HostedCapability hostedCap) {
capabilities.insert(hostedCap.getNamespace(),
(BundleCapability) hostedCap.getDeclaredCapability());
}
@Override
public String toString() {
return "[ConciergeBundleWiring of " + revision + "]";
}
}
}