为跨渠道的商务构建 WebSphere Commerce 服务

本教程对如何开发 Web、移动和第三方应用程序使用的服务提供指南。本教程还介绍了 WebSphere® Commerce 基于 SOA 的编程模型,以及如何构建您可以跨不同渠道进行重用的业务逻辑。

N Krishnan, IT 专家, IBM

作者的照片N Krishnan 是 IBM India Software Labs 的一名 IT 专家。他是 WebSphere Commerce 开发团队的成员,并且在 Retail EBO 团队中开发了零售解决方案。目前,他在 IBM Software Services for Industry Solutions 任职 WebSphere Commerce 顾问。



2012 年 6 月 04 日

关于本教程

本教程对如何开发 Web、移动和第三方应用程序使用的服务提供了指南。本教程还介绍了 WebSphereCommerce 基于 SOA 的编程模型,以及如何构建您可以跨不同渠道进行重用的业务逻辑。本教程主要针对于实现 WebSphere Commerce 解决方案的架构师、顾问和开发人员。

目标

在本教程中,您将定制 Catalog 服务,以支持扩展的目录项信息和使用来自多个渠道的服务。该定制包括:

  • 更新 WebSphere Commerce 架构以存储新信息。
  • 定制 Catalog 服务,将新信息包括为 CatalogEntry 名词中的用户数据。
  • 无论客户端来自哪个渠道,都会重用相同的服务代码。

先决条件

本教程是针对具有中等水平技能和经验的 WebSphere Commerce 开发人员而编写。除此之外,您还需要熟悉面向服务架构 (SOA) 概念。

系统要求

为了在这里创建一个示例,您需要安装和配置 WebSphere Commerce Developer V7。您还需要使用 Furniture catalog 发布的 Madison Extended 站点 Starter Store。

在您开始本教程之前:

  1. 下载并安装 soapUI 应用程序,以使用 Web 服务。
  2. 将本教程提供的 code sample.zip 文件 解压缩至一个临时位置,如 C:\Lab_Instructions
  3. <WCDev_Dir> 代表安装了 WebSphere Commerce 开发环境的目录,例如,C:\WebSphere\WCToolkit.

持续时间

3 至 4 小时


定制 WebSphere Commerce 服务

定制 WebSphere Commerce 架构

在本节中,您将在 WebSphere Commerce 架构中添加新表,以存储其他产品信息。图 1 显示了架构中的变更。

图 1. 架构扩展
架构扩展

该架构显示了新表以及它们与现有表的关系。新表包括 XPRODUCT、XPRODUCT_KIOSK 和 XCAREINSTRUCTION:

  • XPRODUCT 表:该表对 CATENTRY 表有一个外键。出于演示目的,该表为一个给定的目录项保存有关导入信息的数据。这使数据服务层 (DSL) 可以填充来自 CatalogEntry 名词的 USERDATA 元素中的 XPRODUCT 表的数据。

    :USERDATA 是一个开箱即用的扩展点,允许客户将简单的 “名称-值” 对添加到名词或服务响应。该数据来自对名词的基表有一个外键的自定义表。该关系必须是一对一的。 支持用户数据的一对多关系。

  • XPRODUCT_KIOSK 表:该表对 CATENTRY 表也有一个外键。出于演示目的,该表保存特定于 Kiosk 渠道的数据。这使数据服务层 (DSL) 可以填充来自 CatalogEntry 名词的 USERDATA 元素中的 XPRODUCT_KIOSK 表的数据。
  • XCAREINSTRUCTION 表:该表对 CATENTDESC 表有一个外键,使 DSL 可以填充 CatalogEntryDescription 名词部分的 ATTRIBUTE 元素中的数据。

XCAREINSTRUCTION 表对 CATENTRY 表有一个外键,向 DSL 搜索函数提供支持。该表保存有关护理指示的信息,例如,对于一个给定目录项的 “易碎,小心处理”。

要添加新表:

  1. 连接到开发数据库并执行以下 SQL:
    1. 单击 Start > Run 并输入 db2cmd
    2. 将目录更改至 C:\Lab_Instructions
    3. 使用 db2 connect to <DB_Name> 连接到数据库。
    4. 运行 SQL 以创建表,如下所示:
      	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. 通过运行以下 SQL 语句,用一些样例数据填充自定义表。确保您在下面所示的 SQL 中替换了正确的 CatalogEntry_id。在上面步骤 1a 中所打开的 DB2® 命令提示符后,输入以下命令,将样例数据插入自定义表:
    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');

定制物理层以包括新信息

在本节中,您将生成对象-关系元数据和物理服务数据对象 (SDO),从而定制物理层。为此,您要使用一个名为 "Data Service Layer" 向导的工具。

物理 SDO 是服务数据对象,代表 WebSphere Commerce 架构中的表。每个数据对象类型与架构中的一个表定义相对应,而每个数据对象属性与一个表列或对另一个数据对象类型的引用相对应。这些引用与数据库表之间的外键关系相对应。Data Service Layer 向导用于为您的架构定制生成对象-关系元数据和物理数据对象。

每个服务模块都有对象-关系元数据,用于包含将物理数据对象关联到数据库表的信息。自定义对象-关系元数据被存储在组件配置扩展目录中,自定义物理 SDO 被存储在 WebSphereCommerceServerExtensionsLogic 项目中。

该向导执行以下功能:

  • 如果扩展配置文件夹不存在,为 Catalog 服务模块创建一个扩展配置文件夹。目录路径是 WC_eardir\xml\config\com.ibm.commerce.catalog-ext
  • 生成一个自定义对象-关系元数据文件。该文件位于 WC_eardir\xml\config\com.ibm.commerce.catalog-ext\wc-object-relational-metadata.xml
  • 生成 SDO Java 类并将它放在 WebSphereCommerceServerExtensionsLogic 项目中。

    :IBM® 提供的 CatalogEntry 对象被扩展以包括新的 getter。

  • 创建一个实用工具 Java™ 类,返回服务模块的物理 SDO root 类(及其包)。该 root 类确保服务模块的所有 WebSphere Commerce 物理 SDO,以及用于定制的任何其他物理 SDO 在运行时均可用。

    :该配置告诉系统实例化新 CatalogEntry SDO,而不是 IBM 提供的 CatalogEntry SDO,所以它现在可以访问新数据。

  • 创建一个扩展服务模块配置文件,指引 WebSphere Commerce 在 WC_eardir\xml\config\com.ibm.commerce.catalog-ext\wc-component.xml 中使用新创建的目录物理 SDO 类。
  • 创建一个扩展业务对象中介 (object mediator) 来配置文件,对业务对象中介进行配置,以在 CatalogEntry 名词的用户数据中包括来自 XPRODUCT、XPRODUCT_KIOSK 和 XCAREINSTRUCTION 表的数据。该文件位于 WC_eardir\xml\config\com.ibm.commerce.catalog-ext\wc-business-object-mediator.xml

要运行 Data Service Layer 向导:

  1. 选择 File > New > Other > WebSphere Commerce > Data Service Layer
  2. 单击 Next
  3. 选中 Extend a default WebSphere Commerce service module

    :在本教程中,目录服务模块将被扩展。因此,必须选中该选项。如果您创建一个新服务,那么您必须选中另一个选项 "Work with a custom service module"。

  4. 单击 Next
  5. 输入以下信息:
    1. Service module:选中 com.ibm.commerce.catalog
    2. Extension class prefix:MyCompany
    3. Extension Java package name:com.mycompany.commerce.catalog
  6. 单击 Next
  7. 选中 XPRODUCTXPRODUCT_KIOSKXCAREINSTRUCTION 表。
  8. 展开 XPRODUCTXPRODUCT_KIOSK XCAREINSTRUCTION 表,并确保所有列都被选中。
  9. 单击 Next
  10. UserData 下面,确保 IMPORTED、IMPORTED_COUNTRY、AISLE_NUMBER、AISLE_INFORMATION 和 CAREINSTRUCTION 数据库列被设置为 "True",如图 2 所示。如果其他列的默认设置为 "True",则将它们设置为 "False"。

    :该步骤确保您要用到的列都被包括在逻辑架构的 UserData 区域。

    图 2. 在 DSL 向导中使用 Metadata Editing 扩展表
    在 DSL 向导中使用 Metadata Editing 扩展表
  11. 单击 Finish
  12. 确认在 WebSphereCommerceServerExtensionsLogic 项目中创建了以下文件,如图 3 所示。
    图 3. 从 DSL 向导生成的文件
    从 DSL 向导生成的文件

    :在 WebSphereCommerceServerExtensionsLogic 项目中 com.mycompany.commerce.catalog.facade.server.entity.datatypes 下面生成的物理 SDO。这些 SDO 为自定义的目录服务提供一个数据库的 Java 视图。

添加查询模板以包括新的信息

在本节中,您将添加查询模板文件,检索更多产品信息。查询模板文件存储服务模块的 SQL 和访问配置文件定义,将服务模块与业务逻辑层完全隔离。

查询模板文件包括以下内容:

  • 一个符号定义部分,定义您查询模板将使用的表:CATENTRY、CATENTDESC、XPRODUCT、XPRODUCT_KIOSK 和 XCAREINSTRUCTION。
  • 相关的 SQL 语句,定义 SQL 查询。您可以重用这些查询来构建在 PROFILE 部分中定义的不同访问配置文件。

    :访问配置文件决定有多少信息被返回。例如,"Summary" 可能只返回一个产品描述,但 "Details" 配置文件可能返回描述、价格、属性等等。由于您添加了更多数据,您必须创建一个新的访问配置文件。

  • 一个新的访问配置文件 MyCompany_All,与 XPath 键一起使用,以标识 SQL 模板查询。
  • 查询模板中还定义了许多其他访问配置文件,它们将用于不同渠道。

创建一个客户 Get 查询模板文件:

  1. 右键单击 WC\xml\config\com.ibm.commerce.catalog-ext 文件夹。(如果 com.ibm.commerce.catalog-ext 文件夹不可见,选择 WC\config 文件夹并选择 File > Refresh。)

    :自定义的 TPL 文件必须被创建在 <component name>-ext 文件夹下面。

  2. 单击 New > File
  3. 将文件命名为 wc-query-MyCompanyCatalogEntry-get.tpl

    :该名称必须以 wc-query- 开始,并以后缀 .tpl 结束。

  4. 单击 Finish
  5. 复制并粘贴以下查询模板到文件中:
    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. 保存文件。

创建一个访问控制策略以保护新信息

上一节创建了一个新的访问配置文件 MyCompany_All。在默认情况下,只有拥有站点管理员角色的用户可以访问该新数据。在本节中,您将更新 Catalog 服务的访问控制策略,以说明所有用户都具有查看该数据的访问权限。

新的策略为 MyCompany_All 访问配置文件定义了一个新的动作,并将新动作添加到 CatalogEntry 的 All users 组。

  1. 创建以下文件:<WCDev_Dir>\xml\policies\xml\MyCompanyCatalogAccessControlPolicies.xml
  2. 复制并粘贴以下访问控制策略 XML 到该文件中:
    	<?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. 运行 acpload 命令以加载访问控制策略:
    1. 打开命令提示符,并导航至 <WCDev_Dir>\bin
    2. 运行以下命令:
      acpload inst1db db2admin passw0rd MyCompanyCatalogAccessControlPolicies.xml 
       db2admin
    3. 导航至 <WCDev_Dir>\logs 目录。

检查 acpload.logmessages.txt 文件,确保访问控制策略已成功加载。如果一切都成功加载,就不可能有一个 messages.txt 文件。

利用 JUnit 验证 Catalog 定制

本节演示如何使用 JUnit 测试您的定制。JUnit 是一个独立的 Java 程序,它将使用 IBM Client 库(一组 Java API)访问 WebSphere Commerce 服务。它通过发送 HTTP 请求给服务实现该目的。该单元测试假设一个 starter store 的 store ID 为 10152 (MadisonsESite),并且 catalog ID 为 10001 (Extended Sites Catalog Asset Store),这些 ID 已被发布到您的 WebSphere Commerce 开发环境。

为了演示,这被视为调用服务的 First Channel

  1. 将 Catalog 服务的 JUnit Test 项目导入到您的工作区。在 C:\Lab_Instructions 文件夹中提供 项目交换 zip 文件 CatalogExtensions-UnitTest.zip
  2. 在 Enterprise Explorer 视图中,打开:
    CatalogExtensions-UnitTest\src\com.mycompany.commerce.catalog.facade.client\
     CatalogFacadeExtensionClientTest.java

    注:花一点时间来了解这个 JUnit 测试用例是如何构建的。

    有两个重要的方法:

    • setUp():该方法初始化 businesscontext 和 callbackhandler 对象。这些对象用于创建 CatalogEntry 客户端。
    • testCatalogEntryCustomization():在该方法中会发生对服务的实际调用。通过使用 SelectionCriteriaHelper 类形成 Get 表达式,并使用目录项正面客户端的 createGetVerb 方法创建 Get 动词。使用之前在 setUp 方法中所创建的 CatalogEntry 客户端执行 get 请求。在响应对象中导航并打印(或声明)UserData 元素(在本例中,它是 ShowCatalogEntryDataAreaType 类型)。
  3. 请注意已配置的特定于计算机值的下列常数:
    • USER_ID = wcsadmin
    • PASSWORD = passw0rd
    • STORE_ID = 10152(由于我们在本教程中使用 eSite,我们已经设置了 MadisonESite 商店的 StoreID。)
    • CATALOG_ID = 10001(由于为本教程设置的 eSite 使用目录资产存储,我们已经设置目录资产存储的 catalogID。)
    • catEntryId = 10002(这是用于填充样例数据的 catentryId。)
  4. 在 Server 页面上,右键单击 WebSphere Commerce Test Server 并选择 Start
  5. 如果 WebSphere Commerce (WC) 项目尚未发布到 WebSphere Commerce Test Server,请发布 WC 项目:
    1. 在 Server 页面上,右键单击 WebSphere Commerce Test Server 并选择 Add and Remove Projects
    2. 选中 WC 项目。
    3. 单击 Add
    4. 单击 Finish

    如果 WC 项目已经被发布,右键单击 WebSphere Commerce Test Server 并选择 Publish

  6. 在 WebSphere Commerce Developer 中设置一个 TCP/IP 监控器。您将使用这个 TCP/IP 监控器来观察在您所创建的 WebSphere Commerce 服务进出的请求和响应文档。要创建一个将请求转发给 WebSphere Commerce 的 TCP/IP 监控器,步骤如下:
    1. 选择 Window > Preferences
    2. 在 Preferences 页面上,单击 Run/Debug > TCP/IP Monitor
    3. 单击 Add
    4. 输入以下信息:
      • Local monitoring port:81
      • Hostname:localhost - WebSphere Commerce Server 的主机名,目录服务在该主机上运行。
      • Port:80 用于 WebSphere Commerce Developer
      • 选中复选框 Start monitor automatically
    5. 单击 OK
    6. 选中所创建的 TCP/IP 监控器。
    7. 单击 Start。(这应该已启动,在这种情况下请跳至下一步。)
    8. 单击 OK
  7. 右键单击 CatalogExtensionFacadeClientTest.java 类,并选择 Run As > JUnit Test。您将在 TCP/IP 监控器上看到,在客户端和服务器之间传输的请求和响应 XML 文档。

    验证结果:

    • 该测试执行一个 Get 请求,以检索一个 CatalogEntry 名词,包括 Product 扩展和护理指示数据。
    • Get 请求使用了 /CatalogEntry[CatalogEntryIdentifier[(UniqueID=)]] 的 XPath 键,以及在上面步骤 3 中所定义的 MyCompany_All 访问配置文件。
    • 数据服务层使用 XPath 键和访问配置文件来标识 SQL 查询模板,从而针对数据库运行并用结果填充物理数据对象。
    • 业务对象中介将物理数据对象转换为逻辑名词。该流程将更多产品信息填充到 CatalogEntry 名词的 UserData 元素,并将护理指示填充到 Catalog 描述名词部分的属性元素。

从 Madisons store 调用修改后的 Web 服务

在本节中,您会创建一个 JSP 文件,使用 getData 标记显示扩展的目录项信息。您要更新 ProductDisplay.jsp 以链接到该 JSP 文件,获取扩展的目录项信息。完成定制之后,购物者会在 Product Display 页面上看到一个 "Import information" 的链接。当购物者单击该链接时,将会显示扩展的目录项 JSP 页面。为了演示,这被视为从 Web 渠道调用 WebSphere Commerce 的 Second Channel。IBM 提供了一套 JSTL 标记,简化了访问服务。

  1. 创建一个店面的 get-data-config 文件。Import information 的 Country 被添加到 CatalogEntry 名词的 UserData 元素。要从 catalogEntry 提取 Import information,创建一个店面的 get-data-config 文件,覆盖目录组件的配置文件。JSP 页面通过使用表达式构建器来引用 getData 标记,并访问配置文件名称。<wcf:getData> 标记从 WebSphere Commerce 服务检索服务数据对象,并用一个 ID 将它们与一个声明的脚本变量关联。
    1. 在 Enterprise Explorer 视图中,展开 Stores > WebContent > WEB-INF > config
    2. 右键单击 config,然后单击 New > Folder
    3. 在 Folder name 字段中,输入 com.ibm.commerce.catalog-ext
    4. 单击 Finishcom.ibm.commerce.catalog-ext 文件夹被创建在 config 目录下面。
    5. 右键单击 com.ibm.commerce.catalog-ext 并选择 New > File
    6. 在 File name 字段中,输入 get-data-config.xml
    7. 单击 Finish。在 get-data-config.xml 文件中输入以下代码:
      <?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>

      对于 <expression-builder>

      name
      getCatalogEntryWithImport 由 getData 标记使用,并且由 JSP 页面引用,该页面将在下一步中创建。
      data-type-name
      名词。
      expression-template
      在 wc-query-MyCompanyCatalogEntry-get.tpl 文件中定义的 XPath 指定。

      对于 <param>

      name
      访问配置文件的名称 accessProfile,在 wc-query-MyCompanyCatalogEntry-get.tpl 文件中定义。
      value
      MyCompany_All,在 wc-query-MyCompanyCatalogEntry-get.tpl 文件中定义的访问配置文件的值
    8. 保存文件。
  2. 扩展属性文件:
    1. 在 Enterprise Explorer 视图中,展开 Stores > Java Resources: src > MadisonsStorefrontAssetStore > storetext.properties
    2. 打开 storetext.properties 文件。
    3. 为新标题添加头部属性:
      #-------------------------------------------
      # Tutorial - START
      #-------------------------------------------
      productImportDisplayTitle=Import information
      #-------------------------------------------
      # Tutorial - END
      #-------------------------------------------

      :Properties 文件包含在页面上显示的语言特定的标签。

  3. 创建 ImportDisplay.jsp 文件,以显示店中的 Import information:
    1. 在 Java EE 透视图中打开 WebSphere Commerce Developer
    2. 在 Enterprise Explorer 视图中,导航至 Stores > WebContent > MadisonsStorefrontAssetStore > ShoppingArea > CatalogSectio > CatalogEntrySubsection
    3. 右键单击 CatalogEntrySubsection,并选择 New > Web Page
    4. File name 字段中,输入 ImportDisplay.jsp。在 Template 部分,确保已选中 Basic Templates 下面的 JSP
    5. 单击 Finish
    6. 单击 Source 选项卡,并将 ImportDisplay.jsp 文件的内容替换为以下代码样例:
      	<%--
      	  *****
      	  * 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>

      该页面使用 getData 标记检索数据。

    7. 保存文件。
  4. 更新 ProductDisplay.jsp 以链接到 ImportDisplay.jsp 文件:
    1. 打开 Java EE 透视图。
    2. 在 Enterprise Explorer 视图中,展开 Stores > WebContent > MadisonsStorefrontAssetStore > ShoppingArea > CatalogSection > CatalogEntrySubsection
    3. 打开 ProductDisplay.jsp 文件。
    4. ProductDisplay.jsp 中找到以下代码行,它们位于文件的末尾:
      <c:param name="productId" value="${productId}"/>
      	</c:import>
      	<%out.flush();%> </div>
    5. 添加一个 Import 链接。在 </div> 标记后面添加以下代码:
      <!-- 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. 更新 Struts 配置文件,以注册新的 JSP 页面。现在,您已创建了一个 JSP 模板,以生成 Web 服务响应,您可以在 Struts 配置文件中注册该 JSP 页面,以便 WebSphere Commerce Web 服务框架可以使用它。因为 WebSphere Commerce 已启用 Struts,您可以通过添加相应的 Struts 配置来注册 JSP 页面,将视图与物理 JSP 页面关联起来:
    1. 在 Enterprise Explorer 视图中,展开 Stores > WebContent > WEB-INF > struts-config-ext.xml
    2. 打开 struts-config-ext.xml 文件。
    3. 在 global forward 小节中添加以下代码:
      <!-- Tutorial changes - BEGIN -->
      	
      <forward className="com.ibm.commerce.struts.ECActionForward" name=
        "ImportView/10651" path="/ShoppingArea/CatalogSection/CatalogEntrySubsection/
        ImportDisplay.jsp"/>
      	
      <!-- Tutorial changes - END -->
    4. 复制以下样例代码,并将其粘贴到文件末尾的闭合 </actions-mapping> 标记前面:
      <!-- Tutorial changes - BEGIN -->
      	
      <action path="/ImportView" type="com.ibm.commerce.struts.BaseAction">
      <set-property property="https" value="10651:1"/>
      </action>
      	
      <!-- Tutorial changes - END -->
    5. 保存文件。
  6. 创建一个访问控制策略,以保护新的导入视图。在默认情况下,只有分配了 Site Administrator 角色的用户可以访问在上一步中创建的视图。在该步骤中,您会更新 Catalog 服务访问控制策略,以允许所有用户访问新视图。
    1. 在目录 <WCDev_Dir>\xml\policies\xml 下面创建一个名为 ImportViewCommand.xml 的文件。
    2. 复制并粘贴以下访问控制策略 XML 到该文件:
      <?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. 保存文件。
    4. 停止测试环境。
    5. 使用命令提示符,切换到 <WCDev_Dir>\bin 目录。
    6. 通过运行以下命令加载 ImportviewCommand.xml
      acpload inst1db db2admin passw0rd ImportViewCommand.xml db2admin
    7. 导航至 <WCDev_Dir>\logs 目录。检查 acpload.logmessages.txt 文件,以确保已成功加载访问控制策略。如果一切都成功加载,可能不会出现 messages.txt 文件。
  7. 重新启动服务器并发布变更。

在店中测试定制

在本节中,您将在店面测试您的修改。

  1. 确保 WebSphere Commerce Server 已启动。
  2. 打开 Mozilla® Firefox® Web 浏览器并输入以下 URL:http://localhost/webapp/wcs/stores/servlet/StoreView?storeId=10152

    :根据您的环境修改 storeId。

  3. 单击 Furniture 以进入 Category Display 页面。
  4. 选中 Lounge Chairs
  5. 单击 White Fabric Roll Arm Chaise。显示 Import information 的链接,如图 4 所示。
    图 4. 带有新的 Import information 的 Product Display 页面
    带有新的 Import information 的 Product Display 页面
  6. 单击 Import information。如果显示安全警报页面,请选择 Yes。在 Firefox 中,单击 I Understand the Risks then the Add Exception 按钮。单击 Get Certificate,然后再单击 Confirm Security Exception 按钮。显示 Import information 页面,如图 5 所示。
    图 5. 带有 Import information 的新 JSP
    带有 Import information 的新 JSP

从 SOAP UI 工具调用修改后的 Web 服务

您可以通过定义一个新的访问配置文件,对相同服务返回的数据创建一个新的视图。创建四个不同的访问配置文件以供演示。这四个访问配置文件根据从哪个渠道调用服务而返回不同的数据集。本教程使用了 soap UI 工具,以发送请求并分析响应。

出于演示目的,这被视为 Third Channel。在本用例中,客户端可以是一个 Enterprise Service Bus (ESB),它创建一个符合我们的标准的 XML 消息,并直接将该消息发送至我们的服务。请注意,本用例中没有使用 Java 客户端或 JSTL 标记库。客户端通过其他机制创建并发送消息。

  1. 通过双击桌面上的 soapUI X.x 图标打开 soapUI 工具。
  2. C:\Lab_Instructions\soapui-project.xml 导入 样例项目
  3. 展开 Projects,如图 6 所示。
    图 6. 加载了样例项目的 soapUI
    加载了样例项目的 soapUI

使用 Store 访问配置文件进行调用

  1. 双击 StoreProfile Request。图 7 表示请求-响应显示区域。
    图 7. 请求-响应显示区域
    请求-响应显示区域

    在屏幕的左侧检查请求的内容。

  2. 单击屏幕左上角的绿色右箭头,将请求发送到 WebSphere Commerce 服务器。
  3. 在屏幕的右侧检查响应的内容。

:在本例中,MyCompany_Store_CatalogEntryAllDescriptions 访问配置文件用于获得给定目录项的所有描述,包括在自定义表中的其他数据。

使用 Admin 访问配置文件进行调用

  1. 双击 AdminProfile Request
  2. 单击屏幕左上角的绿色右箭头,将请求发送到 WebSphere Commerce 服务器。
  3. 在屏幕的右侧检查响应的内容。

:在本例中,MyCompany_Admin_All 访问配置文件用于获取以下数据,以及在自定义表中的其他数据: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 和 Catalog Entry item spc。

使用 Kiosk 访问配置文件进行调用

  1. 双击 KioskProfile Request
  2. 单击屏幕左上角的绿色右箭头,将请求发送到 WebSphere Commerce 服务器。
  3. 在屏幕的右侧检查响应的内容。

:在本例中,MyCompany_Store_CatalogEntryDetailsWithKioskInfo 访问配置文件用于获取以下数据,以及在自定义表中的其他数据,包括 kiosk 表中的数据:Catalog Entry with description、Catalog Entry price 和 Catalog Entry parent catalog group。

使用 Mobile 访问配置文件进行调用

  1. 双击 MobileProfile Request
  2. 单击屏幕左上角的绿色右箭头,将请求发送到 WebSphere Commerce 服务器。
  3. 在屏幕的右侧检查响应的内容。

:在本例中,MyCompany_Mobile_Description 访问配置文件用于获取以下数据,以及在自定义表中的其他数据:Catalog Entry with description。


从 Madison 的 Mobile Store 调用修改后的 Web 服务

在本节中,您使用 mobile 访问配置文件检索其他信息时所用的 get data 标记与 Web store 中使用的相同。您要更新 mobile store 的 CachedProductDisplay.jsp 以包括扩展的信息。完成定制后,购物者可以在 Product Display 页面看见 "Import information"。出于演示目的,这被视为 Fourth channel。这只是 Web 渠道的扩展,并且使用了相同的编码技术。

  1. 更新移动商店的 CachedProductDisplay.jsp 以包括扩展的信息:
    1. 打开 Java EE 透视图。
    2. 在 Enterprise Explorer 视图中,展开 Stores > WebContent > MadisonsStorefrontAssetStore > mobile > Snippets > Catalog > CatalogEntryDisplay
    3. 打开 CachedProductDisplay.jsp 文件。
    4. CachedProductDisplay.jsp 中,找到以下代码行,它们位于文件的末尾:
      <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. 在 </p> 标记后面添加以下代码:
      <%-- 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. 保存文件。
  2. 打开 Mozilla Firefox 浏览器并设置用户代理为 iPhone 3.0。转到 Tools > Default User Agent 并选择 iPhone 3.0

    :这将 Mozilla Firefox 浏览器设置为将 iPhone® 用户代理信息发送给 WebSphere Commerce 服务器。WebSphere Commerce 服务器根据用户代理信息识别移动请求。

  3. 在浏览器中输入下列 URL:http://localhost/webapp/wcs/stores/servlet/StoreView?storeId=10152

    :根据您的环境修改 storeId。

  4. 单击 Furniture 进入 Category Display 页面。
  5. 选中 Lounge Chairs
  6. 单击 White Fabric Roll Arm Chaise
  7. 在 "Next Product" 链接上面,您可以看到 Import information,如图 8 所示。
    图 8. Madisons Mobile 页面
    Madisons Mobile 页面

如您所见,在移动渠道中,我们已演示了在不同的访问配置文件中重用相同的 get-data 标记。


结束语

本教程演示了如何通过定制 Catalog 服务支持扩展目录项信息,并使用来自多个渠道的服务。您学习了如何执行以下定制:

  • 更新 WebSphere Commerce 架构以存储新的信息。
  • 定制 Catalog 服务,将新信息包括为 CatalogEntry 名词中的用户数据。
  • 无论客户端来自哪个渠道,都会重用相同的服务代码。

致谢

作者感谢 Biswajit DashScott Guminy 对本教程所作的审阅工作。


下载

描述名字大小
样例项目文件code_sample.zip141KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=819423
ArticleTitle=为跨渠道的商务构建 WebSphere Commerce 服务
publish-date=06042012