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 |
---|---|
| A |
| A |
| 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. |
| A |
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:
Subclass
atg.commerce.order.OrderTools
and add a newcreateOrderData()
method that instantiates anOrderData
object and creates anOrderData
repository item in the Order Repository.Subclass
atg.commerce.order.SimpleOrderManager
(which extendsatg.commerce.order.OrderManager
) and override itscreateOrder()
method. The newcreateOrder()
method should first call thecreateOrder()
method of the superclass to create theOrder
object, then call thecreateOrderData()
method that you created in step 1 to create theOrderData
object, and finally add theOrderData
object to theOrder
. (For more information on theSimpleOrderManager
, see the Using the SimpleOrderManager section of the Working With Purchase Process Objects chapter.)
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 /<ATG2007.3dir>/B2CCommerce/config/atg/commerce/
. In ATG Business Commerce, it is located at /<ATG2007.3dir>/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 /<ATG2007.3dir>/B2CCommerce/config/atg/commerce/
. In ATG Business Commerce, it is located at /<ATG2007.3dir>/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.