Fixing IllegalArgumentException in ACC

Recently I was unable to create an item using the ACC because of an IllegalArgumentException.

java.lang.IllegalArgumentException:  Attempt to set
property named view (ContentList:800007)  with value =
moduleTemplate:2200004 (class=class atg.adapter.gsa.GSAItem).
This property  failed due to a property type specific test.
Enable loggingDebug for  details.

It turned out to be a simple issue of the wrong case. The repository path for the ContentList view item was:

/Betweengo/repository/Portal

when it should have been

/betweengo/repository/Portal.

This is certainly not obvious from the exception.

Content Repository in ACC

To display a content repository in the ACC it must be listed in the initialRepositories property of the /atg/registry/ContentRepositories component.

For example:

initialRepositories+=\
       /betweengo/repository/Portal

From the ATG Repository Guide > 12. SQL Repository Reference > Configuring the SQL Repository Component > Registering a Content Repository:

Content repositories must be added to the list of repositories in the initialRepositories property of the /atg/registry/ContentRepositories component. This also causes the new repository to show up in the Content window of the ATG Control Center.

Invalid column type SQLException with ATG Repository

Recently I was seeing this exception.

CONTAINER:atg.repository.RepositoryException; SOURCE:java.sql.SQLException: Invalid column type
        at atg.adapter.gsa.GSAItemDescriptor.executeQuery(GSAItemDescriptor.java:6682)
        at atg.adapter.gsa.GSAView.executeUncachedQuery(GSAView.java:291)
        at atg.adapter.gsa.GSAView.executeQuery(GSAView.java:1027)
        at atg.repository.rql.RqlStatement.executeQuery(RqlStatement.java:208)

For the longest time I could not figure out what this meant and then it dawned on me as I laid in bed.

ATG expects certain Java class types for certain repository types which correspond to certain class types. I talk about this in my Date and Timestamp Repository Data Types post.

I was trying to do this RQL query:

      lastModified > ?0

with this parameter:

      java.util.Calendar param = java.util.Calendar.getInstance();

When I changed the parameter to type:

      java.sql.Timestamp

I no longer saw this exception which makes sense because the lastModified property is of data-type timestamp.

Accessing RepositoryItems with JSTL

Often we are accessing repository items in a JSP page like this.

  <dspel:droplet name="RQLQueryForEach" var="query">
    <dspel:param name="repository" bean="/betweengo/repository/Repository"/>
    <dspel:param name="itemDescriptor" value="Account"/>
    <dspel:param name="queryRQL" value="ALL"/>
    <dspel:setvalue param="account" paramvalue="element"/>
    <dspel:oparam name="output">
      <dspel:valueof param="account.name"/><br/>
    </dspel:oparam>
  </dspel:droplet>

With JSTL we could try to display the name like this.

      <c:out value="${query.account.name}"/>

If you are using JSP 2.0 you can display it even more simply.

      ${query.account.name}

However since account is a RepositoryItem object and there is no get method for the name property (i.e. it’s not a JavaBean object) the above will fail and produce an exception.

To get around this you can extend ATG’s RQLQueryForEach class by overriding the protected setElementParameter. In addition to setting the element parameter in the request you can set a new parameter which we will call “item”. This item is of class atg.beans.DynamicBeanMap and wraps a RepositoryItem.

public class RQLQueryForEachEL extends RQLQueryForEach {
  protected void setElementParameter(DynamoHttpServletRequest pRequest,
                                     String pElementName, Object pValue) {
    super.setElementParameter(pRequest, pElementName, pValue);
    DynamicBeanMap itemBean = new DynamicBeanMap(pValue, true);
    pRequest.setParameter("item", itemBean);
  }
}

Now we can access the repository item with JSTL like this.

      ${query.item.name}

ATG Consulting Interview

Today I had the most detailed but at same time most interesting ATG consulting interview yet. Here are the questions I was asked with answers when appropriate in italics.

  1. Which versions of ATG have you worked with?
  2. What parts of ATG’s stack have you worked with?
  3. How do you compare ATG with Ruby on Rails?
  4. What is Nucleus?
  5. What is the ATG Repository?
  6. When creating form handlers typically what ATG base class do you extend? GenericFormHandler.java
  7. When creating droplets what ATG base class do you extend? DynamoServlet.java
  8. What form handlers and methods do you use during checkout? ShoppingCartFormHandler, numerous handlers like handleMoveToConfirm, etc.
  9. What does a user typically see during checkout? Add to shopping cart, login, billing and shipping address, payment, confirm, confirmation, email confirmation, shipped email.
  10. How do you compare strings in Java? If String a = “hello” and String b= “hello” what is a == b? True, a and b both reference the same constant string.

In another interview I was asked these questions.

  1. What is HTTP? How does it work?
  2. If HTTP is stateless then how does a web application maintain state.

DistributorSender startup warning

When starting ATG instances with the DCS module I will often see this warning.

DistributorSender No remote servers configured

This is because in the product catalog two item descriptors, media-internal-binary and media-internal-text, are configured to use the Dynamo content distributor system if it is available. This is documented in the Using Content Distribution with a SQL Content Repository section of the ATG Dynamo Programming Guide.

Since out of the box the Dynamo content distributor system is not configured we see the above warning. To turn it off simply set loggingWarning=false for the /atg/commerce/catalog/ContentDistributorPool component.

ClientLockManager useLockServer property is false warning

In many ATG instances I will see this warning upon startup.

/atg/dynamo/service/ClientLockManager The useLockServer property of: /atg/dynamo/service/ClientLockManager is false and so global locking is disabled

As explained in the Enable the Repository Cache Lock Managers section of the Installation and Configuration Guide for DAS the ClientLockManager must be enabled if you are using locked mode caching anywhere in your SQL repository, which is typically the case in production when you are running more than one ATG server. The above warning is to inform the ATG administrator that currently the ClientLockManager is not enabled.

To enable it follow the instructions in the Configuring Lock Managers subsection of the Locked Caching section of the ATG Repository Guide.

On a single ATG instance environment you might still choose to setup the ClientLockManager and ServerLockManager just to get rid of this warning. To do this do the following.

  1. Enable ClientLockManager by creating localconfig/atg/dynamo/service/ClientLockManager.properties.lockServerAddress=localhost
    lockServerPort=9010
    useLockServer=true
  2. Start ServerLockManager by adding it to localconfig/atg/dynamo/service/Initial.properties.initialServices+=ServerLockManager

After doing this though you’ll see a warning like this.

/atg/dynamo/service/ServerLockManager No backup server is configured for Lock ServerCRAPBOOK-PRO/localhost:9010 It becomes Primary Server

So maybe in the end it’s better just to ignore the ClientLockManager warning, maybe even disable it. 🙂

Debugging a Category’s Bad Child Products

Today I was using the CategoryLookup droplet to find a category’s child products. However when I accessed the child products I would get this JDBC error.

java.lang.NullPointerException
at java.lang.String.(String.java:166)
at oracle.sql.CharacterSet.AL32UTF8ToString(CharacterSet.java:1517)

I realized my product data was corrupted. However this category had over 70 child products. The stack trace wasn’t telling me which one was corrupt and going through each child product to find out which one was corrupt was too painful.

I first queried the database to find all the child product ID’s.

SQL> select child_prd_id from dcs_cat_chldprd where category_id='cat101' order by sequence_num;

I then created a simple JHTML page which would query each child product and output which ones were corrupted.

<java>
final String [] product_ids = { "prod101", "prod102", "prod103" };

for (int ii = 0; ii < product_ids.length; ii++) {
out.print(ii + ". [" + product_ids[ii] + "] ");
try {
</java>
<droplet name="/atg/commerce/catalog/ProductLookup">
<param name="id" value="`product_ids[ii]`">
<oparam name="output">
<valueof param="element.displayName"/><br/>
</oparam>
</droplet>
<java>
} catch (RuntimeException exc) {
out.println(exc + "<br>");
}
}
</java>

Once I knew which child products were bad I removed their mappings to the category in dcs_cat_chldprd.

SQL> delete from dcs_cat_childprd where child_prd_id = 'prod102' and category_id='cat101';

Then I updated the sequence numbers so that they are all consecutive by moving the ones at the end to fill the holes created by the previous deletes.

SQL> update dcs_cat_childprd set sequence_num = 1 where child_prd_id = 'prod103' and category_id='cat101';

Viewing Expert-Level Information in the ACC

William T. Sherman | FlickrSome information, such as properties and item descriptors, are marked as expert (i.e. expert=true) so that they are not displayed in the ACC unless you are an expert user.

For example:

<property name="type" data-type="enumerated" expert="true"/>

To display expert-level information in the ACC go to Tools > Preferences and select the "Show expert-level information" box.

ATG ACC Preferences

Note that this is not a "sticky" preference, i.e. you have to set it every time you restart the ACC.

Date and Timestamp Repository Data Types

Today I noticed that my timestamp property was being saved with only the date information, not the time information. That is when I queried the corresponding column I saw something like this.

> select TO_CHAR(timestamp, 'yyyy-mm-dd hh24:mi:ss') from foo;

2007-11-17 00:00:00

The timestamp property was represented in Java using a Date class. I thought of switching it to a Timestamp class but was dubious because the only real difference is that the Timestamp class stores fractional seconds.

Then I looked at /atg/commerce/order/orderrepository.xml and the corresponding OrderImpl.java and realized that it used the timestamp repository data type while I was using the date repository date type. Since the Oracle column data type was also timestamp I found when I switched the repository data type the time information was also saved.

> select TO_CHAR(timestamp, 'yyyy-mm-dd hh24:mi:ss') from foo;

2007-11-17 20:03:00 

The moral of the story is to use the timestamp repository data type, not the date repository data type.

ATG has more documentation about this though it is not entirely true, i.e. you can use the Date Java class type with the timestamp SQL type.