Bndtools Resolve vs Command line

Wondering what magic bndtools uses to get get it’s reolve of bndrun files so fast. In bndtools the resolve takes 15 seconds max on complex resolves, On the command line 2+ minutes. Is there some caching or flags I am missing?

Hard to tell from the outside. Could you share which bnd resolve CLI command you use?
The docs mentions “bndrun files” (plural) at the top… so maybe CLI is processing multiple?

Are Eclipse bndtools version and bnd CLI version the same? (what’s the output of bnd version on CLI?)

I am just using a little program. Versions are the same

var run = Bndrun.createBndrun(null,runFile);
run.getModel().setRunBundles(new java.util.ArrayList())
String res = null;
try
{
res = run.resolve(true,false);
}

10-15 seconds in eclipse
2 minutes standalone

Root Cause Analysis: Eclipse (10-15s) vs. Standalone (2 minutes)

The performance gap is not caused by a bug or algorithmic difference in the resolver itself. It comes from two compounding differences in how repositories are initialized and how their indexes are loaded between the two environments.

1. Repository Index Already Loaded In-Memory (Eclipse)

In Eclipse/Bndtools, the Workspace is a long-lived singleton that is created once when the IDE starts. All configured OSGiRepository (or other Repository) plugins are already initialized and their indexes are already parsed and cached in-memory as live Java objects.

When you call run.resolve(...) inside Eclipse, BndrunResolveContext.init() calls loadRepositories()getAllRepos(), which retrieves already-initialized, in-memory repository objects from the shared workspace. The index data (the full OSGi resource/capability/requirement graph) is already in RAM and the BridgeRepository Promise is already resolved.

In standalone mode (Bndrun.createBndrun(null, runFile)), a brand-new Workspace is created via Workspace.createStandaloneWorkspace(...) and ws.open(). Every OSGiRepository plugin starts with inited = false. The very first call to findProviders() (during resolver.resolve(rc)) triggers OSGiRepository.prepare(), which:

  • Creates a new OSGiIndex
  • Calls readIndexes(false), which downloads all index XMLs via HttpClient
  • Parses potentially large OBR/R5 index XML files
  • Builds the full ResourcesRepository and BridgeRepository

All this happens synchronously at resolve time (the Promise is awaited via .getValue() inside getBridge()), taking most of the 2 minutes.

2. HTTP-Level Index Cache Is Not Warm (Standalone)

HttpClient defaults its URL cache to Home.getUserHomeBnd("urlcache") — a shared disk cache. However:

  • The OSGiIndex.download() call uses client.build().useCache(staleTime) where staleTime defaults to YEAR (365 days). So once the HTTP cache is warm, subsequent standalone runs should be fast too.
  • The first run (or any run after the cache expires or is cleared) must fetch index files from the network. For a remote OBR repository with a large XML index, this can easily add tens of seconds to minutes depending on the size and network latency.
  • In Eclipse, the same HttpClient instance has been running for hours/days. Its URL cache is always warm for the index files, so it serves them from the local disk cache with an HTTP 304/UNMODIFIED response.

3. Plugin/Workspace Initialization Overhead (Standalone)

Workspace.createStandaloneWorkspace() + ws.open() initializes the full plugin infrastructure from scratch:

  • Loads and instantiates all -plugin entries from properties
  • Initializes HttpClient, reads connection settings
  • Creates ResourceRepositoryImpl, CachedFileRepo, etc.
  • If -standalone points to remote repository URIs, OSGiRepository.prepare() is called for each one during getPlugins(Repository.class) (triggered by the Prepare interface and initRepositories())

In Eclipse, the workspace is initialized once at IDE startup, amortized over all operations done during the session.

Summary Table

Factor Eclipse Standalone
Workspace already initialized :white_check_mark: Yes (live since IDE start) :cross_mark: No (created fresh per call)
Repositories initialized in memory :white_check_mark: Yes :cross_mark: No (initialized on demand at resolve time)
Repository index XML parsed :white_check_mark: Already done :cross_mark: Done during resolve() call
HTTP index cache state :white_check_mark: Warm (Eclipse ran earlier) :cross_mark: Cold (first run), or warm after first run
OSGi index in-memory :white_check_mark: BridgeRepository Promise already resolved :cross_mark: Promise resolved during resolve, blocking

Mitigation

If you need the standalone call to be consistently fast, you can:

  1. Reuse the Workspace across multiple Bndrun.resolve() calls rather than recreating it each time — this amortizes the initialization cost.
  2. Ensure the HTTP URL cache is warm before resolving (the ~/.bnd/urlcache/ directory). After the first standalone run the index files will be cached on disk and subsequent runs will be much faster.
  3. Set -offline: true or -gestalt: offline in the bndrun file to prevent re-checking remote indexes (relies entirely on disk cache).
  4. Use the cache: config attribute in -standalone or -plugin.repo to point the OSGiRepository cache to a fixed local directory that persists across JVM invocations.
  5. Consider setting max_stale explicitly to a large value so the cached index is used without re-validation for longer.