Archive for November, 2009
Twitter Weekly Updates for 2009-11-29
by Frank Kim on Nov.29, 2009, under Miscellaneous
- ATG Date Tag Converter is Buggy because Java’s DateFormat are inherently unsafe for multithreaded use. http://bit.ly/4VmN3l #
- Covariant Return Types in Java – betweenGo. It is nice to be able to override the return type. http://bit.ly/51JpAt #
- JavaServer Pages (JSP) and JSTL – attribute items does not accept any expressions. I was getting this because wrong URI http://bit.ly/5j5b3I #
ATG Date Tag Converter is Buggy
by Frank Kim on Nov.23, 2009, under Page Development
(Photo: Leaf on my car by exfordy)
The ATG Date Tag Converter is buggy because Java’s DateFormat is inherently unsafe for multithreaded use. This is documented in ATG PR #123210 DateTagConverter.convertObjectToString() method is not thread safe.
You run into this problem whenever you do something like this.
<dsp:valueof param=”creationDate” converter=”date” date=”M/dd/yyyy”/>
Unfortunately there is no work around. If using the Date Tag Converter is not causing problems for you, i.e you don’t see a stack trace like below, then you can ignore this bug.
2009-04-17 00:19:08,220 ERROR [nucleusNamespace.atg.dynamo.servlet.dafpipeline.DynamoServlet] java.lang.ArrayIndexOutOfBoundsException: 502 at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:436) at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2081) at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:1996) at java.util.Calendar.setTimeInMillis(Calendar.java:1071) at java.util.Calendar.setTime(Calendar.java:1037) at java.text.SimpleDateFormat.format(SimpleDateFormat.java:785) at java.text.SimpleDateFormat.format(SimpleDateFormat.java:778) at java.text.DateFormat.format(DateFormat.java:314) at atg.droplet.DateTagConverter.convertObjectToString(DateTagConverter.java:176)
But if you do see a bug like this then you might need to create a custom droplet to format the date. The droplet should only use DateFormat instances that are not static. I usually make my DateFormat instances local.
RQLQueryRange does not sort dates correctly
by Frank Kim on Nov.20, 2009, under Page Development
(Photo: Roppongi District Desktop by powerbooktrance)
This was bedeviling me for awhile. I could not figure out why my RQLQueryRange droplet was not sorting by dates correctly. It turns out this is a known ATG bug which only happens when you are using Oracle, which is 99% of the time.
Considering that this bug has been open since 2003 one can assume ATG is not interested in fixing it. This is probably because there is a simple work around.
Instead of using the sortProperties parameter, put the the sort directive directly in the RQL statement in the queryRQL parameter.
<dsp:param name="queryRQL" value="ALL ORDER BY creationDate DESC"/>
The Dangers of Custom ATG Repository ID’s.
by Frank Kim on Nov.12, 2009, under Repository
(Photo: El Alma del Ebro by Paulo Brandão)
Recently I was creating some SKU and product items using the ACC and I decided to create them with custom ID’s. The items were created fine but then I discovered I couldn’t add these items to the cart, I couldn’t use them in promotions, I couldn’t set up inventory for them, etc. I continually got exceptions like these.
/atg/commerce/order/OrderManager --- CONTAINER:atg.service.pipeline.RunProcessException: An exception was thrown from the context of the link named [setCatalogRefs].; SOURCE:atg.commerce.CommerceException: Unable to retrieve catalog reference for item with id ci90000002 and catalogRefId sku_free_01. /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineChain.runProcess(PipelineChain.java:371) /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineChainContext.runProcess(PipelineChainContext.java:185) /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineManager.runProcess(PipelineManager.java:453) /atg/commerce/order/OrderManager at atg.commerce.order.OrderImpl.ensureContainers(OrderImpl.java:1261) /atg/commerce/order/OrderManager at atg.commerce.order.OrderImpl.getCommerceItems(OrderImpl.java:691) /atg/commerce/order/OrderManager ....stack trace CROPPED /atg/commerce/order/OrderManager Caused by :atg.commerce.CommerceException: Unable to retrieve catalog reference for item with id ci90000002 and catalogRefId sku_free_01. /atg/commerce/order/OrderManager at atg.commerce.order.processor.ProcSetCatalogRefs.loadCatalogRef(ProcSetCatalogRefs.java:267) /atg/commerce/order/OrderManager at atg.commerce.order.processor.ProcSetCatalogRefs.runProcess(ProcSetCatalogRefs.java:121) /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineLink.runProcess(PipelineLink.java:233) /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineChain.runProcess(PipelineChain.java:343) /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineChainContext.runProcess(PipelineChainContext.java:185) /atg/commerce/order/OrderManager at atg.service.pipeline.PipelineManager.runProcess(PipelineManager.java:453) /atg/commerce/order/OrderManager at atg.commerce.order.OrderImpl.ensureContainers(OrderImpl.java:1261) /atg/commerce/order/OrderManager at atg.commerce.order.OrderImpl.getCommerceItems(OrderImpl.java:691) /atg/commerce/order/OrderManager ....stack trace CROPPED after 10 lines.
After pulling out my hair out for a few hours I realized the problem was because I had accidentally put a space at the end of the custom ID I had created. Too bad you can’t determine that from the stack trace.
Lesson Learned #1: Don’t put spaces in your custom ID’s, especially at the end.
Lesson Learned #2: Custom ID’s are a luxury you might want to avoid.
How to Add Multiple Items to the Shopping Cart in ATG
by Frank Kim on Nov.02, 2009, under Commerce
(Photo: Red Cart Conga, Baby by It’sGreg)
ATG’s CartModifierFormHandler has a handle method for adding multiple items to the shopping cart, handleAddMultipleItemsToOrder.
<dsp:input type="submit" bean="CartModifierFormHandler.addMultipleItemsToOrder" />
What is required is that in the request you set the product ID and SKU ID (catalogRefId) for each product you want to add.
<dsp:input bean="CartModifierFormHandler.productIds" paramvalue="product.id" type="hidden" /> <dsp:input bean="CartModifierFormHandler.catalogRefIds" paramvalue="sku.id" type="hidden" />
Seems pretty-straightforward, right? Well there are a couple of tricks.
Trick #1: Setting the quantity
Setting the quantity of the amount of each SKU you want added to the cart requiring naming the quantity input using the SKU iD.
<input type="text" name="<dsp:valueof param="sku.id"/>" />
Trick #2: Handling zero quantity inputs
When the handleAddMultipleItemsToOrder tries to add something that has a quantity of zero or less you it will output an error message that the quantity is zero or less. If you have a input form where the user does not have to add all the items on the page then this will be problematic.
To get around this restriction I overrode the preAddMultipleItemsToOrder method. My method sets the productIds and catalogRefIds properties to only have items that have a quantity greater than zero.
public void preAddMultipleItemsToOrder(DynamoHttpServletRequest pReq,
DynamoHttpServletResponse pRes) throws ServletException,
IOException {
// get the SKU ID's and product ID's set in the form
String[] oldCatalogRefIds = getCatalogRefIds();
String[] oldProductIds = getProductIds();
// make sure that the SKU ID's and product ID's are valid and of the
// same length
if (oldCatalogRefIds == null || oldCatalogRefIds.length == 0)
return;
if (oldProductIds == null || oldCatalogRefIds.length != oldProductIds.length) {
return;
}
// initialize list for the SKU ID's and product ID's that we will add to
// the shopping cart
List newCatalogRefIdsList = new ArrayList (
oldCatalogRefIds.length);
List newProductIdsList = new ArrayList (
oldCatalogRefIds.length);
// iterate through original SKU ID's
for (int ii = 0; ii < oldCatalogRefIds.length; ii++) {
// get next SKU ID
String catalogRefId = oldCatalogRefIds[ii];
// get quantity requested for that SKU ID
long qty;
try {
qty = getQuantity(catalogRefId, pRequest, pResponse);
} catch (NumberFormatException exc) {
if (isLoggingDebug())
logDebug("invalid quantity for catalogRefId=" + catalogRefId, exc);
qty = 0;
}
// if quantity > 0 then save this SKU ID and it's product ID
if (qty > 0) {
newCatalogRefIdsList.add(catalogRefId);
String productId = oldProductIds[ii];
newProductIdsList.add(productId);
}
}
// set the catalog ID's property to only have the SKU ID's of things
// that are being ordered
String[] newCatalogRefIds = new String[newCatalogRefIdsList.size()];
newCatalogRefIdsList.toArray(newCatalogRefIds);
if (isLoggingDebug()) {
logDebug("old catalogRefIds=" + Arrays.toString(oldCatalogRefIds)
+ ", new catalogRefIds="
+ Arrays.toString(newCatalogRefIds));
}
setCatalogRefIds(newCatalogRefIds);
// set the product ID's property to only have the product ID's of things
// that are being ordered
String[] newProductIds = new String[newProductIdsList.size()];
newProductIdsList.toArray(newProductIds);
if (isLoggingDebug()) {
logDebug("old productIds=" + Arrays.toString(oldProductIds)
+ ", new productIds="
+ Arrays.toString(newProductIds));
}
setProductIds(newProductIds);
}



