Bamboo Data Center 13.0: Migrating from Apache Commons Configuration
We have an important API modernization in Bamboo 13.0 to share: as part of our ongoing work to improve security, platform stability, and developer experience, Bamboo no longer exposes the legacy Apache commons-configuration library as part of its public API. In its place, we introduced a Bamboo-owned Configuration API.
Background
For years, Bamboo exposed Apache Commons Configuration types such as HierarchicalConfiguration directly in its API surface. That created two problems.
- It tied Bamboo to an unsupported third-party library with unresolved vulnerabilities.
- It leaked implementation details into plugin contracts, making future platform upgrades harder than they needed to be.
What we considered
| Option | Description | Benefits | Trade-offs |
|---|---|---|---|
Keep Apache commons-configuration |
Retain the existing public API and defer the migration. | Lowest short-term change for plugin code. | Keeps an unsupported and vulnerable dependency in Bamboo’s API surface and preserves long-term lock-in. |
Move directly to commons-configuration2 |
Replace commons-configuration with commons-configuration2 as the public API. |
Moves to a supported upstream library. | Still a breaking change for plugin developers, keeps Bamboo coupled to a third-party API, and performed significantly worse in assessment. |
| Bamboo Configuration API SELECTED | Introduce Bamboo-owned configuration abstractions and migrate plugins to them. | Removes unsupported API exposure, reduces future lock-in, and gives Bamboo long-term ownership of the contract. | Requires plugin migration to Bamboo 13.0 APIs. |
Decision summary: Bamboo adopted a Bamboo-owned Configuration API because it solves the security and API-ownership problem directly, instead of replacing one externally exposed dependency with another.
Why not commons-configuration2?
At first glance, moving from Apache Commons Configuration to Apache Commons Configuration 2 may look like the obvious upgrade path. We chose not to make that the public-facing answer for three reasons.
- It is still a breaking change. Plugin developers would still need to update imports, construction patterns, and usage in their code. If a migration was already required, it made more sense to migrate to a stable Bamboo-owned API rather than another external library.
- Performance was significantly worse. In Bamboo’s assessment, the
commons-configuration2path performed materially worse, around 3x worse than the selected approach. - It would preserve the same architectural problem. Even on a newer library version, Bamboo would still be exposing third-party implementation details as part of its public contract.
Key takeaway: Upgrading to Configuration 2 would change the library version, but not the underlying problem of third-party API leakage.
Compatibility with Apache Configuration
The Bamboo Configuration API was designed to be highly practical for migration, while intentionally staying focused on the product’s real needs.
- Same XML format: Bamboo Configuration uses the same XML format as the previous implementation.
- Feature subset support: It supports the subset of configuration features that are actively used by the Bamboo codebase.
- No undocumented behavior emulation: It does not aim to reproduce undocumented or edge-case behavior from Apache Configuration.
- No XML attribute manipulation support: It deliberately skips implementing APIs for manipulating XML attributes.
- ImmutableConfiguration.getXXX(String property) methods do not throw: calling
getInt(String property)andgetBoolean(String property)on Apache Configuration object throws a runtime exception, if the property does not exist. At the same time, the old Bamboo wrapper classcom.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfigurationreturned a default value instead. We have opted for the latter behavior in our implementation and extended it to othergetXXX(String property)methods. We have also introducedgetXXXUnsafe(String property)counterparts to these methods if you need the Apache-like behavior instead.
In short, compatibility is intentionally strong where real Bamboo usage depends on it, but not expanded into full behavioral parity for rarely used or unspecified library behavior.
Migration
Many plugins, in particular, implementations of tasks, do not require any code changes. However, plugins manipulating plan configuration directly will have to be adapted.
The examples below show the most common migration patterns for plugin code moving to Bamboo 13.0.
1. Replace configuration types in plugin contracts
Update imports and method signatures to use com.atlassian.bamboo.configuration.Configuration.
// Before
import org.apache.commons.configuration.HierarchicalConfiguration;
import com.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfiguration;
@Override
public ErrorCollection validate(BuildConfiguration buildConfiguration) {
return new SimpleErrorCollection();
}
@Override
public MySpecsEntity toSpecsEntity(HierarchicalConfiguration buildConfiguration) {
// ...
}
// After
import com.atlassian.bamboo.configuration.Configuration;
@Override
public ErrorCollection validate(Configuration buildConfiguration) {
return new SimpleErrorCollection();
}
@Override
public MySpecsEntity toSpecsEntity(Configuration buildConfiguration) {
// ...
}
2. Read and write properties through Bamboo’s API
Use Bamboo’s configuration methods directly instead of building temporary Apache configuration trees and copying nodes.
import com.atlassian.bamboo.configuration.Configuration;
public void appendConfiguration(
Configuration configuration,
Map<String, Object> webContext,
boolean enabled) {
configuration.clearTree(PROPERTY_PREFIX);
configuration.setProperty(ENABLED_FOR_JOB, String.valueOf(enabled));
configuration.setProperty(DOCKER_IMAGE, webContext.get("dockerImage"));
configuration.setProperty(DOCKER_ARCHITECTURE, webContext.get("architecture"));
configuration.setProperty(DOCKER_IMAGE_SIZE, webContext.get("size"));
configuration.setProperty(DOCKER_AWS_ROLE, webContext.get("awsRole"));
configuration.setProperty(
DOCKER_EXTRA_CONTAINERS,
webContext.getOrDefault(DOCKER_EXTRA_CONTAINERS, "[]"));
}
3. Convert Bamboo configuration into a Specs entity
Read values from the Bamboo configuration object and map them into your plugin’s domain model.
import com.atlassian.bamboo.configuration.Configuration;
@Override
public PerBuildContainerForJob toSpecsEntity(Configuration buildConfiguration) {
String enabled = buildConfiguration.getString(ENABLED_FOR_JOB, "false");
Map<String, String> values = new HashMap<>();
values.put(ENABLED_FOR_JOB, enabled);
values.put(DOCKER_IMAGE, buildConfiguration.getString(DOCKER_IMAGE));
values.put(DOCKER_IMAGE_SIZE,
buildConfiguration.getString(DOCKER_IMAGE_SIZE, "REGULAR"));
values.put(DOCKER_AWS_ROLE, buildConfiguration.getString(DOCKER_AWS_ROLE));
return createSpecsEntity(values);
}
4. Use BambooConfigurationImpl in tests
Tests should validate behavior and stored values rather than asserting Apache implementation types.
import com.atlassian.bamboo.configuration.Configuration;
import com.atlassian.bamboo.configuration.impl.BambooConfigurationImpl;
private Configuration createBuildConfiguration() {
Configuration configuration = new BambooConfigurationImpl();
configuration.setProperty(ENABLED_FOR_JOB, "true");
configuration.setProperty(DOCKER_IMAGE, "abc123");
configuration.setProperty(DOCKER_IMAGE_SIZE, "LARGE_4X");
return configuration;
}
@Test
void addToBuildConfiguration() {
Configuration configuration = new BambooConfigurationImpl();
buildProcessor.addToBuildConfiguration(specsProperties, configuration);
assertEquals(true, configuration.getProperty(ENABLED_FOR_JOB));
assertEquals("abc123", configuration.getProperty(DOCKER_IMAGE));
assertEquals("LARGE_4X", configuration.getProperty(DOCKER_IMAGE_SIZE));
}
5. Remove obsolete lifecycle validation
If your implementation still contains CustomPreBuildAction.validate(), it can be removed because Bamboo does not invoke it.
// Remove these imports
import com.atlassian.bamboo.utils.error.ErrorCollection;
import com.atlassian.bamboo.utils.error.SimpleErrorCollection;
import com.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfiguration;
// Remove this method
@Override
public ErrorCollection validate(BuildConfiguration configuration) {
return new SimpleErrorCollection();
}
// Keep lifecycle methods Bamboo actually invokes
@Override
public void init(@NotNull BuildContext buildContext) {
this.buildContext = buildContext;
}
Migration tips
- Search your codebase for imports from
org.apache.commons.configurationorcom.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfigurationand replace them with Bamboo configuration types. - Review code that manually creates or copies configuration trees. Those call sites usually need the most change.
- Focus first on serialization, deserialization, and Specs mapping code, because that is where Apache-specific patterns tend to be concentrated.
- Treat configuration objects as Bamboo-owned abstractions, not as wrappers around a specific implementation.
- Compile and test against Bamboo 13.0 release build appropriate for your plugin support policy.
What this means going forward
This change is part of a broader Bamboo 13.0 modernization effort. By owning the configuration contract, Bamboo can improve internals without repeatedly forcing plugin developers through third-party API migrations. That is better for product security, better for long-term maintainability, and better for the ecosystem as a whole.