Repository
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.
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) );
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.
DynamicBeanMap
by Frank Kim on Feb.18, 2009, under Repository
Previously I posted about using the atg.beans.DynamicBeanMap class to wrap a RepositoryItem so that it is accessible via JSTL. However it turns out this is not a great solution.
If we want to access a simple property in a RepositoryItem wrapped in a DynamicBeanMap it is relatively straightforward. For example,
${user.name}
However if we want to access a RepositoryItem property like this,
${user.address}
then we end up getting a StackOverflowError as the DynamicBeanMap spins going back forth between the user RepositoryItem and the address RepositoryItem.
Interestingly enough accessing a simple property from the RepositoryItem property works fine.
${user.address.city}
One solution is to set the recursive argument to false when creating the DynamicBeanMap.
DynamicBeanMap itemBean = new DynamicBeanMap(pValue, false);
When you do this the following will work correctly.
${user.address}
However this no longer works.
${user.address.city}
We ended up abandoning the DynamicBeanMap and creating a library of strongly typed repository item wrapper proxy objects.
ATG Support helped me tremendously to figure out what was going on. They suggested an alternative which we never tried because of the large impact it would have on our JSP.
What I determined in looking at this further is that the DynamicBeanMap class is not really documented for customer use, but there is a DSP/DSPEL tag called “tomap” that uses this class that we do document. See the appendix in our 2006.3 Page Developer’s guide.
So, this tag would avoid this problem since it does have an undocumented “recursive” attribute that defaults to “false”, but I think it might be preferable to set it to “true” and use another undocumented option. After using the “tomap” tag with recursive=true, you can then use a _realObject property to unwrap your “final” object being accessed.
So if your tag is:
<dspel:getvalueof var="address" param="user.address" />You can change it to use:
<dspel:getvalueof var="address" param="user.address._realObject" />Or if you were doing:
<dspel:valueof param="user" />you could use:
<dspel:valueof param="user._realObject" />Basically you just unwrap whatever end/final object you’re trying to get to with _realObject. Since we can’t see exactly what code called hashCode that caused the StackOverflowError, I can’t be certain this will avoid the StackOverflowError, but I suspect it will.
This solution will have the benefit of having minimal impact on your ability to access properties with JSTL.
I’ve entered a PR #155848 about some of these properties not being documented.
Also PR #81771 was submitted requesting the ability to recursively access repository items in JSTL.
Repository creating tables automatically
by Frank Kim on Aug.01, 2008, under Hibernate, Repository
Recently we noticed while running some ATG unit tests that tables were being created by the ATG repository if they had not already been created by our SQL scripts. This was a functionality that I was unaware of but apparently it is not unique, Hibernate does this too. I could not find any documentation about this nor could I determine how to turn it off.
The ATG repository creates these tables using the repository definition and the defaults for column width and data type. It does not seem to warn that it is creating these tables.
Fixing IllegalArgumentException in ACC
by Frank Kim on Jul.22, 2008, under ACC, Repository
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
by Frank Kim on Jul.03, 2008, under ACC, Repository
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
initialRepositoriesproperty of the/atg/registry/ContentRepositoriescomponent. 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
by Frank Kim on May.28, 2008, under 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
by Frank Kim on May.09, 2008, under JSTL, Page Development, Repository
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
by Frank Kim on Mar.10, 2008, under Commerce, Form Handlers, Java SE, Nucleus, Repository
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.
- Which versions of ATG have you worked with?
- What parts of ATG’s stack have you worked with?
- How do you compare ATG with Ruby on Rails?
- What is Nucleus?
- What is the ATG Repository?
- When creating form handlers typically what ATG base class do you extend? GenericFormHandler.java
- When creating droplets what ATG base class do you extend? DynamoServlet.java
- What form handlers and methods do you use during checkout? ShoppingCartFormHandler, numerous handlers like handleMoveToConfirm, etc.
- What does a user typically see during checkout? Add to shopping cart, login, billing and shipping address, payment, confirm, confirmation, email confirmation, shipped email.
- 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.
- What is HTTP? How does it work?
- If HTTP is stateless then how does a web application maintain state.
