dotCMS is continuously evolving to stay secure, modern, and maintainable. One of the most significant recent changes involves our migration away from Elasticsearch toward OpenSearch 3.0 as our index provider. This transition required deep refactoring inside our core, and if you maintain custom OSGi plugins that interact with the indexing layer, this post is for you.
Why Is This Happening?
Elasticsearch Has Reached End of Life
Elasticsearch 7.x — the version dotCMS historically shipped with — reached full end of life on January 15, 2026. With EOL comes a halt in security patches, leaving any system running it exposed to known vulnerabilities. Indeed, Elasticsearch 7.x carries multiple documented CVEs, including authentication bypass issues (Field-Level Security), memory disclosure bugs in the 7.10–7.13 range, and denial-of-service vulnerabilities via the Grok parser.
Beyond security, Elastic changed its licensing model in 2021, moving away from the open Apache 2.0 license to the Server Side Public License (SSPL) and its own proprietary Elastic License, neither of which are OSI-approved open-source licenses. This has long-term implications for any platform that bundles Elasticsearch as a core dependency.
The Move to OpenSearch 3.0
OpenSearch, the community-driven fork of Elasticsearch maintained under the Apache 2.0 license, released OpenSearch 3.0 in December 2025. It brings major performance improvements (up to 9.5× over earlier versions), modern ingestion capabilities, and gRPC support — making it the right foundation for dotCMS going forward.
This comes at the perfect time; we are currently in the process of deprecating our dependency on Java 11 in favor of Java 25. This modernization can be seen as a direct continuation of that one, which we discussed at length in a December blog post.
Paying Technical Debt While Modernizing
The migration to OpenSearch 3.0 is also an opportunity to pay down years of technical debt. Our core was tightly coupled to Elasticsearch-specific types — types that don't exist in OpenSearch or any other index provider. To support OpenSearch 3.0 (and potentially other providers in parallel), we introduced a provider-neutral domain layer for all indexing abstractions.
This is a compile-time change. If your plugin references the old Elasticsearch-specific classes directly, it will fail to load at runtime against dotEvergreen.
Is Your Plugin Affected?
Your plugin is affected if it imports any classes from the org.elasticsearch.* namespace or calls deprecated dotCMS APIs tied to Elasticsearch, such as APILocator.getESIndexAPI().
The first sign you will see — if you deploy an unmodified plugin — is a runtime crash like this:
Caused by: java.lang.NoSuchMethodError:
'com.dotcms.content.elasticsearch.business.ESIndexAPI
com.dotmarketing.business.APILocator.getESIndexAPI()'This error means the method signature no longer exists. The class ESIndexAPI and the getESIndexAPI() accessor have been removed. This change was introduced starting with dotCMS version 26.03.06-02. We recommend always building against the latest available release.
The fix requires recompiling your plugin against the new dotCMS core API. Here is how to do that.
Step 1: Configure Your Maven Project
dotCMS core is built with Maven, and our artifacts are published to our Artifactory repository. While you can use other build tools, Maven is strongly recommended for OSGi plugin development as it integrates cleanly with the maven-bundle-plugin (Apache Felix) that generates the OSGi bundle manifest.
Minimum pom.xml skeleton
Below is a clean, ready-to-use skeleton for a dotCMS OSGi plugin project. Replace the groupId, artifactId, and bundle metadata with your own values.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- ===== Project coordinates — adjust to your plugin ===== -->
<groupId>com.example.dotcms.plugin</groupId>
<artifactId>my-OSGi-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<properties>
<bundle.version>1.0.0</bundle.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<!-- Always target the latest dotCMS release -->
<dotcms-core.version>26.03.20-01</dotcms-core.version>
</properties>
<!-- ===== dotCMS Artifactory repository ===== -->
<repositories>
<repository>
<id>dotcms-repo</id>
<url>https://artifactory.dotcms.cloud/artifactory/libs-release</url>
</repository>
</repositories>
<dependencies>
<!-- dotCMS core — provided at runtime by the container, compile-only -->
<dependency>
<groupId>com.dotcms</groupId>
<artifactId>dotcms-core</artifactId>
<version>${dotcms-core.version}</version>
<scope>provided</scope>
</dependency>
<!--
Add your own compile/runtime dependencies here.
Use scope=compile for libs that must be embedded in the bundle.
-->
</dependencies>
<build>
<plugins>
<!-- OSGi bundle packaging via Apache Felix -->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>5.1.9</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Vendor>Your Company Name</Bundle-Vendor>
<Bundle-Name>My OSGi Plugin</Bundle-Name>
<Bundle-Version>${bundle.version}</Bundle-Version>
<Bundle-Description>Short description of what this plugin does</Bundle-Description>
<Bundle-Activator>com.example.dotcms.plugin.Activator</Bundle-Activator>
<Export-Package>com.example.dotcms.plugin</Export-Package>
<Import-Package>*;resolution:=optional</Import-Package>
<!-- Embed all compile-scoped dependencies into the bundle JAR -->
<Embed-Dependency>*;scope=compile|runtime;inline=true</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Note: dotcms-core must be declared with <scope>provided</scope>. It is available in the dotCMS runtime environment and must not be embedded inside your bundle JAR.
Build your plugin
mvn clean packageStep 2: Expect and Fix Compilation Errors
Once you update your dotcms-core.version to the latest release and run mvn compile, you will likely see errors like these:
[ERROR] COMPILATION ERROR :
[ERROR] .../Indexer.java:[102,49]
incompatible types:
com.dotcms.content.index.IndexAPI
cannot be converted to
com.dotcms.content.elasticsearch.business.ESIndexAPI
[ERROR] .../Indexer.java:[377,66]
incompatible types:
com.dotcms.content.index.domain.CreateIndexStatus
cannot be converted to
org.elasticsearch.client.indices.CreateIndexResponseThese are your migration checklist. Each compilation error pinpoints exactly where an Elasticsearch-specific type needs to be replaced with its dotCMS domain equivalent. This is by design; the compiler is your guide.
Step 3: Update Your Imports and Types
The New Namespace
All provider-neutral indexing abstractions now live under:
com.dotcms.content.index.domainThe class names were intentionally kept as close as possible to the originals, so mapping old types to new ones is straightforward. Here are the most common substitutions:
Before (Elasticsearch-specific) | After (dotCMS domain-neutral) |
|---|---|
org.elasticsearch.client.indices.CreateIndexResponse | com.dotcms.content.index.domain.CreateIndexStatus |
com.dotcms.content.elasticsearch.business.ESIndexAPI | com.dotcms.content.index.IndexAPI |
APILocator.getESIndexAPI() | APILocator.getIndexAPI() |
Example: Updating an Index Operation
Before:
import org.elasticsearch.client.indices.CreateIndexResponse;
import com.dotcms.content.elasticsearch.business.ESIndexAPI;
// ...
ESIndexAPI indexAPI = APILocator.getESIndexAPI();
CreateIndexResponse response = indexAPI.createIndex(indexName, settings);
if (response.isAcknowledged()) {
// index was created
}After:
import com.dotcms.content.index.domain.CreateIndexStatus;
import com.dotcms.content.index.IndexAPI;
// ...
IndexAPI indexAPI = APILocator.getIndexAPI();
CreateIndexStatus status = indexAPI.createIndex(indexName, settings);
if (status.isAcknowledged()) {
// index was created
}The method semantics remain the same; only the types change.
Step 4: Test Against dotEvergreen
After resolving all compilation errors, deploy your rebuilt bundle to a dotEvergreen instance and verify:
The plugin activates without errors in the OSGi console.
All indexing-related functionality behaves as expected.
No NoSuchMethodError or ClassNotFoundException appears in the logs.
A Note From the dotCMS Team
We want to be transparent: This is a breaking change that we are introducing, and we understand it creates work on your end. We don't take that lightly.
This refactoring is part of a necessary and long-overdue effort to modernize dotCMS's core, remove dependencies on end-of-life software, and build a more secure and sustainable platform for everyone. We know that "necessary" doesn't make it painless, and we genuinely appreciate your patience and understanding as we work through this transition together.
We are committed to making the migration path as clear as possible — and we are here to help every step of the way.
What Has Already Changed
The following classes have had their method signatures updated as of 26.03.06-02:
com.dotcms.content.elasticsearch.business.ESMappingAPIImpl
com.dotmarketing.portlets.contentlet.business.ContentletFactory
com.dotcms.content.elasticsearch.business.ContentletIndexAPI
What's Coming Next
The refactoring is ongoing. The following classes are expected to have signature changes in upcoming releases; we recommend keeping an eye on the release notes if your plugin interacts with any of them:
ESIndexAPI
ESContentFactoryImpl
ESMappingAPIImpl
ContentletIndexAPIImpl
ReindexThread
PermissionBitFactoryImpl
ESSearchAPIImpl
ESSiteSearchAPI
ESContentletAPIImpl
ContentTypeAPIImpl
ClusterUtil
ESContentletScrollImpl
This list will be updated as changes are confirmed. When in doubt, compile early and compile often against the latest dotCMS release; the compiler will tell you exactly what needs attention.
Summary
What changed | Where | Since version |
|---|---|---|
APILocator.getESIndexAPI() removed | com.dotmarketing.business.APILocator | 26.03.06-02 |
ESIndexAPI removed | com.dotcms.content.elasticsearch.business | 26.03.06-02 |
New IndexAPI introduced | com.dotcms.content.index | 26.03.06-02 |
New domain objects | com.dotcms.content.index.domain | 26.03.06-02 |
The bottom line: Update your dotcms-core dependency version, compile, follow the compiler errors, replace each Elasticsearch-specific type with its com.dotcms.content.index.domain equivalent, and you are done.
If you run into issues or have questions about specific API mappings, reach out to dotCMS Support — we're here to help.
dotCMS Engineering Team