ATG
Persistent Cart for Anonymous Users
by Frank Kim on May.03, 2010, under Personalization
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:
- creating a profile in the repository (database) for all users that visit the website
- 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
- Turn on persisting anonymous profiles in the ProfileRequestServlet.
# /atg/dynamo/servlet/dafpipeline/ProfileRequestServlet persistAfterLogout=true persistentAnonymousProfiles=true
- Turn on auto-login by cookie and turn off auto-login by basic authentication.
# /atg/userprofiling/CookieManager sendProfileCookies=true # /atg/userprofiling/ProfileRequestServlet verifyBasicAuthentication=false
- 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.
Set ATG Repository Item Date or Timestamp Properties to the Current Time
by Frank Kim on Apr.19, 2010, under Repository
*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.
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.
Set ATG Property And Popup Window After Clicking on Link
by Frank Kim on Mar.26, 2010, under Page Development
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.
Enabling non-XA Resources in JBoss 4.2 with ATG
by Frank Kim on Jan.28, 2010, under Configuration
(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.
Configuring ATG to Send Email via Comcast SMTP
by Frank Kim on Jan.25, 2010, under Configuration
(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.
Specifying One-to-Many Relationship in ATG Repositories
by Frank Kim on Jan.21, 2010, under Repository
(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) );
Limiting the Quantity Added to a Cart
by Frank Kim on Jan.14, 2010, under Commerce
(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;
}
Combining XML in ATG
by Frank Kim on Jan.12, 2010, under Programming
(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.
NullPointerException in ATG OrderDiscountCalculator
by Frank Kim on Dec.15, 2009, under Commerce
(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.
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.




