Skip to main content
Version: 8.8.x

Self-Managed Commerce 8.8.x Upgrade Notes

Upgrade notes​

The upgrading Elastic Path guide provides general instructions on upgrading Elastic Path projects. This page provides details about specific changes that may be required to your customizations when upgrading to Self-Managed Commerce 8.8.

Core changes​

Remove Java Security Manager usage​

All usage of the deprecated System.getSecurityManager() API has been removed from the Cortex API platform. This API was deprecated for removal in Java 17 and its continued use would result in build or runtime warnings, and eventual failures in future Java versions where the API is fully removed.

Customers who have customizations that reference the System.getSecurityManager() API should refactor away from use of this API.

Upgraded SLF4j from 1.7.x to 2.0.x and Logback from 1.2.x to 1.5.x​

SLF4j was upgraded from 1.7.x to 2.0.x and Logback from 1.2.x to 1.5.x. As part of this change, Log4j was also upgraded from 2.17.x to 2.24.x, and the Log4j-to-SLF4j bridge artifact was renamed from log4j-slf4j-impl to log4j-slf4j2-impl to align with the SLF4j 2.x API. The legacy log4j-over-slf4j and slf4j-log4j12 bridge artifacts have been removed as they are no longer needed.

Customers who have customizations that declare direct dependencies on any of these logging libraries should update their Maven POMs to reference the new versions and artifact names. Specifically, any references to log4j-slf4j-impl should be changed to log4j-slf4j2-impl. Custom OSGi bundles that import org.slf4j packages should also be updated to reference the 2.0.x version range. Additionally, note that Logback 1.3+ removes native JMX support; a custom JMX endpoint for logging has been introduced to maintain Query Analyzer functionality.

In the new Logback version, Janino (the Java compiler library for evaluating condition=isDefined(...) expressions) became an optional dependency — not bundled by default. Without Janino on the classpath, Logback silently skips <if condition='...'> blocks entirely. Therefore, customers with customized logback.xml configurations that use Logback if/then/else conditional blocks should migrate those configurations to use the property default syntax (e.g., ${propertyName:-defaultValue}) to ensure compatibility with Logback 1.5 and continued log output in deployed environments.

Replace deprecated ServiceMix dependencies with Elastic Path-maintained wrapped bundles​

Customers or partners who reference org.apache.servicemix.bundles artifacts directly in their Maven POMs or OSGi feature configurations must update those references to the corresponding com.elasticpath.wrappedbundles artifacts. Below is a list of the required transformations:

  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.aspectj → com.elasticpath.wrappedbundles:wrapped-aspectj
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.commons-collections → com.elasticpath.wrappedbundles:wrapped-commons-collections
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.ehcache → com.elasticpath.wrappedbundles:wrapped-ehcache
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-aop → com.elasticpath.wrappedbundles:wrapped-spring-aop
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-beans → com.elasticpath.wrappedbundles:wrapped-spring-beans
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-context → com.elasticpath.wrappedbundles:wrapped-spring-context
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-core → com.elasticpath.wrappedbundles:wrapped-spring-core
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-data-commons → com.elasticpath.wrappedbundles:wrapped-spring-data-commons
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-expression → com.elasticpath.wrappedbundles:wrapped-spring-expression
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-jdbc → com.elasticpath.wrappedbundles:wrapped-spring-jdbc
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-jms → com.elasticpath.wrappedbundles:wrapped-spring-jms
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-orm → com.elasticpath.wrappedbundles:wrapped-spring-orm
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-security-config → com.elasticpath.wrappedbundles:wrapped-spring-security-config
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-security-core → com.elasticpath.wrappedbundles:wrapped-spring-security-core
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-security-data → com.elasticpath.wrappedbundles:wrapped-spring-security-data
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-tx → com.elasticpath.wrappedbundles:wrapped-spring-tx
  • org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-web → com.elasticpath.wrappedbundles:wrapped-spring-web

The org.apache.servicemix.bundles:org.apache.servicemix.bundles.spring-context-support bundle is no longer needed and any references to this bundle can be removed.

Improved promotion category exclusion handling for products in overlapping category hierarchies​

When configuring a promotion condition such as "at least 1 item from category A, excluding items from subcategory B," products assigned to both the parent category A and the excluded subcategory B are now correctly treated as eligible for the promotion. Previously, such products were incorrectly excluded, preventing the promotion from applying even though the product was directly assigned to the intended included category. Products that belong to an excluded category that is not part of the included category hierarchy remain ineligible as expected.

This change affects promotion condition evaluation as well as CartCategoryAmountDiscountImpl and CartCategoryPercentDiscountImpl promotion actions, which now use the corrected exclusion logic via PromotionRuleDelegate#isProductInNotExcludedCategory. Merchants who have customized CartCategoryAmountDiscountImpl, CartCategoryPercentDiscountImpl, PromotionRuleDelegateImpl, or PromotionRuleExceptionsServiceImpl should review their customizations for compatibility with these changes. Additionally, the isInCategory method has been removed from the ProductLookup interface and its associated cache (isInCategoryCache) has been removed; any custom code referencing these should be updated accordingly.

Build now fails on duplicate POM dependency declarations​

The Maven build process has been enhanced to detect and fail on duplicate dependency version declarations within project POM files. Previously, Maven would only emit warnings when duplicate dependencies were found, which could silently threaten build stability and lead to unpredictable behavior. These warnings are now treated as errors, enforcing cleaner and more reliable dependency management. This is achieved by adding the banDuplicatePomDependencyVersions rule to the maven-enforcer-plugin configuration in the root POM, within a new execution phase called enforce-ban-on-duplicate-dependencies.

Customers who have customized their extensions POMs should audit their dependency declarations to ensure no duplicates exist before upgrading, as any pre-existing duplicate dependencies will cause the build to fail.

Job Pause Settings for "peak mode" management​

For customers with custom Quartz jobs, the pausable and critical job name lists (nonCriticalPausableJobNames and criticalJobNames) extensible lists should be reviewed. Custom jobs should be added to either list depending on whether it should be executed during peak transaction events or not. Jobs not specified in either list will not be paused when the job pause settings are activated.

Simplified EhCache Spring XML Configuration via custom ep-caches:cache namespace​

A new custom Spring XML namespace (ep-caches:cache) has been introduced to streamline the declaration of EhCache-backed caches.

You can optionally replace manually defined caches like this:

<bean id="taxCodeByCodeEhCache" class="com.elasticpath.commons.beanframework.EhCacheDefaultConfigurationFactoryBean">
<property name="name" value="taxCodeByCodeCache"/>
<property name="cacheManager" ref="epEhcacheManager"/>
<property name="timeToLive" value="3600"/>
<property name="timeToIdle" value="3600"/>
<property name="maxEntriesLocalHeap" value="10000"/>
</bean>
<bean id="taxCodeByCodeCache" factory-bean="localCacheFactory" factory-method="createCache" depends-on="taxCodeByCodeEhCache">
<constructor-arg value="taxCodeByCodeCache"/>
</bean>

With the simplified namespace like this:

<ep-caches:cache id="taxCodeByCodeCache" timeToLive="3600" timeToIdle="3600" maxEntries="10000"/>

For more information, see Cortex and Integration Server Application Caches.

As part of this change, the EhCacheDefaultConfigurationFactoryBean class has been relocated from com.elasticpath.commons.beanframework to com.elasticpath.cache.spring. Any custom Spring XML files or Java code that reference the old fully-qualified class name must be updated to use the new package.

Custom promotion conditions and actions now use extensible Spring autowiring​

Previously, adding custom promotion conditions or actions required overriding the entire ruleServiceLocal Spring bean definition to inject the allConditions and allActions lists. This was an invasive customization that risked breaking other functionality and made upgrades more difficult.

The allConditions and allActions lists in RuleServiceImpl are now autowired Spring lists. Custom promotion conditions and actions can be added simply by declaring a Spring bean of the appropriate type (RuleCondition or RuleAction), which will be automatically discovered and included without any need to redefine the core service bean.

Customers who previously overrode ruleServiceLocal solely to inject custom conditions or actions into these lists should remove that override and instead register their custom beans directly. Any explicit allConditions or allActions list definitions in extension Spring XML configuration (e.g., ext-service.xml) should also be removed, as the platform's built-in elements are no longer defined this way either.

Removed deprecated shopping cart deletion methods​

Two deprecated methods for directly deleting shopping carts — ShoppingCartService#deleteShoppingCartsByGuid and ShoppingCartService#deleteAllShoppingCartsByShopperUid — have been removed from the ShoppingCartService interface and its implementation. Shopping cart deletion is now exclusively managed by the PurgeCartsBatchProcessor, ensuring consistent and performant cart lifecycle management.

The associated named queries DELETE_SHOPPING_CART_BY_GUIDS, DELETE_SHOPPING_CARTS_BY_SHOPPER_UID, and DISABLE_PARENT_CHILD_CART_ITEM_RELATION_BY_SHOPPER_UID have also been removed. Any custom code invoking these methods must be updated to use the preferred deactivation equivalents: deactivateShoppingCartsByGuid and deactivateAllShoppingCartsByShopperUid.

Removed unused fields and methods from ShoppingCartImpl and ShoppingCart​

Several unused fields and methods have been removed from ShoppingCartImpl and the ShoppingCart interface as part of an ongoing effort to reduce technical debt and simplify the shopping cart API. The removed fields include codeValid, mergedNotification, itemWithNoTierOneFromWishList, giftCertificateService, and discountCalculator. The removed methods include isCodeValid, setCodeValid, isMergedNotification, setMergedNotification, hasItemWithNoTierOneFromWishList, setItemWithNoTierOneFromWishList, getSubtotalOfShippableItems, getCustomerEmailAddress, getCustomerBusinessNumber, and isCustomerAnonymous.

Associated logic that previously set mergedNotification during cart merges and itemWithNoTierOneFromWishList when adding wishlist items has also been removed from ShoppingCartMergerImpl and CartDirectorServiceImpl, as these flags were not being consumed anywhere in the platform.

Any custom code that references the removed fields or methods on ShoppingCart or ShoppingCartImpl — including subclasses, decorators, or direct callers — will need to be updated. If your customization relied on the merge notification or wishlist tier-one flag behavior, equivalent logic will need to be reimplemented independently.

Removed deprecated com.elasticpath.commons.util.Collections class​

The long-deprecated com.elasticpath.commons.util.Collections utility class has been removed from ep-core. This class previously provided emptySortedSet() and emptySortedMap() helper methods as a supplement to java.util.Collections.

Any custom code that references this class must be updated. Replace calls to Collections.emptySortedSet() with Guava's ImmutableSortedSet.of(), and replace calls to Collections.emptySortedMap() with Guava's ImmutableSortedMap.of().

Removed unused Greenmail test dependency from bill of materials and module POMs​

The unused greenmail (com.icegreen, version 1.3) test dependency has been removed from the platform's bill-of-materials and all module POMs where it was referenced, including ep-email, ep-test-application, and ep-core-fittests. This dependency was not actively used and its removal reduces unnecessary third-party library references, improving build cleanliness and reducing potential security exposure from unmaintained libraries.

Customizations that rely on greenmail being provided transitively through the bill-of-materials should explicitly declare this dependency in their own POMs if they still require it for testing purposes.

Removed unused PropertyService​

The PropertyService interface and its implementation PropertyServiceImpl have been removed from the commerce engine core, as they were no longer in use. The associated propertyService Spring bean definition and the ContextIdNames.PROPERTIES_SERVICE constant have also been deleted.

Customizations that reference com.elasticpath.service.misc.PropertyService, com.elasticpath.service.misc.impl.PropertyServiceImpl, or ContextIdNames.PROPERTIES_SERVICE will need to remove or replace those references. Any custom Spring configurations that depend on the propertyService bean should also be updated accordingly.

Removed unused fields from AbstractRuleEngineImpl​

Several unused fields have been removed from AbstractRuleEngineImpl: promotionRuleDelegate, ruleEngineSessionFactory, ruleEngineRuleStrategy, couponConfigDao, productSkuConverter, and shoppingCartConverter. Their corresponding getters, setters, and Spring bean injections have also been removed from the epRuleEngine bean definitions across all relevant Spring configuration files.

This is primarily a code cleanup change with no functional impact on promotion or rule engine behavior. However, merchants or developers who have customized the epRuleEngine Spring bean definition or extended AbstractRuleEngineImpl should review their configurations and remove any references to these now-deleted properties to avoid Spring initialization errors.

Replaced org.drools.core.util.StringUtils with org.apache.commons.lang3.StringUtils​

All platform usages of org.drools.core.util.StringUtils have been replaced with the standard org.apache.commons.lang3.StringUtils. This standardizes string utility usage across the codebase.

org.drools.core.util.StringUtils has also been added to the Checkstyle IllegalImport list, meaning any code — including customizations — that imports this class will now fail the Checkstyle build check. Customers with custom code that imports org.drools.core.util.StringUtils must update those imports to use org.apache.commons.lang3.StringUtils instead.

To assist with this migration, ast-grep upgrade recipes (PB-14031-drools-stringutils-to-commons-lang3.yml and PB-14031-drools-stringutils-to-commons-lang3-static.yml) have been provided under upgrade/recipes/8.8.x/ to automatically replace both regular and static imports in custom Java code when running the Self-Managed Commerce Upgrader Tool.

Improved Extensibility: Private methods and fields changed to protected​

A broad set of private methods, fields, and constants across the Elastic Path Commerce platform have been changed to protected visibility (or public where interface contracts or framework requirements demand it). New protected getters and setters have also been added for previously inaccessible members. The affected areas span core domain objects, services, search/indexing, checkout, pricing, tax calculation, promotions, coupon handling, shopping cart logic, and Commerce Manager UI components.

These changes significantly reduce the need for disruptive or fragile customization workarounds (such as reflection or full class replacement) when extending platform behavior. Customers who have already implemented such workarounds may be able to simplify or remove them in favor of standard subclassing or method overriding.

Customers with existing customizations should review their extensions against the changed classes — particularly ShoppingCartImpl, CheckoutServiceImpl, OrderFactoryImpl, OrderSkuFactoryImpl, TaxCalculationServiceImpl, TaxOperationServiceImpl, ElasticPathPromotionsCalculator, AbstractEpDialog, and the various Promotions UI editor and search tab classes — to determine whether simplifications are possible. Note that in some cases methods have been promoted to public visibility (e.g., OrderSkuImpl.getOrderUidPk(), CouponUsageModelDtoInsertUpdateImporterImpl.importCouponDtos(), and several Commerce Manager dialog and UI component methods), which may affect API compatibility if these classes were previously subclassed.

Maven Checkstyle Plugin upgraded to 3.6.0 (Checkstyle 9.3)​

The Maven Checkstyle plugin has been upgraded from version 2.17 to 3.6.0, which updates the underlying Checkstyle engine from version 6.11.2 to 9.3. This ensures that code quality checks are enforced using a modern, actively maintained version of the tooling, and resolves known issues present in older versions of the plugin.

Customers who have customized or extended Elastic Path's code-checking rules, or who have added their own Checkstyle configurations, should review their configurations for compatibility with Checkstyle 9.3. Some customized source code fixes may be required to comply with the updated rules, including whitespace corrections in varargs declarations (e.g., String... instead of String ...) and removal of redundant final modifiers on abstract method parameters.

Removed OWASP ESAPI dependencies​

All OWASP ESAPI dependencies (esapi, esapi-osgi-bundle) have been removed from the Self-Managed Commerce platform (ep-commerce and api-platform). Although ESAPI was included as a security library, it was not registered as a Servlet filter and was not actively protecting any Cortex API endpoints. Its presence only served to introduce a number of vulnerable transitive dependencies, including bsh-core, commons-configuration, commons-fileupload, and commons-lang.

Customizations that have a direct dependency on ESAPI libraries will need to be managed independently, as they are no longer provided by the platform.

New ShoppingItemDtoBuilder for simplified shopping item DTO construction​

A new ShoppingItemDtoBuilder interface and ShoppingItemDtoBuilderImpl implementation have been introduced to provide a cleaner, more extensible way to construct ShoppingItemDto objects. Previously, ShoppingItemDtoFactory exposed several overloaded methods that were difficult to extend and caused confusion. The new builder pattern replaces this with a fluent API supporting SKU code, quantity, item configuration, fields, parent GUID, shopping cart memento, and selection state.

The existing ShoppingItemDtoFactoryImpl has been refactored to use the new builder internally, so existing behavior is preserved. The builder can also be accessed directly via the static ShoppingItemDto.builder(bundleIdentifier, productSkuLookup) factory method.

Teams that have customized ShoppingItemDto or ShoppingItemDtoFactoryImpl should review their implementations. The ShoppingItemDtoBuilderImpl provides two extension points — createNewDto() for instantiating a custom DTO subclass, and getNewBuilder() for returning a custom builder instance — which should be overridden if a custom ShoppingItemDto subclass is in use.

Removed eagerly fetched customer role relationship​

Previously, every customer loaded from the database triggered a cascade of eager JPA fetches: from CustomerImpl to its set of CustomerGroupImpl objects, and then from each CustomerGroupImpl to its set of CustomerRoleImpl objects stored in the TCUSTOMERGROUPROLEX table. This resulted in a significant number of unnecessary database queries, as the only value ever stored in that table was the static string ROLE_CUSTOMER. The ROLE_CUSTOMER Spring Security authority is now hardcoded as a constant in CustomerImpl.getAuthorities(), eliminating the need for the database relationship entirely.

The TCUSTOMERGROUPROLEX table has been removed. The CustomerRole interface and CustomerRoleImpl class, along with the CustomerGroupService.removeAllRoles() method, have been deprecated and removed from active use. The customer_roles element in customer group import/export XML files is retained for backward compatibility but is now ignored during import and export operations. The CUSTOMER_ROLES dependent element type has been removed from the DependentElementType enum, and the CustomerGroupCollectionsStrategy class has been deleted.

Customizations that reference CustomerRoleImpl, CustomerGroupService.removeAllRoles(), the ContextIdNames.CUSTOMER_ROLE bean, or the customer_roles XML element in customer group data files should be reviewed as these are no longer functional. Any custom logic that depended on configurable per-group Spring Security authorities via TCUSTOMERGROUPROLEX will need to be reimplemented using an alternative approach.

Shopping cart item lookup optimized to avoid expensive recursive flattening​

ShoppingCartImpl has been updated to retrieve cart items directly from the flat list stored in the cart memento, replacing a recursive tree-flattening operation that was unnecessarily invoked when looking up items by identifier. This improves the performance of several shopping cart operations, including item lookup, item removal, shipment type evaluation, and discount clearing.

The signature of the protected method getAllShoppingItemsStream() has changed: it now accepts a boolean parameter (includingRemoved) that controls whether soft-deleted cart items are included in the stream. Any custom subclasses of ShoppingCartImpl that override this method will need to be updated to match the new signature.

Extension point framework output converter classes moved to a new package​

Several extension point framework converter classes that convert extension point entities back into domain objects have been relocated from the com.elasticpath.xpf.converters package to a new com.elasticpath.xpf.converters.output package. The affected classes are: StructuredErrorMessageConverter, StructuredErrorMessageTypeConverter, StructuredErrorResolutionConverter, OrderHoldConverter, InventoryDtoConverter, XPFInventoryExecutionResultConverter, PromotionActionToDiscountConverter, TaxJournalConverter, and TaxJournalConverterImpl.

If your customizations reference any of these classes directly — either through Java imports or Spring XML bean definitions — you must update those references to use the new com.elasticpath.xpf.converters.output package. Upgrade recipes using ast-grep are provided in upgrade/recipes/8.8.x/PB-13069-xpf-output-converters-package.yml to assist with automating these import changes across both Java and XML files.

Cortex changes​

Product SKU availability error messages now use ISO 8601 date format​

When a shopper attempts to add an item to their cart that is not yet available or is no longer available for purchase, the structured error message returned by the platform previously included dates formatted in a localized US short date format (e.g., 9/4/26 12:00 AM). These dates are now formatted using the ISO 8601 standard (e.g., 2026-09-04T00:00:00), providing a consistent, locale-independent representation.

This change affects the available-date field in item.not.yet.available errors and the expiry-date field in item.no.longer.available errors. Any integrations, customizations, or client applications that parse these date values from structured error message responses should be reviewed and updated to handle the new ISO 8601 format.

Commerce Manager changes​

Commerce Manager module customizations​

If your project includes copies of Commerce Manager modules under extensions/cm/ext-cm-modules - a common pattern used to gain access to ext-core classes - you will need to migrate these customizations before upgrading to Self-Managed Commerce 8.7. The upgraded version of Tycho included in Self-Managed Commerce 8.7 no longer permits multiple modules sharing the same artifact ID, and will fail at compile time with a "Duplicate reactor project IUs" error if these copied modules are present.

To resolve this, Self-Managed Commerce 8.8 now allows the standard commerce-manager/cm-modules/cm-plugins modules to access ext-core classes directly, eliminating the need for the copied modules entirely. You will need to migrate any customizations from your extensions/cm/ext-cm-modules modules into their corresponding commerce-manager/cm-modules/cm-plugins modules, then delete the copied modules. If you have accumulated significant customizations across many modules, this migration can be time-consuming to do manually. Therefore we recommend using an AI coding tool such as Claude Code to automate the process. You can use a prompt like the following:

This repo represents a customized version of Self-Managed Commerce. We are in the process of upgrading to version 8.7. In previous versions, Commerce Manager modules (in commerce-manager/cm-modules/cm-plugins) could not access ext-core classes. To work around this, some modules were copied into the extensions/cm/ext-cm-modules folder so they could access custom services in ext-core. However, the new version of Tycho in Self-Managed Commerce 8.7 no longer allows multiple modules with the same artifact ID — it causes a "Duplicate reactor project IUs" error during compilation. Self-Managed Commerce 8.7 now allows the platform modules to access ext-core classes directly. Please migrate the customizations in the extensions/cm/ext-cm-modules modules back into the corresponding commerce-manager/cm-modules/cm-plugins modules and delete the extensions/cm/ext-cm-modules modules. If there is no corresponding commerce-manager/cm-modules/cm-plugins module, leave it in extensions/cm/ext-cm-modules.

BaseConditionalExpressionAction replaced by AbstractConditionalExpressionAction​

The BaseConditionalExpressionAction class in Commerce Manager has been renamed to AbstractConditionalExpressionAction and converted to an abstract class. The placeholder getTargetIdentifier() method, which previously returned a hardcoded stub value ("!replace me on valid value!"), has been removed, enforcing that all subclasses provide their own valid implementation.

The built-in action classes — CreateConditionalExpressionAction, DeleteConditionalExpressionAction, and EditConditionalExpressionAction — have been updated to reflect this change. Customers with customizations that extend BaseConditionalExpressionAction must update their code to extend AbstractConditionalExpressionAction instead, and ensure a proper getTargetIdentifier() implementation is provided.

Replacement for Locale.getAvailableLocales()​

A newly created master catalog with all available languages selected could not be opened in Commerce Manager. Attempting to open the catalog resulted in an IllegalArgumentException and a persistence error, making the catalog inaccessible.

The root cause was that certain locales provided by the JVM (e.g., sr__#Latn) cannot be serialized to a string and then deserialized back to a Locale object using the Apache Commons LocaleUtils.toLocale() method, which is used internally for catalog locale persistence. A new LocaleUtils.getAvailableLocales() method has been added to com.elasticpath.commons.util.impl.LocaleUtils that filters out these incompatible locales and returns only those that can be safely round-tripped.

Customizations that call Locale.getAvailableLocales() directly - particularly in contexts where locales are persisted or compared against catalog-supported locales—should consider migrating to the new LocaleUtils.getAvailableLocales() method to avoid similar issues. Additionally, the return type of Catalog.getSupportedLocales() has been changed from Collection<Locale> to SortedSet<Locale>; any custom code referencing this method signature may require a minor update.

Batch Server changes​

Configurable Quartz batch job schedules via ep.properties​

Batch server Quartz job cron schedules can now be overridden in ep.properties without requiring modifications to quartz.xml. This gives operators greater flexibility to tune job timing to suit their operational needs, such as shifting resource-intensive cleanup jobs to off-peak hours.

The following ep.batch.job.<jobName>.cronExpression properties are now supported: releaseShipment, cleanupOrderLocks, cleanupExpiredOAuth2Tokens, cleanupAbandonedCarts, abandonedCartEvents, cleanupInactiveCarts, cleanupAnonymousCustomers, cleanupFailedOrders, cleanupOrphanedOrderPaymentInstruments, cleanupOrphanedOrderPaymentGuids, cleanupOrphanedPaymentInstruments, cleanupChangesets, expiredDataPointValuesJobProcessor, dataPointRevokedConsentsJobProcessor, relayOutboxMessages, cleanupCompletedOrders, and encryptCustomerPasswords. All existing default schedules are preserved when no override is set.

For more information, see Batch Server Properties.

Promotion recompilation and catalog promotion index updates now use database backed job tracking​

Previously, the Search Server used two filesystem-based properties files (ruleUpdate.properties and catalogPromo.properties) to track the last time promotion rules were compiled and the last time catalog promotion rules were checked for product index updates. In multi-node or multi-threaded environments, concurrent writes to these files could cause race conditions resulting in incorrect timestamps, leading to intermittent failures where some promotions were not recompiled after being edited. Additionally, these files could be erased during a deployment, causing unnecessary full recompilations or missed updates.

Both files have been replaced with a new TJOBEXECUTION database table, which stores the last execution date for each tracked job. The DBCompilingRuleEngineImpl and CatalogPromotionMonitor classes now read and write this state via the new JobExecutionService, eliminating filesystem dependencies and ensuring consistent behavior across all application nodes.

If you have customized DBCompilingRuleEngineImpl or CatalogPromotionMonitor, note that the propertiesDao dependency has been removed from both classes and replaced with jobExecutionService. Custom Spring bean definitions referencing propertiesDao on these beans should be updated accordingly.

Import/Export changes​

Improved Import/Export error and warning log messages​

Customers who have customized Import/Export controllers or who extend ImportAPIServiceImpl should ensure their implementations wire in the messageResolver bean (as introduced in the updated Spring configuration) to benefit from the logging improvement. Additionally, acceptance tests that previously asserted on raw error codes (e.g., IE-30402) have been updated to assert on resolved message text instead, so any similar custom test assertions should be updated accordingly.

Removed unused fields and bean definition from PricingExporterImpl​

The productService and productLoadTuner fields, along with their getter/setter methods, have been removed from PricingExporterImpl, as they were never used in the class's logic. The associated productTunerForPricing Spring bean definition, which was solely referenced by this exporter, has also been deleted from the import/export service configuration.

This is a code cleanup change with no functional impact on pricing export behavior. However, customers who have subclassed PricingExporterImpl or injected/referenced the productTunerForPricing bean in custom Spring configurations should remove those references to avoid startup errors or compilation issues after upgrading.

Deprecated Hamcrest dependencies replaced with consolidated library​

The hamcrest-core and hamcrest-library dependencies have been replaced throughout the platform with the single consolidated org.hamcrest:hamcrest 2.2 dependency. In Hamcrest 2.2, the older split artifacts contain no classes and exist only for backwards compatibility, so continuing to reference them could result in empty or conflicting dependencies on the classpath. The Hamcrest version used in the bill of materials has also been updated from 1.3 to 2.2.

The legacy hamcrest-core and hamcrest-library artifacts have been explicitly excluded from transitive dependencies pulled in by third-party libraries such as JUnit 4, JMock, Awaitility, REST Assured, WireMock, and Tempus Fugit. Additionally, all three legacy artifacts (hamcrest-all, hamcrest-core, and hamcrest-library) are now banned from the build and will cause a build failure if introduced.

Customers with customizations that declare a direct dependency on org.hamcrest:hamcrest-core or org.hamcrest:hamcrest-library should update those references to org.hamcrest:hamcrest. The deprecated @Factory annotation from the Hamcrest library has also been removed from custom matcher classes, and any custom matchers in extensions that use this annotation should be updated accordingly.

Test changes​

Selenium test framework: fix intermittent failures from nested retry calls​

Several page object methods in the Selenium test framework called the retryFunction() polling wrapper from inside another retryFunction() body. When the inner retry exhausted its attempts, the failure was wrapped in a timeout exception that the outer retry did not ignore, causing it to propagate immediately. As a result, composite multi-step interactions received only a single attempt instead of being retried as intended.

A new retryAttempts() method has been added as a safe primitive for inner retry logic. Unlike retryFunction(), it re-throws the original assertion error directly on exhaustion rather than wrapping it in a timeout exception, making it safe to call from within an outer retryFunction() body. The existing retryFunction() method now detects and explicitly forbids nested calls by throwing an IllegalStateException, turning a previously silent bug into a clearly reported error.

Customizations that extend AbstractPageObject or widget classes and make use of retryFunction() should be reviewed to ensure they do not call retryFunction() from inside another retryFunction() body. Any such nested usage should be updated to use the new retryAttempts() method for the inner retry logic.

Support for mocking sealed classes in Java 17 bytecode​

In Java 17, enum types are effectively sealed classes. This caused failures in unit tests that used Mockito to mock or spy on enum types (such as AllocationEventType), because Mockito's default mock maker works by creating subclasses, which is not permitted for sealed classes.

To resolve this, the mockito-inline dependency has been added to the bill of materials and to the ep-core module. The mockito-inline library uses Java instrumentation instead of subclassing, allowing Mockito to mock sealed and final classes without errors. This is a compatibility fix in preparation for upgrading to Java 17 bytecode, and does not affect runtime behaviour.

If you have custom modules that mock enums or other sealed classes in unit tests and encounter similar Mockito errors, you may need to add the mockito-inline dependency (scoped to test) to those modules as well.

Strict fatal JavaScript exception checking enabled by default in system tests​

System tests now fail immediately when a fatal JavaScript exception is detected, rather than treating it as a retryable error. This is controlled by the property ep.selenium.ignore.fatal.js.exceptions, whose default value has changed from true (lenient/ignore) to false (strict/fail-fast).

If your customized system tests rely on the previous lenient behavior, you can restore it by explicitly setting ep.selenium.ignore.fatal.js.exceptions=true in your test configuration. However, it is recommended to update affected tests to be compatible with strict mode, as lenient behavior may mask real defects caused by ambiguous element selectors or other JavaScript errors.

Fixed intermittent test failures caused by ambiguous widget selectors​

Automated tests for Commerce Manager could intermittently fail because the internal findRwtIdForSelector function used a browser query method that silently returns only the first matching element when multiple elements match a given selector. Depending on the order of test steps, a different element could be returned each time, producing unpredictable results.

The function has been updated to query for all matching elements and throw a descriptive error if more than one is found, making ambiguous selectors immediately visible rather than causing hard-to-diagnose flaky tests. Several element selectors used in the system tests — including those for the store list, product category table, shipping region dialog, and category wizard attributes — have also been made more specific so they reliably target the intended element without ambiguity.

If you have custom test definitions that define CSS selectors to match specific widgets, you should review your selectors to ensure they uniquely identify the intended element, as selectors matching multiple elements will now cause an explicit test failure.

Database changes​

Local Oracle database setup​

The reset-db step for local Oracle database setup previously failed with an ORA-12919: Can not drop the default permanent tablespace error. This occurred because the reset script incorrectly attempted to drop the USERS tablespace, which Oracle designates as the default permanent tablespace and does not allow to be dropped.

The Oracle reset script (oracle.sproc) has been reworked to instead drop and recreate the Pluggable Database (orclpdb1) from the Oracle Container Database root, then create dedicated tablespaces (COMMERCE and COMMERCETEMP) and the COMMERCEDB user within the Oracle Pluggable Database. The oracle-dev-db Maven profile in settings.xml and individual-settings.xml has also been updated with corrected default values to match this new approach.

Developers who have configured a local Oracle database should update their ~/.m2/settings.xml oracle-dev-db profile to use the new settings: username COMMERCEDB (replacing C##COMMERCEDB), tablespaces COMMERCE and COMMERCETEMP (replacing USERS and TEMP), and the new epdb.reset_databasename property set to ORCLCDB. Refer to the updated Oracle setup documentation for the full configuration.