Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions framework/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
exports org.osgi.framework.connect;
exports org.osgi.framework.dto;
exports org.osgi.framework.hooks.bundle;
exports org.osgi.framework.hooks.delegation;
exports org.osgi.framework.hooks.resolver;
exports org.osgi.framework.hooks.service;
exports org.osgi.framework.hooks.weaving;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ class BundleRevisionDependencies
private final Map<BundleRevision, Map<BundleCapability, Set<BundleWire>>>
m_dependentsMap = new HashMap<>();

// Revisions that got classes from a provider through a DelegationHook.
// There is no wire for this, so it is tracked separately.
private final Map<BundleRevision, Set<BundleRevision>>
m_delegationDependentsMap = new HashMap<>();

public synchronized void addDelegationDependent(
BundleRevision provider, BundleRevision requirer)
{
if (provider != requirer)
{
m_delegationDependentsMap.computeIfAbsent(provider, k -> new HashSet<>())
.add(requirer);
}
}

public synchronized void addDependent(BundleWire bw)
{
BundleRevision provider = bw.getProvider();
Expand Down Expand Up @@ -85,6 +100,7 @@ public synchronized void removeDependent(
public synchronized void removeDependents(BundleRevision provider)
{
m_dependentsMap.remove(provider);
m_delegationDependentsMap.remove(provider);
}

public synchronized Map<BundleCapability, Set<BundleWire>>
Expand Down Expand Up @@ -197,6 +213,14 @@ public synchronized Set<Bundle> getDependentBundles(Bundle bundle)
}
}
}
Set<BundleRevision> delegating = m_delegationDependentsMap.get(revision);
if (delegating != null)
{
for (BundleRevision requirer : delegating)
{
result.add(requirer.getBundle());
}
}
}
}

Expand Down Expand Up @@ -275,6 +299,10 @@ public synchronized Set<Bundle> getRequiringBundles(Bundle bundle)
public synchronized void removeDependencies(Bundle bundle)
{
List<BundleRevision> revs = bundle.adapt(BundleRevisions.class).getRevisions();
for (Set<BundleRevision> delegating : m_delegationDependentsMap.values())
{
delegating.removeAll(revs);
}
for (BundleRevision rev : revs)
{
BundleWiring wiring = rev.getWiring();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1186,10 +1186,73 @@ private Enumeration<URL> findResourcesByDelegation(String name)
}
}

Enumeration<URL> delegated = findResourcesByDelegationHooks(pkgName, name);
if (delegated != null)
{
completeUrlList.add(delegated);
}

return new CompoundEnumeration((Enumeration[])
completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
}

private boolean isDelegationHookCandidate(String pkgName)
{
// Hooks are not asked for java.*, nor for packages that are
// imported, required or exported.
if (pkgName.startsWith("java.") || hasPackageSource(pkgName))
{
return false;
}
for (BundleCapability cap : m_resolvedCaps)
{
if (BundleRevision.PACKAGE_NAMESPACE.equals(cap.getNamespace())
&& pkgName.equals(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)))
{
return false;
}
}
return true;
}

private DelegationHookSupport getDelegationHooks()
{
DelegationHookSupport hooks =
m_revision.getBundle().getFramework().getDelegationHooks();
return hooks.hasHooks() ? hooks : null;
}

private Enumeration<URL> findResourcesByDelegationHooks(String pkgName, String name)
{
if (isDelegationHookCandidate(pkgName))
{
DelegationHookSupport hooks = getDelegationHooks();
if (hooks != null)
{
List<URL> urls = hooks.findResources(this, name);
if (!urls.isEmpty())
{
return Collections.enumeration(urls);
}
}
}
return null;
}

private Object findByDelegationHooks(String pkgName, String name, boolean isClass)
{
if (isDelegationHookCandidate(pkgName))
{
DelegationHookSupport hooks = getDelegationHooks();
if (hooks != null)
{
return isClass ? (Object) hooks.findClass(this, name)
: (Object) hooks.findResource(this, name);
}
}
return null;
}

private ClassLoader determineParentClassLoader()
{
// Determine the class loader's parent based on the
Expand Down Expand Up @@ -1520,6 +1583,12 @@ private Object findClassOrResourceByDelegation(String name, boolean isClass)
{
result = searchDynamicImports(pkgName, name, isClass);
}

// As the last step ask the delegation hooks.
if (result == null)
{
result = findByDelegationHooks(pkgName, name, isClass);
}
}
}
finally
Expand Down Expand Up @@ -1880,6 +1949,11 @@ public BundleImpl getBundle()
return m_wiring.getBundle();
}

BundleWiringImpl getBundleWiring()
{
return m_wiring;
}

@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* 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.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.hooks.delegation.DelegationHook;
import org.osgi.framework.wiring.BundleWiring;

/**
* Calls the registered {@link DelegationHook} services on behalf of a
* {@link BundleWiringImpl}. Hooks are called in service ranking order without
* holding any framework lock. A hook that throws is reported as a
* {@link FrameworkEvent#ERROR} and is not called again.
*/
class DelegationHookSupport
{
private final Felix m_felix;

DelegationHookSupport(Felix felix)
{
m_felix = felix;
}

boolean hasHooks()
{
return !hooks().isEmpty();
}

Class<?> findClass(BundleWiringImpl wiring, String name)
{
for (ServiceReference<DelegationHook> sr : hooks())
{
DelegationHook hook = m_felix.getService(m_felix, sr, false);
if (hook == null)
{
continue;
}
try
{
Class<?> clazz = hook.findClass(wiring, name);
if (clazz != null)
{
ClassLoader cl = clazz.getClassLoader();
if (cl instanceof BundleWiringImpl.BundleClassLoader)
{
BundleWiringImpl provider =
((BundleWiringImpl.BundleClassLoader) cl).getBundleWiring();
m_felix.getDependencies().addDelegationDependent(
provider.getRevision(), wiring.getRevision());
}
return clazz;
}
}
catch (ClassNotFoundException ex)
{
// The hook knows the class but cannot load it, treat as not provided.
}
catch (Throwable th)
{
failed(sr, th);
}
finally
{
m_felix.ungetService(m_felix, sr, null);
}
}
return null;
}

URL findResource(BundleWiringImpl wiring, String name)
{
for (ServiceReference<DelegationHook> sr : hooks())
{
DelegationHook hook = m_felix.getService(m_felix, sr, false);
if (hook == null)
{
continue;
}
try
{
URL url = hook.findResource(wiring, name);
if (url != null)
{
return url;
}
}
catch (Throwable th)
{
failed(sr, th);
}
finally
{
m_felix.ungetService(m_felix, sr, null);
}
}
return null;
}

List<URL> findResources(BundleWiring wiring, String name)
{
List<URL> result = new ArrayList<>();
for (ServiceReference<DelegationHook> sr : hooks())
{
DelegationHook hook = m_felix.getService(m_felix, sr, false);
if (hook == null)
{
continue;
}
try
{
Collection<URL> urls = hook.findResources(wiring, name);
if (urls != null)
{
result.addAll(urls);
}
}
catch (Throwable th)
{
failed(sr, th);
}
finally
{
m_felix.ungetService(m_felix, sr, null);
}
}
return result;
}

private List<ServiceReference<DelegationHook>> hooks()
{
List<ServiceReference<DelegationHook>> result = new ArrayList<>();
for (ServiceReference<DelegationHook> sr :
m_felix.getHookRegistry().getHooks(DelegationHook.class))
{
if (!m_felix.getHookRegistry().isHookBlackListed(sr))
{
result.add(sr);
}
}
return result;
}

private void failed(ServiceReference<DelegationHook> sr, Throwable th)
{
m_felix.getHookRegistry().blackListHook(sr);
m_felix.fireFrameworkEvent(FrameworkEvent.ERROR, sr.getBundle(), th);
}
}
6 changes: 6 additions & 0 deletions framework/src/main/java/org/apache/felix/framework/Felix.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ public class Felix extends BundleImpl implements Framework
// but no lock for reads since it is copy on write.
private volatile List<BundleImpl> m_uninstalledBundles;
// Object to keep track of dependencies among bundle revisions.
private final DelegationHookSupport m_delegationHooks = new DelegationHookSupport(this);
private final BundleRevisionDependencies m_dependencies =
new BundleRevisionDependencies();

Expand Down Expand Up @@ -3924,6 +3925,11 @@ else if (Util.isFragment(adapt(BundleRevision.class)))
// Hook service management methods.
//

DelegationHookSupport getDelegationHooks()
{
return m_delegationHooks;
}

HookRegistry getHookRegistry()
{
return m_registry.getHookRegistry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class HookRegistry
addHookClass(org.osgi.framework.hooks.bundle.CollisionHook.class);
addHookClass(org.osgi.framework.hooks.bundle.FindHook.class);
addHookClass(org.osgi.framework.hooks.bundle.EventHook.class);
addHookClass(org.osgi.framework.hooks.delegation.DelegationHook.class);
addHookClass(org.osgi.framework.hooks.service.EventHook.class);
addHookClass(org.osgi.framework.hooks.service.EventListenerHook.class);
addHookClass(org.osgi.framework.hooks.service.FindHook.class);
Expand Down
Loading
Loading