Development Support Features
This section details information about the different development support features available.
Extension Annotations
Extension classes can have the following annotations:
@Extension
All extension classes must contain this annotation to be recognized as an extension by the Extension Point Framework.
@XPFEmbedded
This annotation indicates that the extension should be instantiated by the Spring Framework. This allows the extension to define fields with the @Autowired
annotation so it can access Spring services.
Warning
Do not add this annotation to extensions within external plugins.
@XPFAssignment
This annotation indicates that the extension should be automatically assigned to an extension point. If an extension does not contain this annotation, then it will not be assigned at startup but can still assigned at runtime using JMX methods.
This annotation can be defined multiple times on the same extension to assign the extension to multiple extension points, as in the example below:
@XPFAssignment(extensionPoint = XPFExtensionPointEnum.VALIDATE_PRODUCT_SKU_AT_ADD_TO_CART_READ, priority = 1050)
@XPFAssignment(extensionPoint = XPFExtensionPointEnum.VALIDATE_PRODUCT_SKU_AT_CHECKOUT, priority = 1040)
@XPFAssignment(extensionPoint = XPFExtensionPointEnum.VALIDATE_PRODUCT_SKU_AT_ADD_TO_CART, priority = 1040)
The extensionPoint
parameter indicates which extension point the extension should be assigned to.
The priority
parameter indicates the priority of the extension for that extension point. For extensions retrieved using XPFExtensionLookup#getSingleExtension
, the assigned extensions are ordered by priority, and the first valid extension is returned. For extensions retrieved using XPFExtensionLookup#getMultipleExtensions
, the extensions are ordered by priority and returned in that order. Therefore, extensions with lower priority numbers are returned before extensions with higher priority numbers.
note
All Elastic Path Commerce extensions that are delivered as part of the product will be assigned priorities in the range 1000 to 1999. Since we may add or remove extensions in the future, make sure to use priorities below 1000 to ensure that your extension runs before all Elastic Path extensions, or priorities of 2000 or above to ensure that your extension runs after all Elastic Path extensions.
Extension Point Selectors
Selectors are a mechanism to ensure that extensions are only executed in certain contexts. For example, extensions can be configured to only run for certain scopes (stores) in Cortex.
To define the selector mode and the selectors for each extension, see Configuring and Deploying. To determine which selector an extension point supports, see the XPFExtensionPointEnum
documentation.
Extensions can be configured to run in one of two different selector modes:
DEFAULT_NONE
means that the extension will not be executed unless a selector context is defined that matches the current runtime context.DEFAULT_ALL
means that the extension will be executed unless a selector context is defined that matches the current runtime context.
The currently supported selectors are described below:
SELECTOR_NONE
This selector always matches the runtime context.
SELECTOR_STORE
This selector matches the runtime context if the current store matches the defined store code.
Plugin Initialization
To execute certain functionality as soon as a plugin is started, your plugin class can override the public void start()
method from the PF4j Plugin
class. Unlike the constructor, this method can access the Plugin Initialization Context.
The Plugin Initialization Context is an object available to external plugin and external plugin extensions. It provides access to support features such as plugin settings, logging, and caching. This object is not available to embedded extensions.
To access this object, classes can execute MyPlugin.getInstance().getContext()
, where MyPlugin
is replaced with the name of the external plugin class.
Extension Initialization
To execute certain functionality as soon as an extension is instantiated, your extension class can override the public void initialize(XPFExtensionInitializationContext context)
method from the XPFExtensionPointImpl
class. Unlike the constructor, this method can access the Extension Initialization Context, which is passed as a parameter.
The Extension Initialization Context provides access to support features such as extension settings.
Settings
Setting values can be passed into both external plugins and external plugin extensions. This can be useful for inputs such as endpoint URIs, credentials, or behavioural flags.
To access plugin settings, extensions can read Map<String, XPFPluginSetting> getSettings()
from the Plugin Initialization Context. To access extension settings, extensions can read Map<String, XPFPluginSetting> getSettings()
from the Extension Initialization Context.
Setting value keys are not pre-defined, so any setting values can be passed into a plugin or extension. It is up to the plugin developer to decide how to handle expected setting values that are missing, such as falling back to a default value or throwing an exception.
For more information about how to specify the setting values to pass into plugins and extensions, see Configuring and Deploying External Plugins.
Logging
External plugins and external plugin extensions can write to the service logs through an SLF4j logger. Extensions can read Logger getLogger()
from the Plugin Initialization Context. The logger is automatically initialized with the logger name set to the plugin class name.
For example:
@Override
public void initialize(XPFExtensionInitializationContext context) {
super.initialize(context);
MyPlugin.getInstance().getContext().getLogger().info("Extension initialized.");
}
Additionally, the following Extension Point Framework events are logged automatically:
- When an external plugin is loaded:
INFO: XPF plugin {plugin-id} loaded.
- When an external plugin is started:
INFO: XPF plugin {plugin-id} started.
- When an external plugin is stopped:
INFO: XPF plugin {plugin-id} stopped.
- When an external plugin is unloaded:
INFO: XPF plugin {plugin-id} unloaded.
- When an extension is assigned:
DEBUG: Extension class {extension-class-name} assigned to {xpf-extension-enum} with priority {priority}.
- When an extension is unassigned:
DEBUG: Extension class {extension-class-name} unassigned from {xpf-extension-enum}.
- After an extension method is invoked:
TRACE: Extension class {extension-class-name} method {method-name} took {time} ms to execute.
- After an extension method is invoked, if it took more than 100ms:
WARN: Extension class {extension-class-name} method {method-name} took {time} ms to execute.
Caching
External plugins and external plugin extensions can create a cache that is backed by EhCache. Extensions can call XPFCache<K, V> createCache(String cacheName, int timeToLive, int timeToIdle, int maxObjects)
on the Plugin Initialization Context.
Each Elastic Path Commerce service holds a separate cache in memory; they are not shared between services. Also, the cache name is scoped to the plugin ID, meaning that if another plugin specifies the same cache name, separate caches will be created.
The cache will continue to exist even if the plugin is unloaded and reloaded. When createCache
is invoked, if an existing cache already exists for that plugin ID with the same name, the existing cache will be returned. Note that the plugin ID includes the version, so if a new plugin is loaded with a different version, then a new cache will be created.
Parameters
- The
cacheName
parameter specifies the name of the cache. - The
timeToLive
parameter specifies the maximum number of seconds an element can exist in the cache regardless of use. - The
timeToIdle
parameter specifies the maximum number of seconds an element can exist in the cache without being accessed. - The
maxObjects
parameter specifies the maximum number of objects that can exist in the cache.
For example
XPFCache<String, BigDecimal> skuCodeToPriceCache = LifecycleLoggerPlugin.getInstance().getContext().createCache("skuCodeToPriceCache", 3600, 3600, 10000);
BigDecimal price = skuCodeToPriceCache.get("alien_sku", skuCode -> getPriceFromAPI(skuCode));
Performance
All extension method invocations are monitored to ensure they execute within an expected execution time threshold. If an extension method takes more than 100ms to execute, a warning will be logged: Extension class {} method {} took {} ms to execute.