Building WebSphere Commerce services for cross-channel commerce

This tutorial provides instructions on how to develop services that will be used by Web, mobile, and third party applications. The tutorial also covers the WebSphere® Commerce SOA-based programming model and how to build business logic that you can reuse across different channels.

Share:

N Krishnan (nkrishna@in.ibm.com), IT Specialist, IBM

Author photoN Krishnan is an IT Specialist working at the IBM India Software Labs, India. He was part of the WebSphere Commerce development team and also developed retail solutions in the Retail EBO team. He is currently working as a WebSphere Commerce Consultant in IBM Software Services for Industry Solutions.



29 February 2012

Also available in Chinese Portuguese

About this tutorial

This tutorial provides instructions on how to develop services that will be used by Web, mobile, and third party applications. This tutorial also covers the WebSphere Commerce SOA-based programming model and how to build business logic that you can reuse across different channels. The tutorial is intended for architects, consultants, and developers who will be implementing WebSphere Commerce solutions.

Objectives

In this tutorial, you will customize the Catalog service to support extended catalog entry information and use services from multiple channels. This customization includes:

  • Updating the WebSphere Commerce schema to store new information.
  • Customizing the Catalog service to include new information as user data in the CatalogEntry noun.
  • Reusing the same service code no matter which channel the client is coming from.

Prerequisites

This tutorial is written for WebSphere Commerce developers with skills and experience at an intermediate level. Additionally, you also need to be familiar with service-oriented architecture (SOA) concepts.

System requirements

To create the example in this, you need WebSphere Commerce Developer V7 installed and configured. You also need the Madison Extended site starter store published with the Furniture catalog.

Before you start the tutorial:

  1. Download and install the soapUI application to work with the Web service.
  2. Unzip the code sample.zip file that is provided with the tutorial to a temporary location, such as C:\Lab_Instructions.
  3. <WCDev_Dir> represents the directory where the WebSphere Commerce development environment is installed, for example, C:\WebSphere\WCToolkit.

Duration

3 to 4 hours


Customizing the WebSphere Commerce service

Customizing the WebSphere Commerce schema

In this section, you will add new tables in the WebSphere Commerce schema to store the additional product information. Figure 1 shows the changes in the schema.

Figure 1. Schema extensions
Schema extensions

The schema shows the new tables and their relationship with the existing tables. The new tables are XPRODUCT, XPRODUCT_KIOSK, and XCAREINSTRUCTION:

  • XPRODUCT table: This table has a foreign key to the CATENTRY table. For demonstration purposes, this table holds the data about import information for a given catalog entry. This allows the Data Service Layer (DSL) to populate data from the XPRODUCT table in the USERDATA element of the CatalogEntry noun.

    Note: USERDATA is an out-of-box extension point that allows customers to add simple name value pairs to the noun or service response. This data comes from custom tables that have a foreign key to the base table of the noun. This relationship must be one-to-one. One-to-many relationships are not supported for user data.

  • XPRODUCT_KIOSK table: This table also has a foreign key to the CATENTRY table. For demonstration purposes, this table holds the data specific to Kiosk channel. This allows the Data Service Layer (DSL) to populate data from the XPRODUCT_KIOSK table in the USERDATA element of the CatalogEntry noun.
  • XCAREINSTRUCTION table: This table has a foreign key to the CATENTDESC table to allow DSL to populate data in the ATTRIBUTE element of the CatalogEntryDescription noun part.

The XCAREINSTRUCTION table has a foreign key to the CATENTRY table to provide support to the DSL search function. This table holds information about care instructions, for example, "Fragile, handle with care" for a given catalog entry.

To add the new tables:

  1. Connect to the development database and execute the following SQLs:
    1. Click on Start > Run and type db2cmd.
    2. Change the directory to C:\Lab_Instructions.
    3. Connect to the database using db2 connect to <DB_Name>.
    4. Run the SQL to create the tables as shown below:
      	db2 -tvf CustomTablesDefinitions.sql
      
      	CREATE TABLE XPRODUCT
      	(
      	CATENTRY_ID BIGINT NOT NULL,
      	IMPORTED VARCHAR(1),
      	IMPORTED_COUNTRY VARCHAR(30),
      	OPTCOUNTER SMALLINT,
      	CONSTRAINT XPRODUCT_PK PRIMARY KEY (CATENTRY_ID),
      	CONSTRAINT XPRODUCT_FK FOREIGN KEY (CATENTRY_ID) REFERENCES CATENTRY 
           (CATENTRY_ID) on delete cascade
      	);
      	
      	CREATE TABLE XPRODUCT_KIOSK
      	(
      	CATENTRY_ID BIGINT NOT NULL,
      	AISLE_NUMBER VARCHAR(5),
      	AISLE_Information VARCHAR(100),
      	OPTCOUNTER SMALLINT,
      	CONSTRAINT XPRODUCT_KIOSK_PK PRIMARY KEY (CATENTRY_ID),
      	CONSTRAINT XPRODUCT_KIOSK_FK FOREIGN KEY (CATENTRY_ID) REFERENCES 
           CATENTRY (CATENTRY_ID) on delete cascade
      	);
      	
      	CREATE TABLE XCAREINSTRUCTION
      	(
      	CATENTRY_ID BIGINT NOT NULL,
      	LANGUAGE_ID INTEGER NOT NULL,
      	CAREINSTRUCTION VARCHAR(254),
      	OPTCOUNTER SMALLINT,
      	CONSTRAINT XCAREINST_PK PRIMARY KEY (CATENTRY_ID, LANGUAGE_ID),
      	CONSTRAINT XCAREINST_FK1 FOREIGN KEY (CATENTRY_ID,LANGUAGE_ID) 
           REFERENCES CATENTDESC(CATENTRY_ID,LANGUAGE_ID) on delete cascade,
      	CONSTRAINT XCAREINST_FK2 FOREIGN KEY (CATENTRY_ID)REFERENCES CATENTRY 
           (CATENTRY_ID) on delete cascade
      	);
  2. Populate the custom tables with some sample data by running the following SQL statements. Make sure you replace the right CatalogEntry_id in the SQL shown below. In the DB2® command prompt opened above in Step 1a, issue the following command to insert the sample data into the custom tables:
    db2 -tvf SampleData.sql
    
    INSERT INTO XPRODUCT (CATENTRY_ID, IMPORTED, IMPORTED_COUNTRY) VALUES 
     (10001,'Y', 'Japan, The Land of Rising Sun');
    INSERT INTO XPRODUCT (CATENTRY_ID, IMPORTED, IMPORTED_COUNTRY) VALUES 
     (10002,'Y', 'India, The subcontinent');
    		
    INSERT INTO XCAREINSTRUCTION (CATENTRY_ID, LANGUAGE_ID, CAREINSTRUCTION) VALUES 
     (10001, -1, 'Never use an abrasive cleaner or material on any finished product');
    INSERT INTO XCAREINSTRUCTION (CATENTRY_ID, LANGUAGE_ID, CAREINSTRUCTION) VALUES 
     (10002, -1, 'Never use an abrasive cleaner or material on any finished product');
    		
    INSERT INTO XPRODUCT_KIOSK (CATENTRY_ID, AISLE_NUMBER, AISLE_Information) VALUES 
     (10001, '10', 'Available in aisle');
    INSERT INTO XPRODUCT_KIOSK (CATENTRY_ID, AISLE_NUMBER, AISLE_Information) VALUES 
     (10002, '12', 'Available near checkout counter');

Customizing the physical layer to include new information

In this section, you will customize the physical layer by generating object-relational metadata, and by generating the physical service data objects (SDOs). To do so, you will use a tool called the "Data Service Layer" wizard.

Physical SDOs are service data objects that represent tables in the WebSphere Commerce schema. Each data object type corresponds to a table definition in the schema, and each data object property corresponds to a table column or a reference to another data object type. These references correspond to the foreign key relationships between the database tables. The Data Service Layer wizard is used to generate object-relational metadata and physical data objects for your schema customization.

For each service module, there is object-relational metadata that contains the information to relate the physical data object to a database table. The custom object-relational metadata is stored in the component configuration extension directories and the custom physical SDOs are stored inside the WebSphereCommerceServerExtensionsLogic project.

This wizard performs the following functions:

  • Creates an extension configuration folder for the Catalog service module if one does not already exist. The directory path is WC_eardir\xml\config\com.ibm.commerce.catalog-ext.
  • Generates a custom object-relational metadata file. This file is located at WC_eardir\xml\config\com.ibm.commerce.catalog-ext\wc-object-relational-metadata.xml.
  • Generates SDO Java classes and places it in the WebSphereCommerceServerExtensionsLogic project.

    Note: The IBM®-supplied CatalogEntry objects are extended to include the new getters.

  • Creates a utility Java™ class to return the physical SDO root class (and its package) for the service module. This root class ensures that all WebSphere Commerce physical SDOs for the service module, and any additional physical SDOs for the customization, are available at runtime.

    Note: This configuration tells the system to instantiate the new CatalogEntry SDO instead of the IBM-supplied CatalogEntry SDO so that it now has access to the new data.

  • Creates an extension service module configuration file that instructs WebSphere Commerce to use the newly created catalog physical SDO class in WC_eardir\xml\config\com.ibm.commerce.catalog-ext\wc-component.xml.
  • Creates an extension business object mediator configuration file that configures the business object mediator to include data from the XPRODUCT, XPRODUCT_KIOSK, and XCAREINSTRUCTION tables in the user data of a CatalogEntry noun. This file is located at WC_eardir\xml\config\com.ibm.commerce.catalog-ext\wc-business-object-mediator.xml.

To run the Data Service Layer wizard:

  1. Select File > New > Other > WebSphere Commerce > Data Service Layer.
  2. Click Next.
  3. Select Extend a default WebSphere Commerce service module.

    Note: In this tutorial, the catalog service module will be extended. Therefore, this option has to be selected. If you are creating a new service, then you must select the other option, "Work with a custom service module".

  4. Click Next.
  5. Enter the following information:
    1. Service module: Select com.ibm.commerce.catalog
    2. Extension class prefix: MyCompany
    3. Extension Java package name: com.mycompany.commerce.catalog
  6. Click Next.
  7. Select the XPRODUCT, XPRODUCT_KIOSK, and XCAREINSTRUCTION tables.
  8. Expand the XPRODUCT, XPRODUCT_KIOSK, and XCAREINSTRUCTION tables and make sure all columns are selected.
  9. Click Next.
  10. Under UserData, ensure that the IMPORTED, IMPORTED_COUNTRY, AISLE_NUMBER, AISLE_INFORMATION, and CAREINSTRUCTION database columns are set to "True", as shown in Figure 2. Set the other columns to "False" if they are set as "True" by default.

    Note: This step ensures that the columns you want are included in the UserData area of the logical schema.

    Figure 2. Extension table using Metadata Editing in the DSL wizard
    Extension table using Metadata Editing in the DSL wizard
  11. Click Finish.
  12. Verify that the following files are created in the WebSphereCommerceServerExtensionsLogic project, as shown in Figure 3.
    Figure 3. Files generated from the DSL wizard
    Files generated from the DSL wizard

    Note: The physical SDOs are generated in the WebSphereCommerceServerExtensionsLogic project under com.mycompany.commerce.catalog.facade.server.entity.datatypes. These SDOs provide a Java view of the database for the custom catalog service.

Adding query templates to include the new information

In this section, you will add query template files that retrieve your additional product information. Query template files store the SQL and access profile definitions for a service module, isolating it from the business logic layer completely.

The query template file consists of the following:

  • A symbol definition section that defines the tables your query template will use: CATENTRY, CATENTDESC, XPRODUCT, XPRODUCT_KIOSK, and XCAREINSTRUCTION.
  • Associated SQL statements to define SQL queries. You can reuse these queries to build different access profiles defined in the PROFILE section.

    Note: Access profiles determine how much information is returned. For example, a "Summary" may return only a product description, but a "Details" profile might return description, price, attributes, and more. Since you are adding more data, you must create a new access profile.

  • A new access profile, MyCompany_All, is used along with the XPath key to identify the SQL template query.
  • Many other access profiles are defined in the query template, which will be used in different channels.

To create a customer Get query template file:

  1. Right-click the WC\xml\config\com.ibm.commerce.catalog-ext folder. (If the com.ibm.commerce.catalog-ext folder is not visible, select the WC\config folder and select File > Refresh.)

    Note: The custom TPL files must be created under the <component name>-ext folder.

  2. Click New > File.
  3. Name the file as wc-query-MyCompanyCatalogEntry-get.tpl.

    Note: The name must begin with wc-query- and end with the suffix .tpl.

  4. Click Finish.
  5. Copy and paste the following query template into the file:
    BEGIN_SYMBOL_DEFINITIONS
    <!-- WebSphere Commerce tables -->
    COLS:CATENTRY=CATENTRY:*
    COLS:CATENTDESC=CATENTDESC:*
    
    <!-- MyCompany extension tables -->
    COLS:XPRODUCT =XPRODUCT:*
    COLS:XCAREINSTRUCTION=XCAREINSTRUCTION:*
    COLS:XPRODUCT_KIOSK=XPRODUCT_KIOSK:*
    
    END_SYMBOL_DEFINITIONS
    
    BEGIN_ASSOCIATION_SQL_STATEMENT
      name=MyCompanyImportgetCatalogEntryDetails
      base_table=CATENTRY
      additional_entity_objects=true
       sql=
    	SELECT
    	   CATENTRY.$COLS:CATENTRY$,
    	   CATENTDESC.$COLS:CATENTDESC$,
               XPRODUCT.$COLS:XPRODUCT$,	
               XCAREINSTRUCTION.$COLS:XCAREINSTRUCTION$
    	FROM
    	   CATENTRY
    		LEFT OUTER JOIN XPRODUCT ON (CATENTRY.CATENTRY_ID = 
             XPRODUCT.CATENTRY_ID)
    		LEFT OUTER JOIN CATENTDESC ON (CATENTDESC.CATENTRY_ID = 
             CATENTRY.CATENTRY_ID AND CATENTDESC.LANGUAGE_ID in ($CONTROL:LANGUAGES$))
    		LEFT OUTER JOIN XCAREINSTRUCTION ON (CATENTRY.CATENTRY_ID = 
             XCAREINSTRUCTION.CATENTRY_ID AND XCAREINSTRUCTION.LANGUAGE_ID in 
             ($CONTROL:LANGUAGES$))
    	WHERE
    	   CATENTRY.CATENTRY_ID IN ($ENTITY_PKS$) AND
    	   CATENTRY.MARKFORDELETE = 0
    END_ASSOCIATION_SQL_STATEMENT
    
    BEGIN_ASSOCIATION_SQL_STATEMENT
      name=MyCompanyImportgetCatalogEntryWithKioskDetails
      base_table=CATENTRY
      additional_entity_objects=true
       sql=
    	SELECT
    	   CATENTRY.$COLS:CATENTRY$,
           XPRODUCT_KIOSK.$COLS:XPRODUCT_KIOSK$
    	FROM
    	   CATENTRY
    		LEFT OUTER JOIN XPRODUCT_KIOSK ON (CATENTRY.CATENTRY_ID = 
             XPRODUCT_KIOSK.CATENTRY_ID)
    	WHERE
    	   CATENTRY.CATENTRY_ID IN ($ENTITY_PKS$) AND
    	   CATENTRY.MARKFORDELETE = 0
    END_ASSOCIATION_SQL_STATEMENT
    
    
    BEGIN_PROFILE
      name=MyCompany_All
      extends=IBM_Admin_All
      BEGIN_ENTITY
    	associated_sql_statement=MyCompanyImportgetCatalogEntryDetails
      END_ENTITY
    END_PROFILE
    
    BEGIN_PROFILE
      name=MyCompany_Admin_All
      extends=IBM_Admin_All
      BEGIN_ENTITY
    	associated_sql_statement=MyCompanyImportgetCatalogEntryDetails
      END_ENTITY
    END_PROFILE
    
    BEGIN_PROFILE
      name=MyCompany_Mobile_Description
      extends=IBM_Admin_CatalogEntryDescription
      BEGIN_ENTITY
    	associated_sql_statement=MyCompanyImportgetCatalogEntryDetails
      END_ENTITY
    END_PROFILE
    
    BEGIN_PROFILE
      name=MyCompany_Store_CatalogEntryAllDescriptions
      extends=IBM_Admin_CatalogEntryAllDescriptions
      BEGIN_ENTITY
    	associated_sql_statement=MyCompanyImportgetCatalogEntryDetails
      END_ENTITY
    END_PROFILE
    
    BEGIN_PROFILE
      name=MyCompany_Store_CatalogEntryDetailsWithKioskInfo
      extends=IBM_Admin_Details
      BEGIN_ENTITY
        associated_sql_statement=MyCompanyImportgetCatalogEntryDetails
    	associated_sql_statement=MyCompanyImportgetCatalogEntryWithKioskDetails
      END_ENTITY
    END_PROFILE
  6. Save the file.

Creating an access control policy to secure new information

The previous section created a new access profile, MyCompany_All. By default, only the users with a site administrator role have access to this new data. In this section, you will update the Catalog service access control policy to state that all users have access to view this data.

The new policy defines a new action for the MyCompany_All access profile and adds the new action to the CatalogEntry All users group.

  1. Create the following file: <WCDev_Dir>\xml\policies\xml\MyCompanyCatalogAccessControlPolicies.xml.
  2. Copy and paste the following access control policy XML into this file:
    	<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
    	<!DOCTYPE Policies SYSTEM "../dtd/accesscontrolpolicies.dtd">
    	<Policies>
    		<Action Name="GetCatalogEntry.MyCompany_All" CommandName=
             "GetCatalogEntry.MyCompany_All"/>
    	
    		<ActionGroup Name="Catalog-CatalogEntry-AllUsers-
             AccessProfileActionGroup" OwnerID="RootOrganization">
    			<ActionGroupAction Name="GetCatalogEntry.MyCompany_All"/>
    		</ActionGroup>
    	
    	        <!-- === Following entries are for mobile access profile which 
                 will be used in Section 5 of the lab === -->
    		<Action Name="GetCatalogEntry.MyCompany_Mobile_Description" 
             CommandName="GetCatalogEntry.MyCompany_Mobile_Description"/>
    	
    		<ActionGroup Name="Catalog-CatalogEntry-AllUsers-
             AccessProfileActionGroup" OwnerID="RootOrganization">
    			<ActionGroupAction Name="GetCatalogEntry.
                 MyCompany_Mobile_Description"/>
    		</ActionGroup>	
    	</Policies>
  3. Run the acpload command to load the access control policy:
    1. Open a command prompt and navigate to <WCDev_Dir>\bin.
    2. Run the following command:
      acpload inst1db db2admin passw0rd MyCompanyCatalogAccessControlPolicies.xml 
       db2admin
    3. Navigate to the <WCDev_Dir>\logs directory.

Inspect the acpload.log and messages.txt files to ensure that the access control policy loaded successfully. There may not be a messages.txt file present if everything loaded successfully.

Validating the Catalog customization with JUnit

This section shows how to test your customization using JUnit. JUnit is a standalone Java program that will use the IBM Client library (a set of Java APIs) to access the WebSphere Commerce service. It does this by sending HTTP requests to the service. This unit test assumes a starter store with a store ID of 10152 (MadisonsESite) and a catalog ID of 10001 (Extended Sites Catalog Asset Store) have been published to your WebSphere Commerce development environment.

For demonstration purposes, this is considered as the first channel to invoke the services.

  1. Import the JUnit Test project for the Catalog service to your workspace. The project interchange zip file, CatalogExtensions-UnitTest.zip, is provided in the C:\Lab_Instructions folder.
  2. In the Enterprise Explorer view, open:
    CatalogExtensions-UnitTest\src\com.mycompany.commerce.catalog.facade.client\
     CatalogFacadeExtensionClientTest.java

    Note: Take a moment to understand how this JUnit test case is built.

    There are two important methods:

    • setUp(): This initializes the businesscontext and callbackhandler objects. These objects are used to create the CatalogEntry client.
    • testCatalogEntryCustomization(): In this method, the actual call to the service happens. Form the Get expression by using the SelectionCriteriaHelper class and create the Get verb using the createGetVerb method of the catalog entry facade client. Perform the get request by using the CatalogEntry client created earlier in the setUp method. Navigate and print (or assert) the UserData element in the response object (in this case, it is of type ShowCatalogEntryDataAreaType).
  3. Notice the following constants with the machine specific values that are already configured:
    • USER_ID = wcsadmin
    • PASSWORD = passw0rd
    • STORE_ID = 10152 (Since we are using an eSite for this tutorial, we have set the storeID of the MadisonESite store.)
    • CATALOG_ID = 10001 (Since the eSite that is set up for this tutorial uses catalog asset store, we have set the catalogID of catalog asset store.)
    • catEntryId = 10002 (This is the catentryId against in which the sample data got populated.)
  4. On the Server page, right-click the WebSphere Commerce Test Server and select Start.
  5. If the WebSphere Commerce (WC) project is not already published to the WebSphere Commerce Test Server, publish the WC project:
    1. On the Server page, right-click the WebSphere Commerce Test Server and select Add and Remove Projects.
    2. Select the WC project.
    3. Click Add.
    4. Click Finish.

    If the WC project is already published, right-click the WebSphere Commerce Test Server and select Publish.

  6. Set up a TCP/IP monitor in WebSphere Commerce Developer. You will use this TCP/IP monitor to observe the request and response documents going to and from the WebSphere Commerce service you created. To create a TCP/IP Monitor that will forward requests to WebSphere Commerce:
    1. Select Window > Preferences.
    2. From the Preferences page, click Run/Debug > TCP/IP Monitor.
    3. Click Add.
    4. Type the following information:
      • Local monitoring port: 81
      • Hostname: localhost - the hostname of the WebSphere Commerce Server where the catalog service is running.
      • Port: 80 for WebSphere Commerce Developer
      • Check the check box Start monitor automatically.
    5. Click OK.
    6. Select the created TCP/IP monitor.
    7. Click Start. (This should have been started already, in which case skip the next step.)
    8. Click OK.
  7. Right-click the CatalogExtensionFacadeClientTest.java class and select Run As > JUnit Test. You see that the request and response XML documents are transmitted between the client and server on the TCP/IP monitor.

    Verify the results:

    • This test performs a Get request to retrieve a CatalogEntry noun, including the Product extension and care instruction data.
    • The Get request uses the XPath key of /CatalogEntry[CatalogEntryIdentifier[(UniqueID=)]], and the MyCompany_All access profile defined in Step 3 above.
    • The data service layer uses the XPath key and access profile to identify the SQL query template to run against the database and populate physical data objects with the results.
    • The business object mediator transforms the physical data objects into logical nouns. This process populates additional product information into the CatalogEntry noun's UserData element and populates care instructions into the Catalog description noun part's attributes element.

Invoking the modified Web service from the Madisons store

In this section, you create a JSP file that displays extended catalog entry information using the getData tag. You update the ProductDisplay.jsp to link to this JSP file for the extended catalog entry information. After completing the customization, shoppers see a link to "Import information" on the Product Display page. When shoppers click this link, the extended catalog entry JSP page displays. For demonstration purposes, this is considered as the second channel to invoke the WebSphere Commerce service from the Web channel. IBM provides a set of JSTL tags that simplifies accessing the services.

  1. Create the storefront get-data-config file. The country of import information is added to the CatalogEntry noun UserData element. To fetch the import information from catalogEntry, create a storefront get-data-config file that overwrites the configuration file of the catalog component. The JSP pages reference the getData tag by using the expression builder and access profile names. The <wcf:getData> tag retrieves the service data objects from WebSphere Commerce services and associates them with a declared scripting variable with an ID.
    1. In the Enterprise Explorer view, expand Stores > WebContent > WEB-INF > config.
    2. Right-click config, then click New > Folder.
    3. In the Folder name field, enter com.ibm.commerce.catalog-ext.
    4. Click Finish. The com.ibm.commerce.catalog-ext folder is created under the config directory.
    5. Right-click com.ibm.commerce.catalog-ext and select New > File.
    6. In the File name field, enter get-data-config.xml.
    7. Click Finish. Enter the following code in the get-data-config.xml file:
      <?xml version="1.0" encoding="UTF-8"?>
      <wcf:get-data-config
      xmlns:wcf="http://www.ibm.com/xmlns/prod/commerce/foundation"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.ibm.com/xmlns/prod/commerce/foundation ../../
       xsd/get-data-config.xsd ">
      		
      <expression-builder>
      <name>getCatalogEntryWithImport</name>
      <data-type-name>CatalogEntry</data-type-name>
      <expression-template>{_wcf.ap=$accessProfile$}/CatalogEntry
       [CatalogEntryIdentifier[(UniqueID='$catalogEntryId$')]]</expression-template>
      <param>
      <name>accessProfile</name>
      <value>MyCompany_All</value>
      </param>
      </expression-builder>
      		
      </wcf:get-data-config>

      For <expression-builder>:

      name
      getCatalogEntryWithImport is used by the getData tag and is referenced by the JSP page, which is created in the next step.
      data-type-name
      The noun.
      expression-template
      The XPath specify defined in the wc-query-MyCompanyCatalogEntry-get.tpl file.

      For <param>:

      name
      The name of the access profile, accessProfile, defined in the wc-query-MyCompanyCatalogEntry-get.tpl file.
      value
      The value, MyCompany_All, of the access profile defined in the wc-query-MyCompanyCatalogEntry-get.tpl file.
    8. Save the file.
  2. Extend the property file:
    1. In the Enterprise Explorer view, expand Stores > Java Resources: src > MadisonsStorefrontAssetStore > storetext.properties.
    2. Open the storetext.properties file.
    3. Add the header properties for the new title:
      #-------------------------------------------
      # Tutorial - START
      #-------------------------------------------
      productImportDisplayTitle=Import information
      #-------------------------------------------
      # Tutorial - END
      #-------------------------------------------

      Note: The Properties file contains the language specific labels that are displayed on the page.

  3. Create the ImportDisplay.jsp file to display the Import information in the store:
    1. Open WebSphere Commerce Developer in the Java EE perspective.
    2. In the Enterprise Explorer view, navigate to Stores > WebContent > MadisonsStorefrontAssetStore > ShoppingArea > CatalogSectio > CatalogEntrySubsection.
    3. Right-click CatalogEntrySubsection and select New > Web Page.
    4. In the File name field, enter ImportDisplay.jsp. In the Template section, make sure JSP is selected under Basic Templates.
    5. Click Finish.
    6. Click the Source tab and replace the contents of the ImportDisplay.jsp file with the following code sample:
      	<%--
      	  *****
      	  * This JSP imports CachedProductItemDisplay or CachedProductOnlyDisplay 
            * based on the store configuration.
      	  * It also imports header, sidebar, and footer.
      	  *****
      	--%>
      	<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
      	<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
      	<%@ taglib uri="http://commerce.ibm.com/base" prefix="wcbase" %>
      	<%@ taglib uri="http://commerce.ibm.com/foundation" prefix="wcf" %>
      	<%@ taglib uri="flow.tld" prefix="flow" %>
      	
      	<%@ include file="../../../include/JSTLEnvironmentSetup.jspf"%>
      	<%@ include file="../../../include/nocache.jspf"%>
      	
      	<c:set var="productId" value="${WCParam.productId}" />
      	<c:set var="productPage" value="true" scope="request"/>
      	<c:set var="hasBreadCrumbTrail" value="true" scope="request"/>
      	<c:set var="useHomeRightSidebar" value="false" scope="request"/>
      	
      	
      	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
           "http://www.w3.org 
           TR/xhtml1/DTD/xhtml1-transitional.dtd">
      	<!-- BEGIN ProductDisplay.jsp -->
      	<html xmlns:wairole="http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#"
      	xmlns:waistate="http://www.w3.org/2005/07/aaa" lang="${shortLocale}" 
           xml:lang="${shortLocale}">
      	<head>
      	<title>Import Display</title>
      	
      	<link rel="stylesheet" href="<c:out value="${jspStoreImgDir}$
           {vfileStylesheet}"/>" 
           type="text/css"/>
      	<!--[if lte IE 6]>
      	<link rel="stylesheet" href="<c:out value="${jspStoreImgDir}$
           {vfileStylesheetie}"/>" 
           type="text/css"/>
      	<![endif]-->
      	<script type="text/javascript" src="<c:out value="${dojoFile}"/>" 
           djConfig="${dojoConfigParams}"></script>
      	<%@ include file="../../../include/CommonJSToInclude.jspf"%>
      	<script type="text/javascript" src="<c:out value="${jsAssetsDir}
           javascript/MessageHelper.js"/>"></script>
      	
      	<style>
      	tr,td{
      	border:1px;
      	}
      	</style>
      	</head>
      	
      	<body>
      	<%@ include file="../../../include/StoreCommonUtilities.
           jspf"%>
      		<div id="page">
      			
      			<%@ include file="../../../include/LayoutContainerTop.
                   jspf"%>
      			
      			<div id="content_wrapper_box">
      	
      			<wcf:getData var="catentries" type="com.ibm.commerce.
                   catalog.facade.datatypes.CatalogEntryType[]" expressionBuilder=
                   "getCatalogEntryWithImport">
      				<wcf:contextData name="storeId" data=
                       "${WCParam.storeId}"/>
      				<wcf:contextData name="catalogId" data=
                       "${WCParam.catalogId}"/>
      				<wcf:contextData name="langId" data=
                       "${WCParam.langId}"/>
      				<wcf:param name="catalogEntryId" value=
                       "${WCParam.catalogEntryID}"/>
      				<wcf:param name="accessProfile" value=
                       "MyCompany_All"/>
      			</wcf:getData>
      			
      		<%-- Create a table to display the value for ProductID, Import & 
                Import Country --%>
      		 <div>
      			<table id="WC_CachedItemDisplay_Table_1">
      				<tbody align="center">
      					<tr align="left" cellpadding="2" 
                           cellspacing="2" >
      						<td align="left" cellpadding="2" 
                               cellspacing="2">
      						<p align="left"> <b><fmt:
                               message key="productImportDisplayTitle" 
                               bundle="${storeText}"/></b></p>
      						<br/>
      						</td>
      					</tr>
      					<tr cellpadding="2" cellspacing="2">
      						<td align="left" cellpadding="2" 
                               cellspacing="2">
      						<table border="1">
      						<tr style="border:1px" 
                               cellpadding="2"
                               cellspacing="2"> 
                                   <td cellpadding="2" cellspacing="2"
                                   ><u><b>ProductID</b></u></td>
                                   <td>|</td><td cellpadding="2" 
                                    cellspacing="2"><u>
                                    <b>Imported</b></u>
                                    </td><td>|</td><td 
                                    cellpadding="2" 
                                    cellspacing="2"><u><b>
                                    Imported From Country</b></u></td>
                                    <td>|</td>
      						</tr>
                              <c:forEach var="catalogEntry" items=
                               "${catentries}">
       
                                 <tr align="center" cellpadding="2" 
                                 cellspacing="2">
                               <td>${catalogEntry.catalogEntryIdentifier.
                                 uniqueID}</td>
                                   <td>|</td>
      	
      		<%-- Search the value for UserDataField & only output the value 
               for Imported & Imported From--%>
      			<c:forEach var="userDataField" items="${catalogEntry.
                    userData.userDataField}">
                    <c:if test="${userDataField.typedKey == 'imported' || 
                     userDataField.typedKey == 'imported_country'}">
      			<td>${userDataField.typedValue}</td><td>|</td>
      		  </c:if>
      		  </c:forEach>
      		</tr>
      		</c:forEach>
      		</table>
      		</td>
      	</tr>	
      	
      	</tbody>
       </table>
       </div>
       </div>		
       <%@ include file="../../../include/LayoutContainerBottom.jspf"%>
      	</div>
      </body>
      </html>

      The page retrieves data by using the getData tag.

    7. Save the file.
  4. Update ProductDisplay.jsp to link to the ImportDisplay.jsp file:
    1. Open the Java EE perspective.
    2. In the Enterprise Explorer view, expand Stores > WebContent > MadisonsStorefrontAssetStore > ShoppingArea > CatalogSection > CatalogEntrySubsection.
    3. Open the ProductDisplay.jsp file.
    4. In the ProductDisplay.jsp, locate the following lines, which are at the bottom of the file:
      <c:param name="productId" value="${productId}"/>
      	</c:import>
      	<%out.flush();%> </div>
    5. Add an Import link. Add the following code after </div> tag:
      <!-- Tutorial changes - BEGIN -->
       <div>
        <br/>
        <br/>
        <img src="<c:out value="${jspStoreImgDir}" />images/
         expand_icon_hover.gif" />
      
        <c:url var="ImportViewURL" value="ImportView">
      	<c:param name="langId" value="${langId}" />
          <c:param name="storeId" value="${WCParam.storeId}" />
      	<c:param name="catalogId" value="${WCParam.catalogId}" />
      	<c:param name="catalogEntryID" value="${productId}" />
        </c:url>
        <a href='<c:out value="${ImportViewURL}" />' id="">
         Import Information </a>
       </div>
      <!-- Tutorial changes - END -->
  5. Update the Struts configuration file to register the new JSP page. Now that you have created a JSP template to generate the Web service response, you can register the JSP page in the Struts configuration file so that it can be used by the WebSphere Commerce Web services framework. Since WebSphere Commerce is Struts-enabled, you can register the JSP pages by adding the appropriate Struts configuration to associate the view with a physical JSP page:
    1. In the Enterprise Explorer view, expand Stores > WebContent > WEB-INF > struts-config-ext.xml.
    2. Open the struts-config-ext.xml file.
    3. Add the following code in the global forward section:
      <!-- Tutorial changes - BEGIN -->
      	
      <forward className="com.ibm.commerce.struts.ECActionForward" name=
        "ImportView/10651" path="/ShoppingArea/CatalogSection/CatalogEntrySubsection/
        ImportDisplay.jsp"/>
      	
      <!-- Tutorial changes - END -->
    4. Copy and paste the following sample code before the closing </actions-mapping> tag at the bottom of the file:
      <!-- Tutorial changes - BEGIN -->
      	
      <action path="/ImportView" type="com.ibm.commerce.struts.BaseAction">
      <set-property property="https" value="10651:1"/>
      </action>
      	
      <!-- Tutorial changes - END -->
    5. Save the file.
  6. Create an access control policy to secure the new import view. By default, only the users assigned to the Site Administrator role can access to the view created in the preceding step. In this step, you update the Catalog service access control policy to permit all users to access the new view.
    1. Create a file called ImportViewCommand.xml under the directory <WCDev_Dir>\xml\policies\xml.
    2. Copy and paste the following access control policy XML into this file:
      <?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
      <!DOCTYPE Policies SYSTEM "../dtd/accesscontrolpolicies.dtd">
      		
      <Policies>
      		
      <Action Name="ImportView" CommandName="ImportView">
      </Action>
      <ActionGroup Name="AllSiteUsersViews" OwnerID="RootOrganization">
      <ActionGroupAction Name="ImportView"/>
      </ActionGroup>
      		
      </Policies>
    3. Save the file.
    4. Stop the test environment.
    5. Use the command prompt to switch to the <WCDev_Dir>\bin directory.
    6. Load the ImportviewCommand.xml by running the following command:
      acpload inst1db db2admin passw0rd ImportViewCommand.xml db2admin
    7. Navigate to the <WCDev_Dir>\logs directory. Inspect the acpload.log and messages.txt files to ensure that the access control policy loaded successfully. There may not be a messages.txt file present if everything loaded successfully.
  7. Restart the server and publish the changes.

Testing the customization in the store

In this section, you will test your changes in the storefront.

  1. Ensure that the WebSphere Commerce Server is started.
  2. Open the Mozilla® Firefox® Web browser and enter the following URL: http://localhost/webapp/wcs/stores/servlet/StoreView?storeId=10152.

    Note: Change the storeId according to your environment.

  3. Click Furniture to enter into Category Display page.
  4. Select Lounge Chairs.
  5. Click White Fabric Roll Arm Chaise. The link for the Import information is displayed, as shown in Figure 4.
    Figure 4. Product Display page along with new Import information
    Product Display page along with new Import information
  6. Click Import information. Select Yes if the security alert page displays. In Firefox, click the I Understand the Risks then the Add Exception button. Click Get Certificate and then the Confirm Security Exception button. The Import information page displays as shown in Figure 5.
    Figure 5. New JSP with Import information
    New JSP with Import information

Invoking the modified Web service from the SOAP UI tool

You can create new views of the data returned by the same service by defining a new access profile. Four different access profiles are created for demonstration purposes. These four access profiles return different sets of data depending on which channel the service is invoked from. The soap UI tool is used in this tutorial to send the request and analyze the response.

For demonstration purposes, this is considered as the third channel. In this case, the client can be an Enterprise Service Bus (ESB) creating an XML message that conforms to our standard and sending that message directly to our service. Note that no Java client or JSTL tag library is used in this case. The client creates and sends the message via some other mechanism.

  1. Open the soapUI tool by double-clicking the soapUI X.x icon on the desktop.
  2. Import the sample project from C:\Lab_Instructions\soapui-project.xml.
  3. Expand Projects as shown in Figure 6.
    Figure 6. soapUI with sample project loaded
    soapUI with sample project loaded

Invoke using the Store access profile

  1. Double-click on StoreProfile Request. Figure 7 represents the request-response display area.
    Figure 7. Request-Response display area
    Request-Response display area

    Examine the contents of the request on the left hand side of the screen.

  2. Click on the green right arrow on the top left screen to send the request to the WebSphere Commerce server.
  3. Examine the contents of the response on the right hand side of the screen.

Note: In this example, the MyCompany_Store_CatalogEntryAllDescriptions access profile is used to get all the descriptions for a given catalog entry, including the additional data in the custom tables.

Invoke using the Admin access profile

  1. Double-click on AdminProfile Request.
  2. Click on the green right arrow on the top left screen to send the request to the WebSphere Commerce server.
  3. Examine the contents of the response on the right hand side of the screen.

Note: In this example, the MyCompany_Admin_All access profile is used to get the following data, plus additional data in the custom tables: Catalog Entry with description, Catalog Entry price, Catalog Entry shipment, Catalog Entry children, Catalog Entry parent catalog group, Catalog Entry merchandising associations, Catalog Entry attributes, Catalog Entry parent catalog entry, Catalog Entry base item, and Catalog Entry item spc.

Invoke using the Kiosk access profile

  1. Double-click on KioskProfile Request.
  2. Click on the green right arrow on the top left screen to send the request to the WebSphere Commerce server.
  3. Examine the contents of the response on the right hand side of the screen.

Note: In this example, the MyCompany_Store_CatalogEntryDetailsWithKioskInfo access profile is used to get the following data, plus additional data in the custom tables that includes data in the kiosk table: Catalog Entry with description, Catalog Entry price, and Catalog Entry parent catalog group.

Invoke using the Mobile access profile

  1. Double-click on MobileProfile Request.
  2. Click on the green right arrow on the top left screen to send the request to the WebSphere Commerce server.
  3. Examine the contents of the response on the right hand side of the screen.

Note: In this example, the MyCompany_Mobile_Description access profile is used to get the following data, plus additional data in the custom tables: Catalog Entry with description.


Invoking the modified Web service from the Madison’s Mobile Store

In this section, you use the same get data tag used in the Web store to retrieve the additional information by using the mobile access profile. You update the mobile store CachedProductDisplay.jsp to include the extended information. After completing the customization, shoppers can see "Import information" in the Product Display page. For demonstration purposes, this is considered as the fourth channel. This is just an extension of the Web channel and the same coding technique is used.

  1. Update the mobile store's CachedProductDisplay.jsp to include the extended information:
    1. Open the Java EE perspective.
    2. In the Enterprise Explorer view, expand Stores > WebContent > MadisonsStorefrontAssetStore > mobile > Snippets > Catalog > CatalogEntryDisplay.
    3. Open the CachedProductDisplay.jsp file.
    4. In the CachedProductDisplay.jsp, locate the following lines, which are at the bottom of the file:
      <div id="product_description" class="description_container">
       <h3><fmt:message key="DESCRIPTION" bundle="${storeText}" />:</h3>
        <p><c:out value="${product.description.longDescription}" 
         escapeXml="false"/></p>
    5. Add the following code after </p> tag:
      <%-- Tutorial Changes – BEGIN --%>
      <%-- Create a table to display the value for ProductID, Import & 
       Import Country --%>
       <wcf:getData var="catentries" type="com.ibm.commerce.catalog.facade.  
        datatypes.CatalogEntryType[]" expressionBuilder="getCatalogEntryWithImport">
      	<wcf:contextData name="storeId" data="${WCParam.storeId}"/>
      	<wcf:contextData name="catalogId" data="${WCParam.catalogId}"/>
      	<wcf:contextData name="langId" data="${WCParam.langId}"/>
      	<wcf:param name="catalogEntryId" value="${catalogEntryID}"/>
      	<wcf:param name="accessProfile" value="MyCompany_Mobile_Description"/>
       </wcf:getData>	
      	<div>
      	 <table id="WC_CachedItemDisplay_Table_1">
      		<tbody align="center">
      			<tr align="left" cellpadding="2" cellspacing="2" >
      			 <td align="left" cellpadding="2" cellspacing="2">
      			 <p align="left"> <b>Import Information</b></p>
      			 </td>
      			</tr>
      			<tr cellpadding="2" cellspacing="2">
      			 <td align="left" cellpadding="2" cellspacing="2">
      			 <table border="1">
      			 <tr style="border:1px" cellpadding="2" cellspacing="2">
      			 <td cellpadding="2" cellspacing="2"><b>ProductID
                    </b></td><td cellpadding="2" cellspacing="2">
                    <b>Imported</b></td><td cellpadding="2" 
                    cellspacing="2"><b>Imported From Country</b></td>
      			 </tr>
      			 <c:forEach var="catalogEntry" items="${catentries}">
      		
      			 <tr align="center" cellpadding="2" cellspacing="2">
      			 <td>${catalogEntry.catalogEntryIdentifier.uniqueID}</td>
      		
      	<%-- Search the value for UserDataField & only output the value for 
            Imported & Imported From--%>
      	   <c:forEach var="userDataField" items="${catalogEntry.
              userData.userDataField}">
      	   <c:if test="${userDataField.typedKey == 'imported' || 
              userDataField.typedKey == 'imported_country'}">
      		<td>${userDataField.typedValue}</td>
      	   </c:if>
      	   </c:forEach>
      			</tr>
      			</c:forEach>
      			</table>
      			</td>
      			</tr>	
      		
      		</tbody>
      	</table>
      	</div>
      <%-- Tutorial Changes – END --%>
    6. Save the file.
  2. Open the Mozilla Firefox browser and set the user agent to iPhone 3.0. Go to Tools > Default User Agent and select iPhone 3.0.

    Note: This sets the Mozilla Firefox browser to send the iPhone® user agent information to the WebSphere Commerce server. The WebSphere Commerce server identifies mobile requests based on the user agent information.

  3. Enter the following URL in the browser: http://localhost/webapp/wcs/stores/servlet/StoreView?storeId=10152.

    Note: Change the storeId according to your environment.

  4. Click Furniture to enter the Category Display page.
  5. Select Lounge Chairs.
  6. Click White Fabric Roll Arm Chaise.
  7. You see the Import information above the "Next Product" link as shown in Figure 8.
    Figure 8. Madisons Mobile page
    Madisons Mobile page

As you can see that in the mobile channel, we have demonstrated the re-use of the same get-data tag with a different access profile.


Conclusion

This tutorial showed how you can customize the Catalog service to support the extended catalog entry information and to use the services from multiple channels. You learned to perform the following customizations:

  • Update the WebSphere Commerce schema to store the new information.
  • Customize the Catalog service to include the new information as user data in the CatalogEntry noun.
  • Use the same service code no matter which channel the client is coming from.

Acknowledgements

The author would like to thank Biswajit Dash and Scott Guminy for their review of this tutorial.


Download

DescriptionNameSize
Sample project filescode_sample.zip141KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=799093
ArticleTitle=Building WebSphere Commerce services for cross-channel commerce
publish-date=02292012