blob: a81d33dad4ea433d3701fd7c868ebff0daff458b [file] [log] [blame]
/*
* 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.framework;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.apache.felix.framework.capabilityset.Capability;
import org.apache.felix.framework.capabilityset.CapabilitySet;
import org.apache.felix.framework.capabilityset.Requirement;
import org.apache.felix.framework.resolver.CandidateComparator;
import org.apache.felix.framework.resolver.Module;
import org.apache.felix.framework.resolver.ResolveException;
import org.apache.felix.framework.resolver.Resolver;
import org.apache.felix.framework.resolver.Wire;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.R4Library;
import org.osgi.framework.BundlePermission;
import org.osgi.framework.Constants;
import org.osgi.framework.PackagePermission;
class ResolverStateImpl implements Resolver.ResolverState
{
private final Logger m_logger;
// Set of all modules.
private final Set<Module> m_modules;
// Set of all fragments.
private final Set<Module> m_fragments;
// Capability sets.
private final Map<String, CapabilitySet> m_capSets;
// Execution environment.
private final String m_fwkExecEnvStr;
// Parsed framework environments
private final Set<String> m_fwkExecEnvSet;
ResolverStateImpl(Logger logger, String fwkExecEnvStr)
{
m_logger = logger;
m_modules = new HashSet<Module>();
m_fragments = new HashSet<Module>();
m_capSets = new HashMap<String, CapabilitySet>();
m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
List<String> indices = new ArrayList<String>();
indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
m_capSets.put(Capability.MODULE_NAMESPACE, new CapabilitySet(indices, true));
indices = new ArrayList<String>();
indices.add(Capability.PACKAGE_ATTR);
m_capSets.put(Capability.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
indices = new ArrayList<String>();
indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
m_capSets.put(Capability.HOST_NAMESPACE, new CapabilitySet(indices, true));
}
synchronized void addModule(Module m)
{
m_modules.add(m);
List<Capability> caps = m.getCapabilities();
if (caps != null)
{
for (Capability cap : caps)
{
CapabilitySet capSet = m_capSets.get(cap.getNamespace());
if (capSet == null)
{
capSet = new CapabilitySet(null, true);
m_capSets.put(cap.getNamespace(), capSet);
}
capSet.addCapability(cap);
}
}
if (Util.isFragment(m))
{
m_fragments.add(m);
}
}
synchronized void removeModule(Module m)
{
m_modules.remove(m);
List<Capability> caps = m.getCapabilities();
if (caps != null)
{
for (Capability cap : caps)
{
CapabilitySet capSet = m_capSets.get(cap.getNamespace());
if (capSet != null)
{
capSet.removeCapability(cap);
}
}
}
if (Util.isFragment(m))
{
m_fragments.remove(m);
}
}
synchronized Set<Module> getFragments()
{
return new HashSet(m_fragments);
}
synchronized void removeSubstitutedCapabilities(Module module)
{
if (module.isResolved())
{
// Loop through the module's package wires and determine if any
// of them overlap any of the packages exported by the module.
// If so, then the framework must have chosen to have the module
// import rather than export the package, so we need to remove the
// corresponding package capability from the package capability set.
List<Wire> wires = module.getWires();
List<Capability> caps = module.getCapabilities();
for (int wireIdx = 0; (wires != null) && (wireIdx < wires.size()); wireIdx++)
{
Wire wire = wires.get(wireIdx);
if (wire.getCapability().getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
for (int capIdx = 0;
(caps != null) && (capIdx < caps.size());
capIdx++)
{
if (caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
&& wire.getCapability().getAttribute(Capability.PACKAGE_ATTR).getValue()
.equals(caps.get(capIdx).getAttribute(Capability.PACKAGE_ATTR).getValue()))
{
m_capSets.get(Capability.PACKAGE_NAMESPACE).removeCapability(caps.get(capIdx));
break;
}
}
}
}
}
}
//
// ResolverState methods.
//
public synchronized SortedSet<Capability> getCandidates(
Requirement req, boolean obeyMandatory)
{
Module module = req.getModule();
SortedSet<Capability> result = new TreeSet<Capability>(new CandidateComparator());
CapabilitySet capSet = m_capSets.get(req.getNamespace());
if (capSet != null)
{
Set<Capability> matches = capSet.match(req.getFilter(), obeyMandatory);
for (Capability cap : matches)
{
if (System.getSecurityManager() != null)
{
if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE) && (
!((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
new PackagePermission((String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
PackagePermission.EXPORTONLY)) ||
!((module == null) ||
((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
new PackagePermission((String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
cap.getModule().getBundle(),PackagePermission.IMPORT))
)))
{
if (module != cap.getModule())
{
continue;
}
}
else if (req.getNamespace().equals(Capability.MODULE_NAMESPACE) && (
!((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
!((module == null) ||
((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
new BundlePermission(module.getSymbolicName(), BundlePermission.REQUIRE))
)))
{
continue;
}
}
else if (req.getNamespace().equals(Capability.HOST_NAMESPACE)
&& cap.getModule().isResolved())
{
continue;
}
result.add(cap);
}
}
return result;
}
public void checkExecutionEnvironment(Module module) throws ResolveException
{
String bundleExecEnvStr = (String)
module.getHeaders().get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
if (bundleExecEnvStr != null)
{
bundleExecEnvStr = bundleExecEnvStr.trim();
// If the bundle has specified an execution environment and the
// framework has an execution environment specified, then we must
// check for a match.
if (!bundleExecEnvStr.equals("")
&& (m_fwkExecEnvStr != null)
&& (m_fwkExecEnvStr.length() > 0))
{
StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
boolean found = false;
while (tokens.hasMoreTokens() && !found)
{
if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
{
found = true;
}
}
if (!found)
{
throw new ResolveException(
"Execution environment not supported: "
+ bundleExecEnvStr, module, null);
}
}
}
}
public void checkNativeLibraries(Module module) throws ResolveException
{
// Next, try to resolve any native code, since the module is
// not resolvable if its native code cannot be loaded.
List<R4Library> libs = module.getNativeLibraries();
if (libs != null)
{
String msg = null;
// Verify that all native libraries exist in advance; this will
// throw an exception if the native library does not exist.
for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
{
String entryName = libs.get(libIdx).getEntryName();
if (entryName != null)
{
if (!module.getContent().hasEntry(entryName))
{
msg = "Native library does not exist: " + entryName;
}
}
}
// If we have a zero-length native library array, then
// this means no native library class could be selected
// so we should fail to resolve.
if (libs.isEmpty())
{
msg = "No matching native libraries found.";
}
if (msg != null)
{
throw new ResolveException(msg, module, null);
}
}
}
//
// Utility methods.
//
/**
* Updates the framework wide execution environment string and a cached Set of
* execution environment tokens from the comma delimited list specified by the
* system variable 'org.osgi.framework.executionenvironment'.
* @param fwkExecEnvStr Comma delimited string of provided execution environments
* @return the parsed set of execution environments
**/
private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
{
Set<String> newSet = new HashSet<String>();
if (fwkExecEnvStr != null)
{
StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
while (tokens.hasMoreTokens())
{
newSet.add(tokens.nextToken().trim());
}
}
return newSet;
}
}