Announcement: You can find the guides for Commerce 7.5 and later on the new Elastic Path Documentation site. The Developer Center continues to support Commerce 6.13.0 through 7.4.1.Visit new site

Tutorial 9 - Override a Resource Integration with an OSGi Fragment

Tutorial 9 - 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 Tutorial 5 - 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 API Architecture: Architecture Overview.
  • The itemdefinitions resource and its methods GET - an item definition
  • 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 Tutorial 5 - Creating a Resource integration.
Warning: Before you begin

Make sure your development environment has been properly set up as described in Setting up your Developer Environment.

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.

  1. Open a command prompt and navigate to your Extensions Directory.
  2. Run the following command to create a Cortex API resource integration project:
    mvn archetype:generate -DarchetypeArtifactId=ep-cortex-resource-integration-archetype -DarchetypeGroupId=com.elasticpath.cortex.dce -DarchetypeVersion=<your-artifact-version>
    Tip: Stack Trace Tip

    You 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.

    Tip: Troubleshooting Tip

    Build fails when generating the archetype? See: Build Troubleshooting.

  3. Enter the following information when prompted:
    Table 2. Archetype Variable Values
    Variable Value
    groupId com.elasticpath.tutorials
    artifactId resource-integration-fragment-override
    version 1.0-SNAPSHOT
    package com.elasticpath.tutorials
  4. 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.

To add the dependency:

  1. Open your resource-integration-fragment-override project's pom.xml.
  2. Add the following code beneath the <description> element:
    ...
    <dependencies>
    	    <dependency>
    	        <groupId>com.elasticpath.rest.resource</groupId>
    			<artifactId>ep-resource-itemdefinitions</artifactId>
    			<version>0.1.0-SNAPSHOT</version>
    	    </dependency>
    	</dependencies>
  3. Save the pom.xml.

Tip: Dependencies List

For a list of rest resource dependencies, see your cortext-dce\webapp-parent\pom.xml.

Add the Fragment Host

Now we assign the out-of-the-box itemdefinitions resource integration as a fragment host for our fragment project. This tells OSGI that our project is a fragment of the assigned host bundle, which in our case is the out-of-the-box ep-resource-itemdefinitions-epcommerce integration resource.

  1. Open your resource-integration-fragment-override project's pom.xml.
  2. 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>
  3. Save the pom.xml.

Tip: Bundle Symbolic Name

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:

  1. Extract the contents of the bundle jar.
  2. Open the file /META-INF/MANIFEST.MF
  3. 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.

itemdefinitions has three strategy interfaces:

  • 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.

To implement the ItemDefinitionLookupStrategy in our fragment project:

  1. In the resource-integration-fragment-override project, create a new java class called ItemDefinitionLookupStrategyImpl.
  2. Implement the ItemDefinitionLookupStrategy interface in the new class.
  3. 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.

  1. 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.
  2. 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>
    
  3. In command line, navigate to the fragment project root, and build the project by running:
    mvn install
Tip: Application Context File Names
Application context file names for OOTB resource integrations follow this format:
  • applicationContext-{resource name}-integration.xml
For example, the application context file for the items resource integration is: applicationContext-items-integration.xml.

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 API web application startup.

To make this change, you will need to shut down Cortex API web application, modify the web application's POM file, and then restart the web application.

  1. With a text editor, open the Cortex API web application's POM file.
  2. 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>
    	</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>
    
    ...
  3. In a command line, navigate to your Cortex API 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:

  1. Start up your Cortex API web application.
  2. Using a REST client, log into the demo store by following the instructions here: http://api-cortex-developers.docs.elasticpath.com/drupal/content/Authenticate_a_customer using Username: oliver.harris@elasticpath.com and password: password.
  3. Using a REST client, create a search by sending the following request:
    Table 3.
    HTTP Request Method URL Header Body
    POST https://localhost:8443/cortex/searches/mobee/keywords/items Content-type: Application/json {"keywords":"twilight"}
  4. 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": "application/vnd.elasticpath.itemdefinition",
        "uri": "/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": "application/vnd.elasticpath.item",
          "rel": "item",
          "rev": "definition",
          "href": "http://localhost/cortex/items/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2",
          "uri": "/items/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2"
        },
        {
          "type": "application/vnd.elasticpath.links",
          "rel": "options",
          "rev": "definition",
          "href": "http://localhost/cortex/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2/options",
          "uri": "/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2/options"
        },
        {
          "type": "application/vnd.elasticpath.links",
          "rel": "assets",
          "rev": "definition",
          "href": "http://localhost/cortex/assets/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2",
          "uri": "/assets/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2"
        },
        {
          "type": "application/vnd.elasticpath.price.range",
          "rel": "fromprice",
          "rev": "definition",
          "href": "http://localhost/cortex/prices/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2",
          "uri": "/prices/itemdefinitions/mobee/m5yxissunrjgovjqnrefsrluizlweq2ejfkgcy2onnghkz2ujfsvgrkpljdhoqlyoazu44tekvhhmwshk5ywiscrgrhuizzqjzkfumdeo46t2"
        }
      ]
      
    }