You can extend the commerce object hierarchy by subclassing an existing commerce object and adding new object data type properties. Unlike with adding new primitive data type properties (see Adding a Subclass with Primitive Data Type Properties), adding new object data type properties requires that you write code to save and load the object’s properties.

As an example, the following code creates a new class called OrderData. It extends CommerceIdentifierImpl and adds a new String property called miscInformation. A subsequent code example creates a new class called MyOrder, which extends OrderImpl and adds a new OrderData property named orderData.

Note that the OrderData class implements the ChangedProperties interface, which is explained in detail after the code example.

package my_package;

import atg.commerce.order.ChangedProperties;
import atg.commerce.order.CommerceIdentifierImpl;
import java.util.Set;
import java.util.HashSet;
import java.util.Observable;
import atg.repository.MutableRepositoryItem;

public class OrderData extends CommerceIdentifierImpl
             implements ChangedProperties
{
  public OrderData() {
    super();
  }

  // property: miscInformation
  public String getMiscInformation() {
    return (String) getPropertyValue("miscInformation");
  }

  public void setMiscInformation (String pMiscInformation) {
    setPropertyValue("miscInformation", pMiscInformation);
  }

  //
  // Observer implementation
  //
  public void update(Observable o, Object arg) {
    if (arg instanceof String) {
      addChangedProperty((String) arg);
    }
    else {
      throw new RuntimeException("Observable update for " +
         getClass().getName() + " was received with arg type " +
         arg.getClass().getName() + ":" + arg);
    }
  }

  //
  // ChangedProperties implementation
  //

  // property: saveAllProperties
  private boolean mSaveAllProperties = false;

  public boolean getSaveAllProperties() {
    return mSaveAllProperties;
  }

  public void setSaveAllProperties(boolean pSaveAllProperties) {
    mSaveAllProperties = pSaveAllProperties;
  }

  // property: changed
  private boolean mChanged = false;

  public boolean isChanged() {
    return (mChanged || (mChangedProperties != null
                   && ! getChangedProperties().isEmpty()));
  }

  public void setChanged(boolean pChanged) {
    mChanged = pChanged;
  }

  // property: changedProperties
  private HashSet mChangedProperties = new HashSet(7);

  public Set getChangedProperties() {
    return mChangedProperties;
  }

  public void addChangedProperty(String pPropertyName) {
    mChangedProperties.add(pPropertyName);
  }

  public void clearChangedProperties() {
    mChangedProperties.clear();
  }

  // property: repositoryItem
  private MutableRepositoryItem mRepositoryItem = null;

  public MutableRepositoryItem getRepositoryItem() {
    return mRepositoryItem;
  }

  public void setRepositoryItem(MutableRepositoryItem pRepositoryItem) {
    mRepositoryItem = pRepositoryItem;
  }

  // setPropertyValue/getPropertyValue methods
  public Object getPropertyValue(String pPropertyName) {
    MutableRepositoryItem mutItem = getRepositoryItem();
    if (mutItem == null)
      throw new RuntimeException("Null repository item: " + getId());

    return mutItem.getPropertyValue(pPropertyName);
  }

  public void setPropertyValue(String pPropertyName,
                     Object pPropertyValue)
  {
    MutableRepositoryItem mutItem = getRepositoryItem();
    if (mutItem == null)
      throw new RuntimeException("Null repository item: " + getId());

    mutItem.setPropertyValue(pPropertyName, pPropertyValue);
    setChanged(true);
  }
}

As previously mentioned, the code above creates a new OrderData class that extends CommerceIdentifierImpl, implements the ChangedProperties interface, and adds a new String property called miscInformation.

The CommerceIdentifierImpl class is an abstract class that contains a single property called id; it is the base class for all commerce object classes. The class contains the getId() and setId() methods and implements the CommerceIdentifier interface, which contains only the getId() method. The purpose of the id property is to store the repository ID for the given commerce object. The CommerceIdentifier interface provides a standard way for ATG Commerce systems to access the repository IDs of items.

The ChangedProperties interface enhances performance when saving the object by allowing the object’s property values to be set directly to the repository item. The interface contains the properties described in the following table.

Property

Description

changed

A boolean property that returns true if the object has changed since the last update and returns false if it has not.

changedProperties

A Set that contains the names of all changed properties. The property is implemented as a Set to include each property only once.

repositoryItem

Contains the repository item that refers to the object. Having the object contain the repository item eliminates the need to look up the item in the repository when saving it.

saveAllProperties

A boolean property that marks all properties as changed. This causes all properties to be saved to the repository, regardless of whether or not they have changed.

With the OrderData class created, the next step is to add the OrderData property to the Order. The following code creates a new class called MyOrder, which extends OrderImpl and adds a new OrderData property called orderData.

package my_package;

import atg.commerce.order.OrderImpl;
import atg.repository.MutableRepositoryItem;

public class MyOrder extends OrderImpl {
  public MyOrder() {
  }

  // property: orderData
  private OrderData mOrderData = null;

  public OrderData getOrderData() {
    if (mOrderData == null) {
      mOrderData = new OrderData();
      setRepositoryItem((MutableRepositoryItem) getPropertyValue("orderData"));
    }
    return mOrderData;
  }

  public void setOrderData(OrderData pOrderData) {
    mOrderData = pOrderData;
    setPropertyValue("orderData", pOrderData.getRepositoryItem());
  }
}

With the MyCommerceItemImpl subclass created, you now need to integrate the new commerce object into ATG Commerce.

With the OrderData and MyOrder classes created, you now need to integrate the new commerce objects into ATG Commerce. To do so, perform the following steps:

Step 1 of 7 – Extend the Order Repository Definition File

First, extend the Order Repository definition file, orderrepository.xml, to create new item descriptors that support the new properties in OrderData and MyOrder.

The orderrepository.xml file is found in the CONFIGPATH at /atg/commerce/order/orderrepository.xml. To extend it, add a new orderrepository.xml file at /atg/commerce/order/ in your localconfig directory. The new file should define the new item descriptors. During deployment, the ATG platform uses XML file combination to combine the orderrepository.xml files in the CONFIGPATH into a single composite XML file. (For more information on XML file combination, see the Nucleus: Organizing JavaBean Components chapter in the ATG Programming Guide.)

The following orderrepository.xml file defines an item descriptor named myOrder. As a subtype of the order item descriptor, myOrder inherits all of the properties of order. Additionally, it defines one new property, orderData. The definition file also defines a root item descriptor named orderData, which supports the miscInformation property in OrderData.

<gsa-template xml-combine="append">

  <item-descriptor name="order">
    <table name="dcspp_order">
      <property name="type">
        <option value="myOrder" code="1"/>
      </property>
    </table>
  </item-descriptor>

  <item-descriptor name="myOrder" super-type="order"
                                 sub-type-value="myOrder">
    <table name="dcspp_my_order" id-column-name="order_id">
      <property name="orderData" column-name="order_data"
                                 item-type="orderData"/>
    </table>
  </item-descriptor>

  <item-descriptor name="orderData" sub-type-property="type"
                                 version-property="version">
    <table name="dcspp_order_data" type="primary"
                                 id-column-name="order_data_id">
      <property name="type" column-name="type" data-type="enumerated"
             default="orderData"
                                 writable="false">
        <attribute name="useCodeForValue" value="false"/>
        <option value="orderData" code="0"/>
      </property>
      <property name="version" column-name="version" data-type="int"
                                 writable="false"/>
      <property name="miscInformation" column-name="misc_information"
                                 data-type="string"/>
    </table>
  </item-descriptor>

</gsa-template>

The first line in the above XML example begins the GSA template and instructs the XML combiner to append the contents of the tags in this file to the contents of the tags in the file with which it is combined.

The next section defines myOrder as a subtype of the order item descriptor. You do this by adding a new string value for myOrder to the type enumerated property of order. In this case, the new type is called myOrder, and its corresponding integer value is 1. The base orderrepository.xml file contains the other options for the type property. The subsequent section of XML defines the myOrder item descriptor, declaring order as the super-type (or parent item descriptor) and myOrder as the sub-type-value. The sub-type-value refers to the type property in the order item descriptor. Subsequent lines define the name of the table in the database schema that stores the properties of myOrder and then define those properties. In this case, the table is called dcspp_my_order, and it stores a single property named orderData.

The last section of the XML file defines orderData as a root item descriptor. The section then specifies the properties of an orderdata repository item, as well as the database table and columns that store those properties. (Note that each property of a repository item is stored in a database column that has the same name as the property, unless otherwise specified using the column-name attribute.) In this case, the following properties are specified: type, version, and miscInformation. The type and version properties are defined as readonly (writable="false" in the XML) because they are used primarily by the repository. All three properties are stored in the dcspp_my_order database table. The dcspp_my_order table is declared a primary table, meaning it defines a column that stores repository IDs. In this case, that column is order_data_id.

Step 2 of 7 – Modify the Order Repository Database Schema

In step 1, you created the new orderData and myOrder item descriptors, defining both their properties and the database tables and columns that store those properties. Now you need to modify accordingly the Order Repository database schema.

The following DDL statements create the database tables and columns specified in the orderrepository.xml file that you created in step 1.

CREATE TABLE dcspp_my_order (
     order_id          VARCHAR(40)          NOT NULL
REFERENCES dcspp_order(order_id),
     order_data          VARCHAR(40)          NULL,
     PRIMARY KEY(order_id)
);

CREATE TABLE dcspp_order_data (
     order_data_id         VARCHAR(40)           NOT NULL,
     type                  integer               NOT NULL,
     version               integer               NOT NULL,
     misc_information      VARCHAR(254)          NULL,
     PRIMARY KEY(order_data_id)
);
Step 3 of 7 - Subclass OrderTools and SimpleOrderManager to Create the New Object

When an Order is created, the new OrderData object must also be created and added to the Order object. This functionality requires the following two steps:

The following code example subclasses OrderTools and adds a new createOrderData() method.

package my_package;

import atg.commerce.*;
import atg.commerce.order.*;
import atg.repository.*;

public class MyOrderTools extends OrderTools
{
  public MyOrderTools() {
  }

  public OrderData createOrderData() throws ObjectCreationException
  {
    // instantiate the orderData object
    OrderData orderData = new OrderData();
    if (orderData instanceof ChangedProperties)
      ((ChangedProperties) orderData).setSaveAllProperties(true);

    // create the OrderData in the repository and set its id to the
    // repository's id
    try {
      MutableRepository mutRep = (MutableRepository) getOrderRepository();
      MutableRepositoryItem mutItem = mutRep.createItem("orderData");
      orderData.setId(mutItem.getRepositoryId());
      if (orderData instanceof ChangedProperties)
        ((ChangedProperties) orderData).setRepositoryItem(mutItem);
    }
    catch (RepositoryException e) {
      throw new ObjectCreationException(e);
    }

    return orderData;
  }
}

The following code example subclasses SimpleOrderManager, overriding its createOrder() method in order to call the createOrderData() method in the OrderTools object.

package my_package;

import atg.commerce.*;
import atg.commerce.order.*;
import atg.commerce.pricing.*;

public class MyOrderManager extends SimpleOrderManager
{
  public MyOrderManager() {
  }

  public Order createOrder(String pProfileId, String pOrderId,
         OrderPriceInfo pOrderPriceInfo, TaxPriceInfo pTaxPriceInfo,
         ShippingPriceInfo pShippingPriceInfo, String pOrderType)
         throws CommerceException
  {
    MyOrder order = (MyOrder)super.createOrder(
                         pProfileId, pOrderId, pOrderPriceInfo,
                         pTaxPriceInfo, pShippingPriceInfo, pOrderType);

    OrderData orderData = ((MyOrderTools)getOrderTools()).createOrderData();
    order.setOrderData(orderData);

    return order;
  }
}
Step 4 of 7 – Modify the OrderTools and OrderManager Configuration Files

In step 3, you subclassed OrderTools and SimpleOrderManager to create the new OrderData object and add it to the Order. Now you need to configure instances of these new classes in Nucleus.

First, configure an instance of MyOrderManager in Nucleus by modifying the existing OrderManager configuration file. To do so, layer on a configuration file by creating an OrderManager.properties file at /atg/commerce/order/ in your localconfig directory. The OrderManager.properties file should look as follows (Note that no properties need to be configured.):

$class=my_package.MyOrderManager

Second, configure an instance of MyOrderTools in Nucleus by modifying the existing OrderTools configuration file. The OrderTools component controls many aspects of the purchase process, such as mapping between commerce object types and class names, defining the default commerce object types, and mapping between commerce objects and item descriptors. You need to modify the OrderTools configuration file to support the new commerce objects and item descriptors that you have created.

To modify the OrderTools configuration file, layer on a configuration file by creating an OrderTools.properties file at /atg/commerce/order/ in your localconfig directory. The OrderTools.properties file should look as follows:

$class=my_package.MyOrderTools

beanNameToItemDescriptorMap+=\
        my.class.dir.OrderData=orderData,\
        my.class.dir.MyOrder=myOrder

orderTypeClassMap+=\
        default=my.class.dir.MyOrder

The beanNameToItemDescriptorMap property maps Order Repository item descriptors to Bean names. In ATG Commerce, the processors that save and load an Order look for an item descriptor that is mapped to the corresponding commerce object class; the beanNameToItemDescriptorMap property contains this mapping. The configuration file above adds two new entries, mapping the orderData and myOrder item descriptors that you created in step 1 to their corresponding classes. The my.class.dir prefix specifies the Java package in which the class exists.

Because you can have more than one type of Order object, the orderTypeClassMap property maps Order types to class names. This mapping is used by the createOrder() method in the OrderManager; by passing it a type parameter (such as the string “default”), the method constructs and returns an instance of the corresponding class. When one of the createOrder() methods that does not take a type parameter is called, the method constructs and returns an instance of the type specified in OrderTools.defaultOrderType. By default, the defaultOrderType property is set to the type “default,” which, in turn, is mapped to the new MyOrder class in the orderTypeClassMap property in the configuration file above. The my.class.dir prefix indicates some Java package in which the class exists.

Step 5 of 7 – Add a Processor to Save the New Object

You do not need to write new code to save the orderData reference in the MyOrder object. The processors in the updateOrder pipeline, which perform the actual saving of the Order object to the Order Repository, use the Dynamic Beans mechanism to read and write the values of a bean using their property names. However, to save the data in the OrderData object itself, you must create a new processor that saves the OrderData object to the Order Repository and insert that new processor into the updateOrder pipeline. (For more information on the updateOrder pipeline, see Saving Orders in the Configuring Purchase Process Services chapter.)

First, write the Java source code for the new processor. The following Java source file, ProcSaveOrderDataObject.java, serves as an example.

package my_package;

import atg.repository.*;
import atg.commerce.order.*;
import atg.commerce.CommerceException;
import atg.service.pipeline.*;
import atg.beans.*;
import atg.commerce.order.processor.*;

import java.util.*;

/*
This class extends a class called SavedProperties. SavedProperties provides a set
of properties and a way to retrieve a mapped property. This functionality
corresponds to the savedProperties property and the
propertyDescriptorToBeanPropertyMap property in the properties file which
corresponds the this object.

This class also implements the PipelineProcessor interface. This interface
includes the runProcess() and getRetCodes() methods. All pipeline processors must
implement this interface.
*/
public class ProcSaveOrderDataObject extends SavedProperties
                    implements PipelineProcessor
{
// These are the two valid return codes for this pipeline processor
  private final int SUCCESS = 1;
  private final int FAILURE = 2;

// The constructor
  public ProcSaveOrderDataObject() {
  }

// Returns the set of all valid return codes for this processor.
  public int[] getRetCodes()
  {
    int[] ret = {SUCCESS, FAILURE};
    return ret;
  }

// property: orderDataProperty
// This is the order data property.
  private String mOrderDataProperty = "orderData";

  public String getOrderDataProperty() {
    return mOrderDataProperty;
  }

  public void setOrderDataProperty(String pOrderDataProperty) {
    mOrderDataProperty = pOrderDataProperty;
  }

  public int runProcess(Object pParam, PipelineResult pResult)
                      throws Exception
  {
/*
The pParam parameter contains the data   required for executing this pipeline
processor. This code extracts the required parameters for this processor.
*/
    HashMap map = (HashMap) pParam;
    Order order = (Order) map.get(PipelineConstants.ORDER);
    OrderManager orderManager = (OrderManager)
                        map.get(PipelineConstants.ORDERMANAGER);
    Repository repository = (Repository)
                        map.get(PipelineConstants.ORDERREPOSITORY);
    OrderTools orderTools = (OrderTools) orderManager.getOrderTools();

    MutableRepository mutRep = null;
    MutableRepositoryItem mutItem = null;
    Object value = null;
    Object[] savedProperties = null;
    String mappedPropName = null;

// Check for null parameters
    if (order == null)
      throw new InvalidParameterException();
    if (repository == null)
      throw new InvalidParameterException();
    if (orderManager == null)
      throw new InvalidParameterException();

/*
Try to cast the repository and make sure that it is a MutableRepository.
In most cases it will be a GSA Repository which is mutable.
*/
    try {
      mutRep = (MutableRepository) repository;
    }
    catch (ClassCastException e) {
      throw e;
    }

/*
This code is taking advantage of the ChangedProperties interface methods. The
first check is checking whether this processor is configured to save all
properties always, or take advantage of ChangedProperties. The
saveChangedPropertiesOnly property is inherited from SavedProperties. If
getSaveChangedPropertiesOnly() returns false, then the local variable
savedProperties is set to the entire list of properties defined in the
SaveOrderDataObject.properties file. If it returns true, then a check is done to
determine whether the orderData object implements ChangedProperties. If so, then
it gets the list of properties whose value has changed, otherwise it sets the list
of properties to save to the savedProperties property.
*/
    CommerceIdentifier orderData = (CommerceIdentifier)
      DynamicBeans.getPropertyValue(order, getOrderDataProperty());
    if (getSaveChangedPropertiesOnly()) {
      if (orderData instanceof ChangedProperties
         && (! ((ChangedProperties) orderData).getSaveAllProperties()))
       savedProperties =
        ((ChangedProperties) orderData).getChangedProperties().toArray();
      else
        savedProperties = getSavedProperties();
    }
    else {
      savedProperties = getSavedProperties();
    }

/*
Next a check is done to get the repositoryItem property from the object if it has
a repositoryItem property defined. If it does not have the repositoryItem property
or its value is null, then a lookup is done in the repository for the item and set
if it has the property.
*/
    boolean hasProperty = false;
    if (DynamicBeans.getBeanInfo(orderData).hasProperty("repositoryItem"))
    {
      hasProperty = true;
      mutItem = (MutableRepositoryItem)
              DynamicBeans.getPropertyValue(orderData, "repositoryItem");
    }

    if (mutItem == null) {
      mutItem = mutRep.getItemForUpdate(orderData.getId(),
           orderTools.getMappedItemDescriptorName(
           orderData.getClass().getName()));
      if (hasProperty)
        DynamicBeans.setPropertyValue(orderData, "repositoryItem",
                                      mutItem);
    }

/*
This section loops through all the properties in the savedProperties array and if
they exist in the object being saved, then the property will be saved to the
repository. The OrderRepositoryUtils class provides functionality which parses
mapped property values and either gets the property values, determines if the
property exists, etc. This code is preserved so it will allow classes created for
DCS 5.0 to still work. If your classes do not use the addChangedProperties()
method in the set() methods of your beans, then this for loop can be eliminated.
*/
    for (int i = 0; i < savedProperties.length; i++) {
      mappedPropName = getMappedPropertyName((String) savedProperties[i]);

      if (! OrderRepositoryUtils.hasProperty(order, orderData,
                                             mappedPropName))
       continue;

      try {
           value = OrderRepositoryUtils.getPropertyValue(order, orderData,
                                                         mappedPropName);
      }
      catch (PropertyNotFoundException e) {
        continue; // should not happen
      }

      if (orderManager.isLoggingDebug())
          orderManager.logDebug("save property[" + (String)
                  savedProperties[i] + ":" + value + ":" +
                  orderData.getClass().getName() + ":" +
                  orderData.getId() + "]");
      OrderRepositoryUtils.saveRepositoryItem(mutRep, mutItem,
                  (String) savedProperties[i], value, orderTools);
    } // for

/*
Here the repository item is updated to the repository.
This is done here to catch any Concurrency exceptions.
*/
    try {
      mutRep.updateItem(mutItem);
    }
    catch (ConcurrentUpdateException e) {
      throw new CommerceException("Concurrent Update Attempt", e);
    }

/*
Finally, the ChangedProperties Set is cleared and the saveAllProperties property
is set to false. This resets the object for more edits. Then the SUCCESS value is
returned.
*/
    if (orderData instanceof ChangedProperties) {
      ChangedProperties cp = (ChangedProperties) orderData;
      cp.clearChangedProperties();
      cp.setSaveAllProperties(false);
    }

    return SUCCESS;
  }
}

Next, configure an instance of ProcSaveOrderDataObject by adding a SaveOrderDataObject.properties file to your localconfig directory at /atg/commerce/order/processor/. The configuration file might look as follows:

$class=my_package.ProcSaveOrderDataObject

# This property tells the processor to only save the properties which have
# changed. This requires that when a property is changed that it be marked
# for saving.
saveChangedPropertiesOnly=true

# These are the properties of the OrderData object which will be saved to
# the repository. All properties which should be saved should be listed
# here. By default a property name listed here will be saved to the
# corresponding repository item property with the same name. If the name
# in the bean and repository are not the same then it can be mapped in the
# property propertyDescriptorToBeanPropertyMap below. Nothing needs to be
# defined for this property
savedProperties=

# This property maps a OrderRepository property descriptor to an OrderData
# bean property. By default the processor will look for an OrderRepository
# property descriptor which is the same as the bean property name. If
# there are any properties whose names differ, they can be mapped here.
# The format is repository_property_descriptor=bean_property_name
propertyDescriptorToBeanPropertyMap=

orderDataProperty=orderData

Finally, insert the SaveOrderDataObject processor into the updateOrder pipeline. The updateOrder pipeline is defined in the commerce pipeline definition file, commercepipeline.xml. In ATG Consumer Commerce, this file is located at /<ATG9dir>/B2CCommerce/config/atg/commerce/. In ATG Business Commerce, it is located at /<ATG9dir>/B2BCommerce/config/atg/commerce/.

To insert the SaveOrderDataObject processor into the updateOrder pipeline, extend the commercepipeline.xml file by creating a new commercepipeline.xml file that defines the new processor and placing it in your localconfig directory at /atg/commerce/. During deployment, the ATG platform uses XML file combination to combine the commercepipeline.xml files in the CONFIGPATH into a single composite XML file. Insert the new processor into the pipeline somewhere after the first processor, updateOrderObject, and before the last processor, setLastModifiedTime. By convention, you should insert the processor into the pipeline immediately after the processor that references the new object. In this example, the most appropriate place would be immediately after the updateOrderObject processor.

For more information on how to add a processor to an existing pipeline, refer to Processor Chains and the Pipeline Manager chapter. For more information on XML file combination, see the Nucleus: Organizing JavaBean Components chapter in the ATG Programming Guide.

Step 6 of 7 – Add a Processor to Load the New Object

You do not need to write new code to load the orderData reference in the Order object. The loadOrderObject processor in the loadOrder pipeline knows how to load all of the properties of the Order object, regardless of whether the commerce object hierarchy has been extended. However, to load the data into the OrderData object itself, you must create a new processor that loads the data from the repository into the OrderData object and insert that new processor into the refreshOrder pipeline, which performs the actual loading of most of the contained objects in the Order. (For more information on loading and refreshing orders, see Loading Orders in the Configuring Purchase Process Services chapter.)

First, write the Java source code for the new processor. The following Java source file, ProcLoadOrderDataObject.java serves as an example.

package my_package;

import atg.repository.*;
import atg.commerce.order.*;
import atg.service.pipeline.*;
import atg.beans.*;
import atg.commerce.order.processor.*;

import java.util.*;

/*
This class extends a class called LoadProperties. LoadProperties provides a set of
properties and a way to retrieve a mapped property. This functionality corresponds
to the loadProperties property and the propertyDescriptorToBeanPropertyMap
property in the properties file which corresponds the this object.

This class also implements the PipelineProcessor interface. This interface
includes the runProcess() and getRetCodes() methods. All pipeline processors must
implement this interface.
*/
public class ProcLoadOrderDataObject extends LoadProperties
            implements PipelineProcessor
{
// These are the two valid return codes for this pipeline processor
  private final int SUCCESS = 1;
  private final int FAILURE = 2;

// The constructor
  public ProcLoadOrderDataObject() {
  }

// Returns the set of all valid return codes for this processor.
  public int[] getRetCodes()
  {
    int[] ret = {SUCCESS, FAILURE};
    return ret;
  }

  // property: orderDataProperty
  private String mOrderDataProperty = null;

  public String getOrderDataProperty() {
    return mOrderDataProperty;
  }

  public void setOrderDataProperty(String pOrderDataProperty) {
    mOrderDataProperty = pOrderDataProperty;
  }

  public int runProcess(Object pParam, PipelineResult pResult)
                        throws Exception
  {
/*
The pParam parameter contains the data required for executing this pipeline
processor. This code extracts the required parameters for this processor.
*/
    HashMap map = (HashMap) pParam;
    Order order = (Order) map.get(PipelineConstants.ORDER);
    MutableRepositoryItem orderItem = (MutableRepositoryItem)
               map.get(PipelineConstants.ORDERREPOSITORYITEM);
    OrderManager orderManager = (OrderManager)
               map.get(PipelineConstants.ORDERMANAGER);
    OrderTools orderTools = orderManager.getOrderTools();

// Check for null parameters
    if (order == null)
      throw new InvalidParameterException();
    if (orderItem == null)
      throw new InvalidParameterException();
    if (orderManager == null)
      throw new InvalidParameterException();

/*
Local variables. loadProperties lists all the properties which must be loaded for
this object.
*/
    String mappedPropName;
    Object value;
    String[] loadProperties = getLoadProperties();

/*
This section of code first gets the orderData item descriptor from the order
repository item. Next it gets the orderData item descriptor. The third line of
code does a lookup in the OrderTools object and returns the class mapped to the
orderData item descriptor.
*/
    MutableRepositoryItem mutItem = (MutableRepositoryItem)
          orderItem.getPropertyValue(getOrderDataProperty());
    RepositoryItemDescriptor desc = mutItem.getItemDescriptor();
    String className =
          orderTools.getMappedBeanName(desc.getItemDescriptorName());

/*
Next, an instance of OrderData is constructed, its id property set to the
repository item's id, and if the object has a repositoryItem property, the item
descriptor is set to it.
*/
    CommerceIdentifier ci = (CommerceIdentifier)
           Class.forName(className).newInstance();
    DynamicBeans.setPropertyValue(ci, "id", mutItem.getRepositoryId());
    if (DynamicBeans.getBeanInfo(ci).hasProperty("repositoryItem"))
        DynamicBeans.setPropertyValue(ci, "repositoryItem", mutItem);

/*
This section loops through all the properties in the loadProperties array and
loads the property values from the repository into the object. First we look for a
mapping to the property value and then look to see if the property exists in the
item descriptor. If so, then we load it. The OrderRepositoryUtils class provides
functionality which parses mapped property values and either gets the property
values, determines if the property exists, etc. This code is preserved so it will
allow classes created for DCS 5.0 to still work. If your classes use the
getPropertyValue() method in the get() methods of your beans, then this for loop
can be eliminated.
*/
    for (int i = 0; i < loadProperties.length; i++) {
      mappedPropName = getMappedPropertyName(loadProperties[i]);
      if (desc.hasProperty(loadProperties[i]))
      {
       value = mutItem.getPropertyValue(loadProperties[i]);
       if (orderManager.isLoggingDebug())
           orderManager.logDebug("load property[" + loadProperties[i]
                   + ":" + value + ":" + ci.getClass().getName()
                   + ":" + ci.getId() + "]");
        OrderRepositoryUtils.setPropertyValue(order, ci, mappedPropName, value);
      }
    }

/*
If the loaded object implements ChangedProperties, then clear the
changedProperties Set. Then set the orderData property in the Order object to ci.
Finally, return SUCCESS.
*/
    if (ci instanceof ChangedProperties)
      ((ChangedProperties) ci).clearChangedProperties();
    DynamicBeans.setPropertyValue(order, getOrderDataProperty(), ci);

    return SUCCESS;
  }
}

Next, configure an instance of ProcLoadOrderDataObject by adding a LoadOrderDataObject.properties file to your localconfig directory at /atg/commerce/order/processor/. The configuration file might look as follows:

$class=my_package.ProcLoadOrderDataObject

# These are the properties of the Order which will be loaded from the
# repository. By default a property name listed here will be loaded from
# the corresponding repository item property with the same name. If the
# name in the bean and repository are not the same then it can be mapped
# in the property propertyDescriptorToBeanPropertyMap below. Nothing needs
# to be defined for this property.
loadProperties=

# This property maps a OrderRepository property descriptor to an Order
# bean property. By default the processor will look for an OrderRepository
# property descriptor which is the same as the bean property name. If
# there are any properties whose names differ, they can be mapped here.
# The format is repository_property_descriptor=bean_property_name
# The repository_property_descriptor name must be listed above in
# loadProperties.
propertyDescriptorToBeanPropertyMap=
orderDataProperty=orderData

Finally, insert the LoadOrderDataObject processor into the refreshOrder pipeline. The refreshOrder pipeline is defined in the commerce pipeline definition file, commercepipeline.xml. In ATG Consumer Commerce, this file is located at /<ATG9dir>/B2CCommerce/config/atg/commerce/. In ATG Business Commerce, it is located at /<ATG9dir>/B2BCommerce/config/atg/commerce/.

To insert the LoadOrderDataObject processor into the refreshOrder pipeline, extend the commercepipeline.xml file by creating a new commercepipeline.xml file that defines the new processor and placing it in your localconfig directory at /atg/commerce/. During deployment, the ATG platform uses XML file combination to combine the commercepipeline.xml files in the CONFIGPATH into a single composite XML file. Insert the new processor into the pipeline somewhere after the first processor, loadOrderObjectForRefresh. By convention, you should insert the processor into the pipeline immediately after the processor that references the new object. In this example, the most appropriate place would be immediately after the loadOrderObjectForRefresh processor.

As previously mentioned, for more information on how to add a processor to an existing pipeline, refer to the Processor Chains and the Pipeline Manager chapter. For more information on XML file combination, see the Nucleus: Organizing JavaBean Components chapter in the ATG Programming Guide.

Step 7 of 7 – Extend the ID Spaces Definition File

Note: This step is only necessary when the new item descriptor is a root item descriptor. It does not need to be performed when the new item descriptor is a subclass of an item descriptor.

When an ID is requested for a new repository item, it is requested from the appropriate IdSpace for that repository item. The item descriptor’s id-space-name attribute specifies which IdSpace supplies repository IDs for items of that item type. By default, all items use the item descriptor’s name as the ID space unless their item type inherits from another item type. In the latter case, the items use the ID space name of the root item descriptor in the super-type hierarchy.

If a new item descriptor that you’ve defined is a root item descriptor, you need to modify the ID spaces definition file, idspaces.xml, in order to define an ID space for that item descriptor. ATG Commerce IdGenerator guarantees that IDs within a named ID space are unique, and each root item descriptor defines the characteristics of its ID space in the idspaces.xml definition file.

In the example used throughout this section, you’ve defined a single root item descriptor, orderData. Consequently, you need to define an ID space for that descriptor. To do so, create a new idspaces.xml file at /atg/dynamo/service/ in your localconfig directory. During deployment, the ATG platform uses XML file combination to combine the idspaces.xml files in the CONFIGPATH into a single composite XML file. (For more information on XML file combination, see the Nucleus: Organizing JavaBean Components chapter in the ATG Programming Guide.) The idspaces.xml file might look as follows:

<id-spaces xml-combine="append">
    <id-space name="orderData" seed="1" batch-size="10000"
                                                prefix="od"/>
</id-spaces>

For more information on defining ID spaces and its impact on repository item IDs, see the ID Generators section of the Core Dynamo Services chapter in the ATG Programming Guide.

 
loading table of contents...