Skip to content

Add native support for Eclipse-BundleShape: dir in Equinox framework#1168

Open
Copilot wants to merge 2 commits intomasterfrom
copilot/support-eclipse-bundleshape-dir
Open

Add native support for Eclipse-BundleShape: dir in Equinox framework#1168
Copilot wants to merge 2 commits intomasterfrom
copilot/support-eclipse-bundleshape-dir

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Oct 16, 2025

Problem

The Eclipse-BundleShape: dir manifest header is currently only evaluated by P2 during installation time. This creates significant challenges when using Equinox in embedded scenarios:

  1. Special handling is required outside of the standard OSGi APIs
  2. When using BundleContext#installBundle(String, InputStream), it's impossible to work around this limitation
  3. Bundles that should be extracted to directories are instead kept as JAR files, which can cause issues with bundle resolution and resource access

Solution

This PR adds native support for Eclipse-BundleShape: dir directly in the Equinox framework. When a bundle is installed (via either installBundle(String) or installBundle(String, InputStream)), the framework now:

  1. Checks the bundle's manifest for the Eclipse-BundleShape header
  2. If the value is "dir", extracts the JAR contents to a directory instead of copying the JAR file
  3. Uses the extracted directory as the bundle content, allowing Equinox to create a DirBundleFile automatically

Implementation Details

Modified: BundleInfo.java

  • Added Generation.installBundleFile() method that combines content setting with bundle shape checking
  • Added private Generation.checkBundleShape() method that creates the BundleFile, reads headers, and extracts if needed
  • Headers are read only once from the already-opened BundleFile, avoiding performance overhead

Modified: Storage.java

  • Added ECLIPSE_BUNDLESHAPE constant for the header name
  • Added extractJarToDirectory() helper method to safely extract JAR contents with directory traversal protection
  • Updated install() and update() methods to use installBundleFile() instead of separate setContent() and getBundleFile().open() calls

The implementation integrates seamlessly into the existing bundle installation flow, requiring no changes to public APIs or calling code. The bundle's JAR file is opened only once during installation, with headers read from the already-open BundleFile to determine if extraction is needed.

Performance

The refactored implementation ensures optimal performance:

  • JAR files are opened only once during installation (not twice)
  • Headers are read from the already-open BundleFile
  • No overhead for regular bundles without the Eclipse-BundleShape header
  • Extraction only happens when explicitly needed

Testing

Added comprehensive test coverage:

  • Created test bundle test.bundleshape.dir with Eclipse-BundleShape: dir header
  • Added testBundleShapeDir() to verify installation via location string
  • Added testBundleShapeDirWithInputStream() to verify installation via InputStream

Backward Compatibility

This change is fully backward compatible:

  • Reference bundles (Type.REFERENCE) are not affected
  • Connect bundles (Type.CONNECT) are not affected
  • Existing bundles without the header continue to work as before
  • Directory bundles are not double-processed
  • If manifest reading fails, bundles are treated as normal (safe fallback)

Security

The extraction process includes canonical path validation to prevent directory traversal attacks from malicious bundle entries.

Fixes #1167

Original prompt

This section details on the original issue you should resolve

<issue_title>Support for "Eclipse-BundleShape: dir" directly in Equinox</issue_title>
<issue_description>Once in a while when using Equinox in an embedded scenario I stumble over bundles that set Eclipse-BundleShape: dir.
As this only evaluated by P2 during install time one always has to do special things. Especially when using BundleContext#installBundle(String, InputStream) it is even not possible at all to use a workaround here.

I therefore wonder if we should not simply add support at Equinox directly it could work this way:

If org.osgi.framework.BundleContext.installBundle(String) is called and not the reference: protocol is used or the string already points to a directory or if org.osgi.framework.BundleContext.installBundle(String, InputStream) is used we check for the header Eclipse-BundleShape and if it is equal to dir instead of copy it into the current bundleFile as today we extract everything from the jar into a bundleDir directory and use that instead so this header is respected.

</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #1167


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Add support for Eclipse-BundleShape: dir in Equinox Add native support for Eclipse-BundleShape: dir in Equinox framework Oct 16, 2025
Copilot AI requested a review from laeubi October 16, 2025 07:03
@laeubi laeubi marked this pull request as ready for review October 16, 2025 07:26
@laeubi laeubi force-pushed the copilot/support-eclipse-bundleshape-dir branch from b6c7f4b to 1be2c2c Compare October 23, 2025 05:57
Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
@laeubi laeubi force-pushed the copilot/support-eclipse-bundleshape-dir branch from 1be2c2c to 5590ead Compare October 23, 2025 06:11
@github-actions
Copy link
Copy Markdown

Test Results

  810 files  ±0    810 suites  ±0   1h 28m 37s ⏱️ - 1m 24s
2 224 tests +2  2 173 ✅ ±0   49 💤 ±0  2 ❌ +2 
6 684 runs  +6  6 529 ✅ ±0  149 💤 ±0  6 ❌ +6 

For more details on these failures, see this check.

Results for commit 5590ead. ± Comparison against base commit e3c0da8.

@laeubi
Copy link
Copy Markdown
Member

laeubi commented Oct 23, 2025

@tjwatson I'm struggling a bit with getting the test working but would you be fine with the general idea?

Comment thread bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java Outdated
- Move bundle shape check to Generation.installBundleFile() method
- Read headers only once by creating BundleFile first, then checking headers
- Extract JAR to directory only when needed based on Eclipse-BundleShape: dir header
- Addresses performance concerns raised in code review

Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@tjwatson tjwatson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we didn't have to write new JAR extraction code when something already exists on BundleFile to extract the content. I approve the PR, but would like us to consider removing the new extract code.


try {
// Extract to temporary directory first
getStorage().extractJarToDirectory(jarFile, tempExtractDir);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use initialBundleFile.getFile("", false) to get the extracted content instead of having a new extractJarToDirectory. I'll admit the implementation of getFile when using the empty string root isn't the most efficient implementation and that could be improved.

@tjwatson tjwatson self-requested a review November 6, 2025 14:23
Copy link
Copy Markdown
Contributor

@tjwatson tjwatson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except we seem to have a compilation error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for "Eclipse-BundleShape: dir" directly in Equinox

3 participants