Core Extensions
Extension Modules
The extensions
folder of the Elastic Path Commerce source code contains several modules that allow project teams to extend and override core functionality. The most commonly-used modules are described below.
batch/ext-batch
Use the batch/ext-batch
module to define extensions of ep-batch
module classes. This module is used by the Batch Server Webapp.
cm/ext-cm-libs
Use the cm/ext-cm-libs
module to define extensions of com.elasticpath.cmclient.core
module classes. This module is an OSGI "buddy" class of the com.elasticpath.cmclient.core
module, and also contains a dependency on ext-core
.
core/ext-core
Use the core/ext-core
module to customize and extend core functionalities. This module can include extensions to classes in ep-core
as well as modules that ep-core
depends on, such as ep-settings
or ep-persistence-openjpa
. It can also include new functionalities required for other extension modules.
This module contains several example classes and other files that can be used as a reference:
com.elasticpath.extensions.domain.Example
- An example JPA domain entity interface.com.elasticpath.extensions.domain.impl.ExampleImpl
- An example JPA domain entity class.com.elasticpath.extensions.service.ExampleService
- An example domain service interface.com.elasticpath.extensions.service.impl.ExampleServiceImpl
- An example domain service class.src/main/resources/META-INF/example-orm.xml
- An example JPA named query definition XML.
This module has the following structure:
ext-core
src
main
java
com
java
// Extension classes go here
resources
META-INF
conf
ep-core-plugin.xml // Spring definitions and overrides
jpa-persistence.xml // OpenJPA persistence additions and overrides
OSGI-INF
blueprint
ext-core-blueprint.xml // OSGi blueprint descriptor file
pom.xml
core/ext-core-caching
Use the core/ext-core-caching
module to define extensions of ep-core-caching
module classes. This allows project teams to define caching implementations for custom service classes.
This module contains several example classes and other files that can be used as a reference:
com.elasticpath.extensions.caching.core.example.impl.CachingExampleServiceImpl
- An example caching implementation for a domain service class.src/main/resources/spring/core-caching/ext-cache-example.xml
- An example Spring XML that defines the caches and bean definitions.
core/ext-core-itests
Use the core/ext-core-itests
module to define integration tests for custom service classes.
This module contains an example integration test class that can be used as a reference:
com.elasticpath.extensions.service.example.ExampleServiceIntegrationTest
- An example integration test for a domain service class.
data-population/ext-data-population-core
Use the data-population/ext-data-population-core
module to define extensions of ep-data-population-core
module classes. This module is used by the Data Population tool.
importexport/ext-importexport
Use the importexport/ext-importexport
module to define extensions of ep-importexport
module classes. This module provides Import/Export functionality that is used by the Integration Server and the Import/Export CLI tool.
For more information, see our expert blog post about how to extend Import/Export classes.
search/ext-search
Use the search/ext-search
module to define extensions of ep-search
module classes. This module is used by the Search Server Webapp.
sync/ext-sync
Use the sync/ext-sync
module to define extensions of ep-sync
module classes. This module is used by the Data Sync Webapp.
Extensible Enums
Most enum classes in the Elastic Path source code are built as extensible enums. They extend the AbstractExtensibleEnum
class, rather than using the standard Java enum keyword. You can create customizations to add additional values to these enums without modifying the platform source code.
To add a new constant to an existing extensible enum class, create a new class in a module within the extensions
folder which extends the original class.
note
This is usually in ext-core
if the original class is in core
.
Create a new class in a module within the
extensions
folder which extends the original class. For example, to add a newORDER_FRAUDULENT
enum value to the order event types, extend theOrderEventType
class and add a newExtOrderEventType
class:package com.elasticpath.extensions.core.messaging.order; import com.elasticpath.core.messaging.order.OrderEventType; /** * Extensions class for {@link OrderEventType}. */ public class ExtOrderEventType extends OrderEventType { private static final long serialVersionUID = 4025398581967246417L; /** Ordinal constant for ORDER FRAUDULENT. */ public static final int ORDER_FRAUDULENT_ORDINAL = 23; /** * Signals that the order is fraudulent. */ public static final ExtOrderEventType ORDER_FRAUDULENT = new ExtOrderEventType(ORDER_FRAUDULENT_ORDINAL, "ORDER_FRAUDULENT"); /** * Create an enum value for a enum type. Name is case-insensitive and will be stored in upper-case. * * @param ordinal the ordinal value * @param name the name value (this will be converted to upper-case). */ protected ExtOrderEventType(final int ordinal, final String name) { super(ordinal, name); } }
Add a reference to the extension class to the
enumLoader
bean in theep-core-plugin.xml
file. The following example referencesExtOrderEventType
:
<bean id="enumLoader" class="com.elasticpath.extensions.commons.EnumLoader"
init-method="init">
<property name="enums">
<util:list>
<value type="java.lang.Class">com.elasticpath.extensions.core.messaging.order.ExtOrderEventType</value>
</util:list>
</property>
</bean>
Extensible Spring Lists
The Elastic Path Spring XML definitions often contain lists of beans that can be injected into the setters of other beans. An example of this is the validation strategy definitions that exist in the validation-strategies.xml
file. These lists are defined as extensible lists, so you can add or remove values from these lists without redefining the entire list. Instead, you can use the extensibleList:modify
custom Spring element to modify the list.
The following example shows how to identify an extensible list through its use of the extensibleList:create
custom Spring element:
<extensibleList:create id="productSkuValidatorListParent" overridableId="productSkuValidatorList"
valueType="com.elasticpath.service.shoppingcart.validation.ProductSkuValidator">
<ref bean="productSkuDatesValidator"/>
<ref bean="inventoryProductSkuValidator"/>
<ref bean="visibleProductSkuValidator"/>
<ref bean="inCatalogProductSkuValidator"/>
<ref bean="priceExistsProductSkuValidator"/>
<ref bean="soldSeparatelyProductSkuValidator"/>
<ref bean="autoSelectableBundleConstituentDelegateValidator"/>
</extensibleList:create>
To remove an entry from this list, do the following:
- Specify the following Spring XML in a module of your
extensions
folder:
<extensibleList:modify id="productSkuValidatorList" parent="productSkuValidatorListParent">
<extensibleList:removeFromList>
<ref bean="inventoryProductSkuValidator"/>
</extensibleList:removeFromList>
</extensibleList:modify>
The preceding definition removes the inventoryProductSkuValidator
from the productSkuValidatorList
. Note the value of the parent
attribute matches the value of the id
attribute from the original extensible list definition.
- Specify the following Spring XML in a module of your
extensions
folder:
<extensibleList:modify id="productSkuValidatorList" parent="productSkuValidatorListParent">
<extensibleList:addToList>
<ref bean="custom1ProductSkuValidator"/>
<ref bean="custom2ProductSkuValidator"/>
</extensibleList:addToList>
</extensibleList:modify>
The preceding definition adds custom1ProductSkuValidator
and custom2ProductSkuValidator
to the productSkuValidatorList
. Note that the value of the parent
attribute matches the value of the id
attribute from the original extensible list definition.
- Specify the following Spring XML in a module of your
extensions
folder:
<extensibleList:modify id="productSkuValidatorList" parent="productSkuValidatorListParent">
<extensibleList:removeFromList>
<ref bean="inventoryProductSkuValidator"/>
</extensibleList:removeFromList>
<extensibleList:addToList>
<ref bean="custom1ProductSkuValidator"/>
<ref bean="custom2ProductSkuValidator"/>
</extensibleList:addToList>
</extensibleList:modify>
OSGi configuration
Cortex runs in an OSGi container. Any library that Cortex uses must be packaged as an OSGi bundle. This section describes how ext-core
is configured for OSGi.
pom.xml
Extension The key OSGi configuration is highlighted in this snippet from the core extension’s pom.xml
:
<artifactId>ext-core</artifactId>
<packaging>bundle</packaging>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<!-- Fragment-Host gives the symbolic name of the core wrapper bundle to attach to. -->
<Fragment-Host>ext-commerce-engine-wrapper</Fragment-Host>
<Import-Package>
!*
</Import-Package>
...
</instructions>
</configuration>
</plugin>
<packaging>bundle</packaging>
The first OSGi configuration data in the POM
is the project’s packaging <packaging>bundle</packaging>
data. With this setting, Maven builds the project as an OSGi bundle. The project still builds into a JAR file, so the project can be used outside of an OSGi setting, which means you can leave this as is even if you are not using the core extension in an OSGi setting.
<artifactId>maven-bundle-plugin</artifactId>
The maven-bundle-plugin
configuration transforms the core extension into a OSGi fragment and tells the fragment to attach to Cortex’s ext-commerce-engine-wrapper
bundle. The extension commerce engine wrapper bundle wraps the core extension in an OSGi bundle. Fragments are treated as part of the host bundles. Relevant definitions of the fragment are merged with the definitions of the host bundle before the host is resolved, as long as the information does not conflict. If possible, fragment dependencies are resolved. However, if the fragment dependencies cannot be resolved, the fragment is not attached to the host bundle. A fragment cannot have its own class loader or bundle activator. It cannot override the information present in the host bundles. Fragments extend bundles with resources, classes, and permitted headers to customize your bundles.
<Import-Package>
The <Import-Package>
element contributes any additional imports to the core bundle. Add !*
in the <Import-Package>
element if you do not want to import anything to the core bundle. If your core extension relies on additional third party libraries, other than the libraries already used by the core bundle, you must list the required packages in the <Import-Package>
instruction. You must also include the manifest header directive value identifying a mandatory resolution type to enable attachment problem logging. For example:
<Import-Package>
org.thirdparty.library.package; resolution:=mandatory,
!*
</Import-Package>
If you do not specify the mandatory resolution, OSGi does not find the third party library package at run time and the fragment fails to attach, however, no error message appears.
springCtx-import.xml
This file provides information about the spring configuration for the extension project to OSGi.
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
<!-- Let OSGi know the spring configuration of this project. -->
<import resource="../../META-INF/conf/ep-core-plugin.xml" />
</beans>
note
Core extension classes replacing the default core classes, which have the same package structure and class name, do not work with this approach.
If the extension uses third party libraries, the following additional configuration is required:
- Add the third party packages to the
<Import-Package>
element as described in the Extension pom.xml section - If the third-party libraries are OSGi compliant, add the libraries as a dependency and artifact items in the Cortex Webapp
- If the libraries are not OSGi compliant, the libraries must be embedded in the fragment
jpa-persistence.xml
The core extension’s jpa-persistence.xml
file specifies the following:
<persistence-unit name="commerce-persistence-unit">
...
<mapping-file>...</mapping-file>
<class>...</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<validation-mode>NONE</validation-mode>
<properties>
<property name="openjpa.Log" value="slf4j"/>
<property name="openjpa.DetachState" value="loaded(DetachedStateField=true)"/>
</properties>
</persistence-unit>
<mapping-file>
Defines the classpath for new ORM (Object Relational Mapping) metadata mapping files.
<class>
Defines the fully qualified class names of the new persistent classes.
<exclude-unlisted-classes>
Must be set to
true
so that OpenJPA will scan only the classpaths of the listed classes. OSGi does not permit scanning of unlisted clases.<validation-mode>
Must be set to
NONE
, so that OpenJPA does not automatically performJSR 330
validation on objects persisted to the database.<properties>
Must have values as shown above.
openjpa.DetachState
is used during OpenJPA byte code enhancement.Important: Do not add additional properties here. To add or override OpenJPA properties, see Override Persistence Unit Properties
For more information about how to exclude existing ORM mapping files or persistent classes, see Exclude references from the Persistence Unit.