Skip to main content

Vendor a Dependency, Then Give It Back

· 5 min read
Alejandro Revilla
jPOS project founder
AR Agent
AI assistant

We just released version 0.0.20 of the jPOS Gradle Plugin. It adds two small tasks with a deliberately narrow purpose: vendor and unvendor.

They let you bring a dependency's source into your project temporarily, make a local change, and then cleanly return to the published dependency.

Vendor a dependency

Suppose a project uses jposee_txn or jposee_saf, and an urgent production issue calls for a little more logging, a diagnostic change, or a short-lived workaround. With 0.0.20:

./gradlew vendor --lib jposee_txn

Here, jposee_txn is a library key from the [libraries] section of gradle/libs.versions.toml. The plugin resolves that dependency, extracts its -sources.jar, and creates a local Gradle subproject:

vendor/jposee_txn/
├── build.gradle
├── .vendored
└── src/main/{java,resources}/...

It also adds the project to settings.gradle and redirects the original Maven coordinate to :vendor:jposee_txn through Gradle dependency substitution.

The important part is what it does not do: it does not change your build.gradle or version catalog. Your dependency declaration remains the same. Resolution simply uses your local copy while it exists.

note

Vendoring is not limited to jPOS modules. Any dependency listed in your project's version catalog can be vendored. That makes it a useful debugging tool: inspect the exact source being used, add instrumentation, place breakpoints, or test a focused hypothesis without first creating and publishing a fork.

The substitution is applied at resolution time, so it catches both direct and transitive uses of the same module. If another dependency brings in the module, it resolves to the same local project. Vendored modules that depend on one another are wired together as local projects too.

Make the smallest useful change

A good vendoring session is intentionally short:

./gradlew vendor --lib jposee_saf
# inspect, instrument, or patch vendor/jposee_saf
./gradlew test

The generated build.gradle carries the module's group, version, repositories, and dependencies from its POM. That gives the local source enough context to build with the application.

note

Vendoring is an escape hatch, not a development model. It is the dependency equivalent of forking an open-source project: sometimes necessary, but rarely good for the community—or for a small project that must carry its own private divergence indefinitely.

Use it to restore service, prove a fix, or understand a problem. Then contact us with the use case. The best outcome is usually a generic upstream capability that solves the real problem for everyone.

This matters especially for jPOS-related modules. A local patch to jposee_txn may get an urgent deployment moving; the conversation that follows may reveal a missing extension point, configuration option, or observable event that belongs upstream. Once the generic solution is available and tested, update to it and remove the temporary copy.

Return to the published module

When the local copy is no longer needed:

./gradlew unvendor --lib jposee_txn

Or remove every vendored module:

./gradlew unvendor

unvendor removes the local vendor/<name> directory and its corresponding settings.gradle include. The original dependency declaration was never changed, so the build immediately returns to the published artifact.

The task protects work you may still need: it records a content digest when it vendors a module, and refuses to delete a copy that has been modified. After preserving or reverting the local patch, unvendor it. If you truly intend to discard it, remove the directory yourself.

Re-sync the IDE—or run any Gradle task—after either operation so it sees the updated project structure and dependency substitution.

Vendoring is not adopting

There is an important distinction here.

Vendoring says: “I need this upstream module locally for now, but I want to remain connected to its vendor and return to it.”

Adopting says: “This is now our module. We will own its roadmap, maintenance, testing, releases, and compatibility from here on.”

note

Adoption is a conscious fork of responsibility. It is not an unvendor variant and it is not automated by the plugin. If you mean to take permanent ownership, manually move the vendored module into your real modules/ directory, wire it into the build as one of your own modules, and remove the vendoring wiring. At that point, the upstream relationship is no longer the project's dependency-management mechanism—it is simply a source of ideas or future merges.

That is a legitimate choice when the business genuinely needs a permanently specialized component. But it should be made with clear eyes. In most cases, a temporary vendor patch followed by an upstream conversation is cheaper, safer, and better for everyone.

One practical caveat

A -sources.jar contains source, not necessarily everything used to produce the published artifact. Modules that depend on annotation processing or generated code may need manual attention in the local subproject.

That limitation is another reason to keep vendoring focused: change what you need, verify it in the consuming application, contribute the general solution upstream when possible, and unvendor as soon as the published module can do the job again.

Upgrade

plugins {
id 'org.jpos.jposapp' version '0.0.20'
}

Version 0.0.20 gives you a controlled way to look inside a dependency when time matters—without pretending that a local fork is the final destination.