betweenGo

ATG

Persistent Cart for Anonymous Users

by Frank Kim on May.03, 2010, under Personalization

129/365 Isn’t it iconic? on Flickr Isn’t it iconic? by Mykl Roventine

Convenience

eCommerce sites want to make their users’ experience as convenient and intuitive as possible.  One convenience found on most major eCommerce sites is remembering what the user put in his shopping cart, even if that person didn’t log in.  Therefore when the user returns to the site he will see what he left in his shopping cart.

Persistent Cart

ATG makes it relatively simple to do this by:

  1. creating a profile in the repository (database) for all users that visit the website
  2. automatically logging in users by cookie

Therefore if a user returns, she/he will be automatically logged in and if there were any items in his cart they will be added to the current cart.

Implementation

  1. Turn on persisting anonymous profiles in the ProfileRequestServlet.
    # /atg/dynamo/servlet/dafpipeline/ProfileRequestServlet
    persistAfterLogout=true
    persistentAnonymousProfiles=true
  2. Turn on auto-login by cookie and turn off auto-login by basic authentication.
    # /atg/userprofiling/CookieManager
    sendProfileCookies=true
    
    # /atg/userprofiling/ProfileRequestServlet
    verifyBasicAuthentication=false
  3. Make all profile properties not required except for login and password in userProfile.xml.  Also make autoLogin true.
    <table name="dps_user">
      <property name="login" required="true" />
      <property name="password" required="true" />
      <property name="firstName" required="false" />
      <property name="lastName" required="false" />
      <property name="email" required="false" />
      <property name="autoLogin" default="true" />
    </table>

Notes

When a profile is created for an anonymous user the login and password are set to the user’s ID (i.e. the profile’s repository ID).

If you are adding this functionality to an existing up and running site you may have to modify your user tables so that there no “not null” columns except for the id, login and password columns, you can leave those as how they were.  Also you will need to set auto_login to true for all your existing users.

update dps_user set auto_login = 1;

To determine when the anonymous user was created look at the registrationDate profile property.  To determine when was the last time the anonymous user logged in look at the lastActivity profile property.  Both of these are updated by ATG’s TrackActivity scenario which is in the DSS folder.

For further reading please see Tracking Guest Users and Tracking Registered Users in the ATG 9.1 Personalization Programming Guide.

  • Share/Bookmark
Leave a Comment :, , more...

Set ATG Repository Item Date or Timestamp Properties to the Current Time

by Frank Kim on Apr.19, 2010, under Repository

*Time* Ticking away... on Flickr *Time* Ticking away… by Michel Filion

This is a neat trick for automatically setting a date or timestamp property to the current time.  I learned it while perusing the ATG Repository Guide.

Date and Timestamp Properties

A repository item can use properties whose values are dates or timestamps, with the value set to the current date or time, using the java.util.Date, java.sql.Date, or java.sql.Timestamp classes. You can have a property whose value is set to the current time or date at the moment a repository item is created. You can do this by setting the feature descriptor attribute useNowForDefault. For example:

<property name="creationDate" data-type="timestamp">
  <attribute name="useNowForDefault" value="true"/>
</property>

For more information about this technique, see the Assigning FeatureDescriptorValues with the <attribute> Tag section in this chapter.

  • Share/Bookmark
Leave a Comment :, , more...

Set ATG Property And Popup Window After Clicking on Link

by Frank Kim on Mar.26, 2010, under Page Development

la cuarta ventana on Flickrla cuarta ventana by bachmont 

Sometimes when you click on a link in an ATG JSP/DSP page you want a property of an ATG component to be set as well.  For example:

<dsp:a href="foo.jsp">Foo
  <dsp:property bean="BarFormHandler.baz" paramvalue="index"/>
</dsp:a>

What gets tricky is if you also want a popup window to display the contents of this link.

The Wrong Way

I tried adapting the instructions from the tutorial Popup Windows | open new customised windows with JavaScript.

<dsp:a href="javascript:poptastic('foo.jsp');">Foo
  <dsp:property bean="BarFormHandler.baz" paramvalue="index"/>
</dsp:a>

This does not work, i.e. the setter for baz in BarFormHandler is never called.

The Brute Force Way

I then reverted to the original DSP and looked at the outputted HTML.  Based on that I updated the DSP like this.

<% atg.servlet.DynamoHttpServletRequest dreq = atg.servlet.ServletUtil.getCurrentRequest(); %>
<a href="javascript:poptastic('foo.jsp?_DARGS=/betweengo/test.jsp_AF&_dynSessConf=<%= dreq.getSessionConfirmationNumber() %>&_D%3A/betweengo/BarFormHandler.baz=+&/betweengo/BarFormHandler.baz=<dsp:valueof param="index" />');">Foo</a>

This works but is grotesque.

The Good Idea That Did Not Work

Then I realized I could just set a parameter in the URL and have the form handler use the value to set the property.

<a href="javascript:poptastic('foo.jsp?index=<dsp:valueof param="index" />');">Foo</a>

And in BarFormHandler

public boolean beforeSet(DynamoHttpServletRequest req,
            DynamoHttpServletResponse res) throws DropletFormException {

  String indexParam = request.getParameter("index");
  setIndex(Integer.parseInt(indexParam));

  return super.beforeSet(request, response);
}

This did not work plus I did not really like it because now I have a beforeSet method that is called for every single request.

The Winner

I then realized I did not read the tutorial Popup Windows | open new customised windows with JavaScript carefully.  There is a more elegant way to call the JavaScript which degrades gracefully for browsers that don’t support JavaScript.

<dsp:a href="foo.jsp" onclick="poptastic(this.href);return false;">Foo
  <dsp:property bean="BarFormHandler.baz" paramvalue="index"/>
</dsp:a>

This works, is elegant and requires just adding the onclick attribute to the original DSP.

  • Share/Bookmark
Leave a Comment :, more...

Enabling non-XA Resources in JBoss 4.2 with ATG

by Frank Kim on Jan.28, 2010, under Configuration

a dog and it's boss on Flickr
(Photo: a dog and it’s boss by Pixel Addict)

ATG documents how to enable non-XA resources in JBoss 4.2 for SOLID.  We ended up following the same instructions to work with Oracle.

JBoss Note: JBoss 4.2 by default assumes XA drivers, which some ATG applications use; however, there are no XA drivers for SOLID. To enable multiple non-XA resources in JBoss 4.2, add the property in bold text to the jbossjta-properties.xml file, under the <property depends="arjuna" name="jta"> tag:

<property depends="arjuna" name="jta">
  <property name="com.arjuna.ats.jta.allowMultipleLastResources" value="true"/>

You may still see warnings in your log file, but ATG applications will run correctly. To suppress these warnings, add the following to your jboss-log4j.xml file:

<category name="com.arjuna.atg.jta.logging">
  <priority value="ERROR"/>
</category>

For further reading please see Starting the SOLID SQL Database document in the Running Nucleus-Based Applications section of the ATG Installation and Configuration Guide.

  • Share/Bookmark
Leave a Comment :, , , more...

Configuring ATG to Send Email via Comcast SMTP

by Frank Kim on Jan.25, 2010, under Configuration

Comcast still sucks on Flickr
(Photo: Comcast still sucks by dmuth)

When you are developing at home you will probably need to configure your ATG application to send email via your ISP’s SMTP server.  Here is how I configured ATG to send email via Comcast’s SMTP server.

First you need to update ATG’s configuration to point to the Comcast SMTP server by modifying {ATG}/home/localconfig/atg/dynamo/Configuration.properties.

emailHandlerHost=smtp.comcast.net
emailHandlerPort=587

Typically you don’t need to set the emailHandlerPort, it is by default set to port 25.  But Comcast has recently been switching over to use port 587 because email viruses use port 25 on infected computers.

Next you need to update ATG’s SMTP Email service configuration by modifying {ATG}/home/localconfig/atg/dynamo/service/SMTPEmail.properties.

defaultFrom=betweengo@comcast.net
username=betweengo
password=betweengo

These values used to be optional but now are required because Comcast requires authentication as part of its increased security.

  • Share/Bookmark
Leave a Comment :, more...

Specifying One-to-Many Relationship in ATG Repositories

by Frank Kim on Jan.21, 2010, under Repository

Monta driving on Flickr
(Photo: Monta driving by Yogma)

Specifying one-to-many relationships is ridiculously easy in Ruby on Rails.  Unfortunately it’s not so straight-forward in ATG repositories.

First you specify the “belongs to” relationship.  In this example the player belongs to a team.

<item-descriptor name="player">
  <table name="team_players" type="auxiliary" id-column-names="team_id" shared-table-sequence="1">
    <property name="team" column-name="team_id" item-type="team" />
  </table>
</item-descriptor>

Then you specify the “has many” relationship.  In this example the team has many players.

<item-descriptor name="team">
  <table name="team_players" type="multi" id-column-names="player_id" shared-table-sequence="2">
    <property name="players" column-name="player_id" data-type="set" component-item-type="player" />
  </table>
</item-descriptor>

Note the trick is specifying the “shared-table-sequence.”

Here is the SQL for the table that specifies this relationship in our example.

CREATE TABLE team_players
(
  team_id     VARCHAR2(40)  NOT NULL,
  player_id   VARCHAR2(40)  NOT NULL,
  CONSTRAINT team_players_pk PRIMARY KEY (team_id, player_id),
  CONSTRAINT team_players_players_fk foreign key (player_id) references players (id),
  CONSTRAINT team_players_team_fk foreign key (team_id) references teams (id)
);
  • Share/Bookmark
2 Comments :, more...

Limiting the Quantity Added to a Cart

by Frank Kim on Jan.14, 2010, under Commerce

Speed Limit 14 MPH on Flickr

(Photo: Speed Limit 14 MPH by bredgur)

Sometimes the client will ask that the quantity of items you can add to the cart be limited to some number, say 14 like in the photo above. :-)

Often people will implement this by putting in checks throughout the JSP.  But this is not the best solution because it is more labor intensive and you may miss something.

Another solution is to deal with the issue in the CartModifierFormHandler by extending the doAddItemsToOrder method.  Simply check the quantity of each AddCommerceItemInfo item and make sure that its quantity plus the quantity of the same item already in the cart does not go over the limit.  If it does modify the quantity in the AddCommerceItemInfo item appropriately.

Here is how I implemented this.

    @Override
    protected void doAddItemsToOrder(DynamoHttpServletRequest pRequest,
            DynamoHttpServletResponse pResponse) throws ServletException,
            IOException {

        // fetch the order
        Order order = getOrder();
        if (order == null) {
            String msg = formatUserMessage(MSG_NO_ORDER_TO_MODIFY, pRequest,
                    pResponse);
            throw new ServletException(msg);
        }

        // iterate through the add commerce item infos, making sure that adding
        // any of them will not result in a quantity greater than LIMIT
        AddCommerceItemInfo[] addCommerceItemInfos = getItems();
        for (int ii = 0; ii < addCommerceItemInfos.length; ii++) {

            // see if there is a commerce item already in the order for the next
            // add commerce item info
            AddCommerceItemInfo addCommerceItemInfo = addCommerceItemInfos[ii];
            String catalogRefId = addCommerceItemInfo.getCatalogRefId();
            CommerceItem commerceItem = findCommerceItemByCatalogRefId(order,
                    catalogRefId);
            if (commerceItem == null) {
                continue;
            }

            // check that the quantity we add won't result in a total quantity
            // greater than LIMIT
            long addQty = addCommerceItemInfo.getQuantity();
            long qty = commerceItem.getQuantity();
            if (qty >= LIMIT) {
                addCommerceItemInfo.setQuantity(0);
            } else if (qty + addQty > LIMIT) {
                long newAddQty = LIMIT - qty;
                addCommerceItemInfo.setQuantity(newAddQty);
            }
        }

        super.doAddItemsToOrder(pRequest, pResponse);
    }

    protected CommerceItem findCommerceItemByCatalogRefId(Order pOrder,
            String pCatalogRefId) {
        for (int ii = 0; ii < numCommerceItems; ii++) {
            CommerceItem commerceItem = (CommerceItem) commerceItems.get(ii);
            String catalogRefId = commerceItem.getCatalogRefId();
            if (catalogRefId.equals(pCatalogRefId))
                return commerceItem;
        }
        return null;
    }
  • Share/Bookmark
Leave a Comment :, more...

Combining XML in ATG

by Frank Kim on Jan.12, 2010, under Programming

Legospective on Flickr

(Photo: Legospective by Guillermо)

ATG uses XML files sometimes instead of Java properties files for configuration.  Combining them is not as straight-forward as with Java properties files but more flexible.

The XML File Combination section in the Nucleus: Organizing JavaBean Components chapter of the ATG Programming guide gives a good explanation.  You can combine XML files using one of these methods.

  • replace
  • remove
  • append
  • append-without-matching
  • prepend
  • prepend-without-matching

In most cases you will append.  In one case I wanted to update the id-spaces for orders but I had to first remove before appending because replace did not work in this case.  This is how I did it.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<id-spaces>
  <id-space xml-combine="remove" name="order" seed="1" batch-size="10000" prefix="o"/>
  <id-space name="order" seed="100" batch-size="1" prefix="m"/>
</id-spaces>

To see the result of XML combining you can use the Dynamo Administration Server’s Component Browser to go to the component and then click on the property which specifies the XML file(s).

For example to find the result of combining idspaces.xml’s you would go to http://localhost:8080/dyn/admin/nucleus/atg/dynamo/service/IdGenerator/?propertyName=initialIdSpaces.

To find the result of combining productCatalogs.xml’s you would go to http://localhost:8080/dyn/admin/nucleus/atg/commerce/catalog/ProductCatalog/?propertyName=definitionFiles.

  • Share/Bookmark
Leave a Comment :, , more...

NullPointerException in ATG OrderDiscountCalculator

by Frank Kim on Dec.15, 2009, under Commerce

calculator on Flickr

(Photo: calculator by ansik)

There is a bug in the ATG OrderDiscountCalculator which causes a NullPointerException (NPE) under certain conditions.  Fortunately ATG provides the source for this class (I wish they did for all their classes or at least a larger subset of them) so it was pretty simple to figure out why this was happening.

The OrderDiscountCalculator assumes that the taxableShippingGroupSubtotalInfo local will not be null.  If it is an NPE will result when this local is referenced and the page that called this calculator will crash.

The simple fix is to check if it is null and if it is to continue to the next shipping group.

if (taxableShippingGroupSubtotalInfo == null) {
  continue;
}

At my request ATG Support has filed a problem report, NullPointerException in OrderDiscountCalculator.

Update 12-17-2009: ATG may have fixed this issue for ATG 9.1 p1, NPE in OrderDiscountCalculator w/empty shipping groups in Order.

  • Share/Bookmark
Leave a Comment :, more...

ATG Date Tag Converter is Buggy

by Frank Kim 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/Bookmark
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!