Override a Resource Integration with an OSGi Fragment
Override a Resource Integration with an OSGi Fragment
This class explains how to create an OSGi bundle fragment to override functionality within a Commerce Engine resource integration. A fragment is "a bundle that is attached to a host bundle" and adds content to the target bundle. Here, we use that capability to override part of a resource integration's functionality while leaving the other functionality intact.
Suppose you are tasked with modifying itemdefinitions functionality so that itemdefinitions' "display-name" and "details" are sourced from outside of the Commerce Engine, while the itemdefinitions' existing functionality, such as options and components, which integrate with Commerce Engine, remain unchanged.
One approach, as described in Creating a Resource integration, is to create a new resource integration and then override the out-of-the-box resource integration. This method requires you to implement all the itemdefinitions' resource strategies. The approach is costly, requires knowledge of resource integration code, and produces unnecessary duplicate code.
In this lesson, we will go over the steps for creating an OSGI fragment. A fragment is a bundle whose contents are available to another bundle (host bundle), but it does not have a class loader of its own. We will attach a new fragment bundle to the itemdefinitions resource integration module to override a single strategy class.
What you should already know:
- Cortex Architecture: Architecture Overview.
- The itemdefinitions resource and its methods
- Your backend system (Our tutorial code does not talk to a backend system. You can infer from this tutorial how to code a resource integration to talk to your backend system).
- How to create a resource integration, as shown in Creating a Resource integration.
Generate a New Bundle Fragment Project
First, we create a resource integration project for our bundle fragment. Elastic Path provides a Maven Archetype to simplify the task to create a new resource integration. Archetypes are project templates that contain some of the standard components you'll need for your project.
- Open a command prompt and navigate to your Extensions Directory.
- Run the following command to create a Cortex resource integration project:
mvn archetype:generate -DarchetypeArtifactId=ep-cortex-resource-integration-archetype -DarchetypeGroupId=com.elasticpath.cortex.dce -DarchetypeVersion=<your-artifact-version>
Tip: Stack Trace TipYou may see a stack trace preceded by a warning similar to the following:
[WARNING] Error reading archetype catalog http://repo1.maven.org/maven2 org.apache.maven.wagon.TransferFailedException: Error transferring file: Connection timed out: connect
This can occur if you are operating Maven in online mode. It can be safely ignored.
- Enter the following information when prompted:
- Change to the newly created project directory and execute the following command to
build the resource integration JAR
file:
mvn install
Add the Resource Integration's Dependency
Once the base project is set up, add the appropriate rest resource dependency to it. Because we are building an itemdefinitions resource integration, we'll add the itemdefinitions rest resource dependency.
- Open your resource-integration-fragment-override project's pom.xml.
- Add the following code beneath the <description> element:
... <dependencies> <dependency> <groupId>com.elasticpath.rest.resource</groupId> <artifactId>ep-resource-itemdefinitions</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
- Save the pom.xml.
For a list of rest resource dependencies, see your cortext-dce\webapp-parent\pom.xml.
Add the Fragment Host
- Open your resource-integration-fragment-override project's pom.xml.
- Under the <instructions> element, add the <Fragment-Host> element and assign the bundle symbolic name for the itemdefinitons resource integration. This should like similar to the code below:
...<build> <plugins> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> <instructions> <Import-Package> org.springframework.context.annotation, * </Import-Package> <!-- Add the fragment's host here --> <Fragment-Host>com.elasticpath.rest.integration.epcommerce.ep-resource-itemdefinitions-epcommerce</Fragment-Host> </instructions>
- Save the pom.xml.
A bundle's symbolic name is in the bundle JAR's MANIFEST.MF file.
To retrieve a bundle's symbolic name from its MANIFEST.MF file:
- Extract the contents of the bundle jar.
- Open the file /META-INF/MANIFEST.MF
- Look for the key Bundle-SymbolicName. This value is the bundle's symbolic name.
Usually, the symbolic name is a combination of the bundle's group ID and artifact ID in this format: groupId.artifactId.
Implement the Lookup Strategy Interface
Our project is now an itemdefinitions resource integration bundle fragment. Now we need to implement our custom logic in the fragment project.
- ItemDefinitionLookupStrategy - Lookup for an item's display-name and details.
- ItemDefinitionOptionLookupStrategy - Lookup for an item's available options and their values.
- ItemDefinitionComponentLookupStrategy - Lookup for an item's components and its children.
In our use case, we only care about changing the itemdefinition's display-name and details. Therefore, we only need to implement the ItemDefinitionLookupStrategy in our fragment project. The other functionality in OOTB itemdefinitions resource integration will be unaffected by this change.
- In the resource-integration-fragment-override project, create a new java class called ItemDefinitionLookupStrategyImpl.
- Implement the ItemDefinitionLookupStrategy interface in the new class.
- Implement the find() method. Create an ItemDefinitionDto object and populate it with the values you want for display-name and details. Be sure to pass the decodedItemId to the dto by calling setItemCorrelationId(). This decodedItemId is used by other strategy classes to find the item's options and components from Commerce Engine. Below is a example implementation of the find() method:
@Override public ExecutionResult<ItemDefinitionDto> find(String scope, String decodedItemId) { ItemDefinitionDto dto = ResourceTypeFactory.createResourceEntity(ItemDefinitionDto.class); dto.setDisplayName("Item Display"); dto.setItemCorrelationId(decodedItemId); DetailsEntity detailsEntity = ResourceTypeFactory.createResourceEntity(DetailsEntity.class); detailsEntity.setName("itemextname") .setValue("itemextvalue") .setDisplayName("Item Ext Display Name") .setDisplayValue("Item Ext Display Value"); dto.setAttributes(Collections.singleton(detailsEntity)); return ExecutionResultFactory.createReadOK(dto); }
Create the Application Context XML file
We need to declare our new strategy implementation, so it's called instead of the out-of-the-box implementation's strategy. To do this, we create an application context XML file and declare our new bean in it.
- In the resource-integration-fragment-override project, navigate to resources/OSGI-INF/blueprint/ and create a new XML file called applicationContext-itemdefinitions-integration.xml. This is the same name of the application context file in the OOTB itemdefinition resource integration.
- In applicationContext-itemdefinitions-integration.xml, declare a new spring bean with id itemDefinitionLookupStrategy. This is the ID of the bean we want to override in the OOTB itemdefinition resource integration. Set the bean's class to the new ItemDefinitionStrategyImpl class. The contents of the xml file look similar to the following:
<xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="itemDefinitionLookupStrategy" class="com.example.resource.integration.ItemDefinitionLookupStrategyImpl" /> </beans>
- In command line, navigate to the fragment project root, and build the project by running:
mvn install
Add the Bundle Fragment to the Application
Now we need to add the bundle fragment to our application, so it attaches to our itemdefinitions resource integration during Cortex web application startup.
To make this change, you will need to shut down Cortex web application, modify the web application's POM file, and then restart the web application.
- With a text editor, open the Cortex web application's POM file.
- Add the new bundle fragment as a <dependency> and as an <artifactItem> to the web app's pom as shown below:
... <dependencies> <!-- Add Extension resources or Integration modules as maven dependencies here. --> <dependency> <groupId>com.elasticpath.tutorials</groupId> <artifactId>resource-integration-fragment-override</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency> ... <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-extra-cortex-resources</id> <phase>prepare-package</phase> <goals> <goal>copy</goal> </goals> <configuration> <artifactItems> <!-- Add Extension resources or Integration modules as maven artifacts here. --> <artifactItem> <groupId>com.elasticpath.tutorials</groupId> <artifactId>resource-integration-fragment-override</artifactId> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> ...
- In a command line, navigate to your Cortex web application folder and execute:
mvn clean install -DskipAllTests
Run and Test the Bundle Fragment
With the new fragment in place, queries for itemdefinitions return the same display-name and details as we have configured them to do in this sample fragment bundle.
To test this capability:
- Start up your Cortex web application.
- Using a REST client, log into the demo store by following the instructions in Authenticate a Customer and using these credentials:
- Username: oliver.harris@elasticpath.com
- Password: password
- Using your REST client, create a search by sending the following request:
- Now in the links returned, follow the link with the rel "element" to retrieve the item. Then follow the rel "itemdefinitions" to retrieve item definitions for Twilight. The Response should be similar to the following:
{ "self": { "type": "elasticpath.itemdefinitions.item-definition", "uri": "/commerce-legacy/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2", "href": "http://localhost/cortex/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2", "max-age": 600 }, "details": [ { "display-name": "Item Ext Display Name", "display-value": "Item Ext Display Value", "name": "itemextname", "value": "itemextvalue" } ], "display-name": "Item Display", "links": [ { "type": "elasticpath.items.item", "rel": "item", "rev": "definition", "href": "http://localhost/cortex/items/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2", "uri": "/commerce-legacy/items/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2" }, { "type": "elasticpath.collections.links", "rel": "options", "rev": "definition", "href": "http://localhost/cortex/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2/options", "uri": "/commerce-legacy/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2/options" }, { "type": "elasticpath.collections.links", "rel": "assets", "rev": "definition", "href": "http://localhost/cortex/assets/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2", "uri": "/commerce-legacy/assets/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2" }, { "type": "elasticpath.prices.item-price-range", "rel": "fromprice", "rev": "definition", "href": "http://localhost/cortex/prices/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2", "uri": "/commerce-legacy/prices/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2" } ] }