betweenGo

Archive for November, 2009

Twitter Weekly Updates for 2009-11-29

by 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 #
Share
Leave a Comment more...

ATG Date Tag Converter is Buggy

by on Nov.23, 2009, under Page Development

Leaf on my car on Flickr

(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.

Share
Leave a Comment :, , more...

RQLQueryRange does not sort dates correctly

by on Nov.20, 2009, under Page Development

Roppongi District Desktop on Flickr

(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"/>
Share
Leave a Comment :, , more...

The Dangers of Custom ATG Repository ID’s.

by on Nov.12, 2009, under Repository

El Alma del Ebro on Flickr

(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. :-)

Share
Leave a Comment : more...

How to Add Multiple Items to the Shopping Cart in ATG

by on Nov.02, 2009, under Commerce

Red Cart Conga, Baby on Flickr

(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);
}
Share
Leave a Comment more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!