Eclipse Equinox Documentation

Logo

This is the documentation of the Eclipse Equinox Framework.

View the Project on GitHub eclipse-equinox/equinox

Boot Delegation

Overview

Boot delegation in the Equinox OSGi Framework controls which classes can be loaded directly from the parent (boot) classloader, bypassing the normal OSGi class loading mechanism. This feature is particularly important when dealing with JDK classes and framework extensions.

In a standard OSGi environment, each bundle has its own classloader and explicitly declares its dependencies through Import-Package or Require-Bundle headers. However, there are scenarios where bundles need access to classes from the boot classpath (JDK classes, VM-provided classes) without explicitly importing them.

How Boot Delegation Works

When a bundle attempts to load a class, the Equinox framework follows the OSGi delegation model:

  1. Check java.* packages - These are always delegated to the parent classloader
  2. Boot delegation check - If the package is in the boot delegation list, attempt to load from parent classloader
  3. Search imported packages - Load from explicitly imported packages
  4. Search required bundles - Load from required bundles
  5. Search local bundle - Search within the bundle itself
  6. Dynamic imports - Attempt dynamic package imports
  7. Compatibility boot delegation - As a fallback, try parent classloader again (if enabled)

The boot delegation mechanism operates at step 2, allowing early delegation to the parent classloader for specified packages.

Configuration Properties

org.osgi.framework.bootdelegation

The primary property for configuring boot delegation is org.osgi.framework.bootdelegation. This property accepts a comma-separated list of package names that should be delegated to the parent classloader.

Syntax:

org.osgi.framework.bootdelegation=package1,package2.*,package3.subpackage

Examples:

Delegate specific package:

org.osgi.framework.bootdelegation=sun.reflect

Delegate package and all subpackages (using wildcard):

org.osgi.framework.bootdelegation=sun.reflect.*

Delegate all packages:

org.osgi.framework.bootdelegation=*

Delegate multiple packages:

org.osgi.framework.bootdelegation=sun.reflect,sun.misc,com.sun.*

Wildcard Rules:

osgi.compatibility.bootdelegation

The osgi.compatibility.bootdelegation property provides backward compatibility for older applications that relied on implicit boot delegation.

Values:

Default behavior:

When enabled, this property allows bundles to load classes from the boot classpath even if they don’t explicitly import them and the class is not in the boot delegation list. This happens as a last resort after all other class loading mechanisms have been attempted.

Example in config.ini:

osgi.compatibility.bootdelegation=false

Example programmatically:

Map<String, Object> configuration = new HashMap<>();
configuration.put("osgi.compatibility.bootdelegation", "true");
Equinox equinox = new Equinox(configuration);

osgi.context.bootdelegation

The osgi.context.bootdelegation property controls whether boot delegation is enabled when loading classes from the context classloader.

Values:

Default: true

This property is particularly useful when dealing with thread context classloaders and class loading that happens outside the normal bundle classloader chain.

osgi.java.profile.bootdelegation

This property controls how the org.osgi.framework.bootdelegation property defined in Java profiles should be processed.

Values:

Default: ignore

Use Cases

Loading JDK Internal Classes

Some applications need access to internal JDK classes that are not part of the standard exported API.

org.osgi.framework.bootdelegation=sun.misc,sun.reflect.*,com.sun.jndi.*

Note: Relying on internal JDK classes is discouraged as they may change or be removed in future Java versions. Use with caution and consider alternatives.

Framework Extensions

When using framework extensions that add classes to the system bundle, boot delegation may be needed to ensure proper access.

Testing Scenarios

During testing, you may want to allow bundles to access test framework classes from the boot classpath:

org.osgi.framework.bootdelegation=org.junit.*,org.hamcrest.*

Legacy Application Migration

When migrating legacy applications to OSGi, boot delegation can provide a temporary bridge:

osgi.compatibility.bootdelegation=true

However, this should be a temporary measure. The long-term solution is to properly modularize the application with correct Import-Package declarations.

Configuration Methods

Using config.ini

In an Eclipse installation, edit the configuration/config.ini file:

org.osgi.framework.bootdelegation=sun.reflect,sun.misc.*
osgi.compatibility.bootdelegation=false

Programmatic Configuration

When embedding Equinox:

Map<String, Object> configuration = new HashMap<>();
configuration.put(Constants.FRAMEWORK_BOOTDELEGATION, "sun.reflect,sun.misc.*");
configuration.put("osgi.compatibility.bootdelegation", "false");

Equinox equinox = new Equinox(configuration);
equinox.start();

System Properties

Boot delegation can also be configured via system properties:

java -Dorg.osgi.framework.bootdelegation=sun.reflect,sun.misc.* -jar org.eclipse.osgi.jar

Runtime Arguments

When starting Eclipse:

eclipse -vmargs -Dorg.osgi.framework.bootdelegation=sun.reflect.*

Best Practices

Minimize Boot Delegation

Recommendation: Use boot delegation sparingly. Over-reliance on boot delegation defeats the purpose of OSGi modularization.

Why: Boot delegation bypasses the explicit dependency management that makes OSGi valuable. It can lead to:

Prefer Explicit Imports

Instead of relying on boot delegation, bundles should explicitly declare their dependencies:

Import-Package: sun.misc;resolution:=optional

This makes dependencies explicit and allows the OSGi framework to properly manage versions and resolve conflicts.

Use Specific Packages

Avoid using wildcards unnecessarily:

Good:

org.osgi.framework.bootdelegation=sun.misc.Unsafe

Acceptable:

org.osgi.framework.bootdelegation=sun.misc.*

Avoid:

org.osgi.framework.bootdelegation=*

Using * delegates all packages to the boot classloader, which can cause:

Document Why Boot Delegation Is Needed

When using boot delegation, document the reason in your configuration:

# Required for legacy JNDI integration
org.osgi.framework.bootdelegation=com.sun.jndi.*

Test Without Boot Delegation First

During development, test your bundles without boot delegation to identify missing imports:

osgi.compatibility.bootdelegation=false

This helps identify missing Import-Package declarations that should be added to your manifest.

Troubleshooting

ClassNotFoundException Despite Boot Delegation

Symptom: Bundle throws ClassNotFoundException even though the package is in boot delegation list.

Possible causes:

  1. Typo in package name - Verify the package name exactly matches
  2. Class not in boot classpath - The class must be available in the parent classloader
  3. Incorrect wildcard - Ensure you’re using package.* not just package*

Solution:

Conflicting Class Versions

Symptom: Wrong version of a class is loaded when boot delegation is enabled.

Possible cause: Boot delegation loads the class from the parent classloader instead of the version bundled with your application.

Solution:

Performance Issues

Symptom: Slow class loading or startup time.

Possible cause: Using * for boot delegation causes all class loading to attempt parent delegation first.

Solution:

Unexpected Class Loading Behavior

Symptom: Classes are loaded from unexpected locations.

Possible cause: osgi.compatibility.bootdelegation=true allows fallback to boot classloader.

Solution:

Debugging

Enable OSGi Debug Tracing

To debug boot delegation issues, enable OSGi debug tracing:

In config.ini:

osgi.debug=true
osgi.debug.loader=true

Create .options file:

org.eclipse.osgi/debug=true
org.eclipse.osgi/debug/loader=true
org.eclipse.osgi/debug/classloader=true

Then start with:

eclipse -debug .options

Use OSGi Console

Start Eclipse with the OSGi console to inspect bundle wiring:

eclipse -console

Useful commands:

Test Boot Delegation Configuration

To test if a package is in the boot delegation list:

  1. Create a minimal test bundle that doesn’t import the package
  2. Try to load the class from the package
  3. If it loads, boot delegation is working; if not, check configuration

Example test code:

try {
    Class<?> clazz = bundle.loadClass("sun.misc.Unsafe");
    System.out.println("Boot delegation working for sun.misc");
} catch (ClassNotFoundException e) {
    System.out.println("Boot delegation not configured for sun.misc");
}

Implementation Details

The boot delegation mechanism in Equinox is implemented in the BundleLoader class. When a bundle attempts to load a class, the framework:

  1. Extracts the package name from the fully qualified class name
  2. Checks if the package matches any entry in the boot delegation list (using exact match or wildcard matching)
  3. If matched, attempts to load the class from the parent classloader
  4. If parent classloader throws ClassNotFoundException, continues with normal OSGi delegation
  5. As a fallback (if osgi.compatibility.bootdelegation=true), tries parent classloader again after all other mechanisms fail

The boot delegation list is configured during framework initialization and is compiled into efficient data structures:

Migration from Non-OSGi Applications

When migrating existing Java applications to OSGi, you may encounter class loading issues. Here’s a migration strategy:

Phase 1: Get It Working

  1. Enable compatibility boot delegation:
    osgi.compatibility.bootdelegation=true
    
  2. Add broad boot delegation if needed:
    org.osgi.framework.bootdelegation=sun.*,com.sun.*
    

Phase 2: Identify Dependencies

  1. Set osgi.compatibility.bootdelegation=false
  2. Note which classes fail to load
  3. Determine if they’re:
    • JDK classes that should be imported
    • Internal classes that should be avoided
    • Classes that need boot delegation

Phase 3: Proper Modularization

  1. Add explicit Import-Package declarations for JDK classes
  2. Remove or replace usage of internal JDK classes
  3. Minimize boot delegation to only truly necessary packages
  4. Remove osgi.compatibility.bootdelegation setting

Phase 4: Remove Boot Delegation

The final goal is to eliminate boot delegation entirely by:

Parent Classloader

Boot delegation relies on the parent classloader, which is typically the application classloader that loaded the OSGi framework. This classloader has access to:

Framework Extensions

Framework extensions are fragments to the system bundle that can add classes to the framework classpath. These classes are available through boot delegation.

Class Loading Order

Understanding OSGi class loading order is crucial for using boot delegation effectively:

  1. java.* packages (always boot delegated)
  2. Boot delegation packages (if configured)
  3. Imported packages (explicit dependencies)
  4. Required bundles (Require-Bundle dependencies)
  5. Local bundle classes (Bundle-ClassPath)
  6. Dynamic imports (DynamicImport-Package)
  7. Hooks and buddy policy
  8. Compatibility boot delegation (if enabled)

Import-Package vs Boot Delegation

Aspect Import-Package Boot Delegation
Explicitness Explicit dependency Implicit dependency
Version control Supports version ranges No version control
OSGi visibility Follows OSGi rules Bypasses OSGi
Best practice Recommended Use sparingly
Performance Optimized Additional lookup overhead

Examples

Example 1: Minimal Boot Delegation

Configure only essential internal classes:

config.ini:

org.osgi.framework.bootdelegation=sun.misc.Unsafe
osgi.compatibility.bootdelegation=false

Example 2: Testing Configuration

Allow test frameworks to be boot delegated for easier test setup:

config.ini for testing:

org.osgi.framework.bootdelegation=org.junit.*,org.hamcrest.*,org.mockito.*
osgi.compatibility.bootdelegation=false

Example 3: Legacy Application

Temporary configuration for legacy application migration:

config.ini:

org.osgi.framework.bootdelegation=sun.*,com.sun.*,javax.xml.ws.*
osgi.compatibility.bootdelegation=true

Example 4: Embedded Equinox

Configure boot delegation when embedding Equinox programmatically:

import org.eclipse.osgi.launch.Equinox;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;

import java.util.HashMap;
import java.util.Map;

public class EmbeddedEquinoxExample {
    public static void main(String[] args) throws Exception {
        Map<String, Object> config = new HashMap<>();
        
        // Configure storage location
        config.put(Constants.FRAMEWORK_STORAGE, "./osgi-cache");
        
        // Configure boot delegation
        config.put(Constants.FRAMEWORK_BOOTDELEGATION, "sun.reflect,sun.misc.*");
        
        // Disable compatibility boot delegation for strict OSGi behavior
        config.put("osgi.compatibility.bootdelegation", "false");
        
        // Create and start framework
        Framework framework = new Equinox(config);
        framework.start();
        
        // Use framework...
        
        framework.stop();
        framework.waitForStop(0);
    }
}

Example 5: Debugging Boot Delegation

Enable detailed logging to understand class loading:

config.ini:

org.osgi.framework.bootdelegation=sun.misc.*
osgi.debug=true
eclipse.consoleLog=true

.options:

org.eclipse.osgi/debug=true
org.eclipse.osgi/debug/loader=true
org.eclipse.osgi/debug/classloader=true
org.eclipse.osgi/debug/loader/findClass=true

References

OSGi Specification

The boot delegation behavior is specified in the OSGi Core Specification:

Equinox Implementation

Key classes in the Equinox implementation:

Other class loading related properties:

See Also