Thursday, December 29, 2011

Heap dump analysis

Below is one example of analyzing heap dump to know which objects are responsible for JVM out of memory.
Once you have java option option -XX:+HeapDumpOnOutOfMemoryError, heap dump will be generated once JVM is out of memory.
or else you can use JMX tools to generate heap dump.
for more information go through http://atgkid.blogspot.com/2011/12/how-to-take-thread-dumps-and-heap-dumps.html

Once heap dump is generated.

Download Eclipse Memory Analyzer (For 64-bit OS get 64 bit version) - http://eclipse.org/mat/
Depending on the heap size dump you need to increase the vm size of Eclipse MAT
To increase size in Mac OS X, Right click on MemoryAnalyzer (application) then "Show Package Contents".
Edit - MemoryAnalyzer.app/Contents/MacOS/MemoryAnalyzer.ini

In below example - size of heap dump is 1.8 GB and Eclipse MAT vm size is "3048m" and instance  vm size (from which the heap dump is generated) is 2560m.

Open heap dump -


Once the heap dump is loaded in Eclipse Memory Analyzer
Open dominator tree.

Select "Group by class"

By here you can know what type of objects occupied much of heap dump.
For example - In below scenario, GSAItem objects occupy 41% of heap dump (788 MB of 1.8 GB)

To further know which item's are resposible for 41%
Right click on atg.adapter.gsa.GSAItem -> List objects -> with outgoing references

Once the objects are listed. To know what type of items. 
In GSAItem object, mItemDescriptor.mItemDescriptorName will give the name of the GSAItem.

We need to group by value "mItemDescriptor.mItemDescriptorName" on Class atg.adapter.gsa.GSAItem (in dominator_tree).
So go back to dominator_tree tab, right click on atg.adapter.gsa.GSAItem -> Java Basics -> Group By Value.


In Group By Value window, fill field with "mItemDescriptor.mItemDescriptorName" and click finish.

This will take time depending on the number of objects it needs to parse. 
Once it is done, you can see which type of items (GSAItems) take more space in jvm heap space. (sort it by Retained Heap)
In below scenario, ss-sku items took 333 MB of jvm heap space and number of ss-sku items in memory are 35,465 (i.e 35,465 items are cached in memory)


Hope this helps!


How to take thread dumps and heap dumps using java tools

1) To take thread dumps -
Once you have below java options
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
(Note: all tools mentioned below are available in <JAVA_HOME>/bin)

Using jdb
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8000

> suspend
All threads suspended.
"where all" will give snapshot of all threads.
> where all
http-127.0.0.1-8443-6:
  [1] java.lang.Object.wait (native method)
  [2] java.lang.Object.wait (Object.java:485)
  [3] org.apache.tomcat.util.net.JIoEndpoint$Worker.await (JIoEndpoint.java:416)
  [4] org.apache.tomcat.util.net.JIoEndpoint$Worker.run (JIoEndpoint.java:442)
  [5] java.lang.Thread.run (Thread.java:662)
http-127.0.0.1-8443-5:
  [1] java.lang.Object.wait (native method)
  [2] java.lang.Object.wait (Object.java:485)
  [3] org.apache.tomcat.util.net.JIoEndpoint$Worker.await (JIoEndpoint.java:416)
  [4] org.apache.tomcat.util.net.JIoEndpoint$Worker.run (JIoEndpoint.java:442)
  [5] java.lang.Thread.run (Thread.java:662)
http-127.0.0.1-8443-4:
  [1] java.lang.Object.wait (native method)
  [2] java.lang.Object.wait (Object.java:485)
...
..
to resume all threads.
> resume

Using  jstack -
jstack <jvmid>  (you can get jvmid using jps)

For example -
C:\Documents and Settings\Administrator>jps
4416 Main
5800
3300 Jps
6676

C:\Documents and Settings\Administrator>jstack 4416
2011-12-27 16:32:19
Full thread dump Java HotSpot(TM) Server VM (19.0-b09 mixed mode):

"ajp-127.0.0.1-8009-Acceptor-0" daemon prio=6 tid=0x6fc39c00 nid=0x11fc runnable [0x6d27f000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:390)
        - locked <0x14495ae0> (a java.net.SocksSocketImpl)

To get thread dump using jboss jmx-console, please go through below link
http://atgkid.blogspot.com/2011/10/how-to-take-thread-dumps-in-jboss.html

2) To take heap dumps - 
Using jmap (available from jdk 1.6 but can also be used on jvm with 1.4+ )
jmap -dump:format=b,file=<filename>.hprof <jvmid>

C:\Documents and Settings\Administrator>jps
4416 Main
5800
3300 Jps
6676

C:\Documents and Settings\Administrator>jmap -dump:format=b,file=sample.hprof 4416
Dumping heap to C:\Documents and Settings\Administrator\sample.hprof ...
Heap dump file created

Using java option -XX:+HeapDumpOnOutOfMemoryError
Once you have above java option, JVM will create heap dump once it is out of memory.

Using  jconsole - Java Monitoring and Management Console - JMX-compliant graphical tool for monitoring a Java virtual machine. It can monitor both local and remote JVMs
To use jconsole you need to have java option "-Dcom.sun.management.jmxremote"


heap dump file will be created in folder from where the application is run. For example - <JBOSS-HOME>/bin or <BEA-DOMAIN-HOME>/bin

Using  jvisualvm - comes with java 1.6 but can be used to visualize/monitor applications running on jdk 1.4 and higher. 
Monitor showing CPU and Heap Usage and total threads and classes. 
To get thread dump you can use

Sample heap dump taken using jvisualvm


To take thread dumps in jvisualvm


Sample Thread dump taken using jvisualvm 


Saturday, December 10, 2011

hitCount for referred items will not be increased while accessing parent item

While accessing an item, hitCount (in Dynamo component browser repository cache usage statistics) for referred items will not be increased.

For example - In PioneerCycling
In test4.jsp
<%@ taglib uri="dsp" prefix="dsp"%>
<dsp:page>
<dsp:importbean bean="/atg/commerce/catalog/ProductLookup"/>
<dsp:droplet name="ProductLookup" >
<dsp:param name="id" value="prod10023"/>
<dsp:param name="elementName" value="product"/>
<dsp:oparam name="output">
<dsp:valueof param="product.smallImage.url"/> <br>
</dsp:oparam>
</dsp:droplet>
</dsp:page>
By default PioneerCycling ProductCatalog has simple cache and item-cache-size for all items is 1000.
Before accessing this jsp

Product - accesscount is 0. Hit count is 0.
Media - accesscount is 0, hit count is 0.
Once test4.jsp is accessed for the first time.

Product - accesscount is 2, hit count is 0.
Media - accesscount is 6, hit count is 0.

First time access - In logs. select queries are fired.
[++SQLQuery++]
 SELECT t1.product_id,t1.version,t1.description,t1.nonreturnable,t1.brand,t1.end_date,t1.display_name,t1.disallow_recommend,
        t1.long_description,t1.creation_date,t1.start_date,t1.parent_cat_id,t1.product_type
   FROM dcs_product t1
  WHERE t1.product_id = ?
-- Parameters --
p[1] = {pd} prod10023 (java.lang.String)
[--SQLQuery--]
[++SQLSelect++]
 SELECT template_id,thumbnail_image_id,small_image_id,large_image_id
   FROM dcs_prd_media
  WHERE product_id=?
-- Parameters --
p[1] = {pd} prod10023 (java.lang.String)
[--SQLSelect--]

Once again if the test4.jsp is refreshed

Product - accessCount is 3, hit count is 1.
Media - accessCount is 6, hit count is 0.

As you can observe - neither the access count or hit count is increased for media items. But for product it got increased.
Note: Only first time queries are fired. Once items are cached there are no queries fired for product and media.
As test4.jsp is refreshed, product stats are getting increased but media stats are not getting increased.

Why?
Reason - (Below explanation is given by Oracle ATG support Nick Glover)
First time when product is accessed all its properties are loaded from database including media items, so for the first time accessCount for product and media items is increased. Once media items are loaded they are stored as references in the product property values. So, the next time the product is accessed, they don't need to be loaded at all, the product has a reference to them. This is not considered a cache hit on the media items because the media item cache is not being used in this situation; instead the product item cache is being used and its references to the media items are what is saving the trip to the database. And obviously, the hit ratio for media items is going to be 0% because the initial loading of them is a miss, and you have not done any direct requests for the media items that successfully found them in cache to get a cache hit.

Regarding Weak entry cache -
Suppose that number of products in application is 10000 and the item-cache-size is 5000
and number of media-items is 30000 (for each product 3 media-items) and the item-cache-size is 1000.
So if 1 product is accessed, 3 media items are loaded.

In above scenario, if 1000 products are accessed
product entry count is 1000 (in main cache)
media entry count is 1000 (in main cache)
media weak entry count is 2000 (in weak cache)

weak entry items in weak cache are not garbage collected until the referenced item (parent item) is removed/pushed from main cache.


Friday, November 25, 2011

Regarding ConcurrentUpdateException and InvalidVersionException

There are 3 places where order version is stored
1) Database level - dcspp_order.version
2) Repository item cache level - orderItem.getPropertyValue("version");
3) Session level - order.getVersion();

// This check is done in classes where order is modified
if (order.getVersion() != orderItem.getPropertyValue("version"))
     throw atg.commerce.order.InvalidVersionException

// This check is done while updating item
if (dcspp_order.version != orderItem.getPropertyValue("version"))
      throw atg.repository.ConcurrentUpdateException

Note: concurrency can be maintained on any repository item, please go through below documentation
http://docs.oracle.com/cd/E26180_01/Platform.94/RepositoryGuide/html/s0709maintainingitemconcurrencywithth01.html

atg.repository.ConcurrentUpdateException - Is thrown if 2 separate sessions (belong to 2 different instances) of same user edit same order simultaneously or if 2 applications are editing same order. One way to reproduce in cluster environment (in production)
Login in one browser 1  with one user suppose xyz and logout
Login in another browser 2  with same user xyz and logout
Login back in browser 1 with same user xyz

This exception is thrown in logs of instance (browser 1 session instance)
CONTAINER:atg.commerce.CommerceException; SOURCE:CONTAINER:atg.service.pipeline.RunProcessException: An exception was thrown from the context of the link named [updateCommerceItemObjects].; 
SOURCE:CONTAINER:atg.commerce.CommerceException: Saving order 2288449002 failed because doing so would result in data being overwritten. This save attempt had an out of date repository item [commerceItem].; 
SOURCE:atg.repository.ConcurrentUpdateException: no rows updated oldVersion=20 for item=commerceItem:ci55251446 in GSATransaction=atg.adapter.gsa.GSATransaction@6a77cca2    
thread=ajp-10.238.160.131-30100-37 transaction=TransactionImple < ac, BasicAction: aeea083:6d44:4ecb6eb0:da1fd5 status: ActionStatus.RUNNING >
Note: Typically item-cache-timeout by default is -1 (when not set) which means item will be in cache until invalidated. Suppose the item-cache-timeout for order is set to 30 mins.

Explanation -
When the user xyz login in one browser 1
  suppose his/her session is tied to one instance 1 and suppose his order version is incremented to 2.
logout
Note that user xyz order is cached in this instance 1 and its version is 2 and it will be there in the cache until item-cache-timeout which is 30 mins.

If the same user xyz tries to login using another browser 2
  suppose his/her session now got tied to another instance 2 and his order version will be incremented to 3.
logout
Note that user xyz order is cached in this instance 2 and its version is 3 and it will be there in the cache until 30 mins

since orderRepository has simple cache, instance 1 has no idea of updated version.
Note: If the browser 1 cookies are not removed, request will always go to instance 1.
In this 30 mins time, when user xyz login using browser 1, at the time of loadorder since the version in cache (which is 2) doesnt match with version in database
(which is 3) ConcurrentUpdateException is thrown.

This can also happen if the user in application like CSC and dotcom user both working on same order, then above exception is thrown

atg.commerce.order.InvalidVersionException -  is caused if 2 sessions (of same instance) of same user modifying same order simultaneously. 
One way to reproduce in dev environment (only 1 instance) or you need to make sure that 2 sessions belongs to same instance in cluster environment.

If the user has 2 seperate session in 2 seperate browsers, where both sessions are tied to one instance and since both session are updating same order, InvalidVersionException exceptions is thrown.

Explanation -
Whenever the order is updated, both order (session) version and order repository version are updated.
When updating if both order version and order repository version are not equal, then invalid version exception is thrown.

**** Error      Fri Nov 25 15:08:57 CST 2011    1322255337883   /atg/commerce/order/purchase/RepriceOrderDroplet        ---
CONTAINER:atg.service.pipeline.RunProcessException:
An exception was thrown from the context of the link named [updateOrderAfterReprice].; SOURCE:atg.commerce.order.InvalidVersionException: This order (2293186179) is out of date.
Changes have been made to the order and the operation should be resubmitted. Order version 4, Repository item version 9.
        at atg.service.pipeline.PipelineChain.runProcess(PipelineChain.java:371)
        at atg.service.pipeline.PipelineChainContext.runProcess(PipelineChainContext.java:185)
        at atg.service.pipeline.PipelineManager.runProcess(PipelineManager.java:453)
        at atg.service.pipeline.servlet.PipelineChainInvocation.service(PipelineChainInvocation.java:267)
        at atg.commerce.order.purchase.RepriceOrder.service(RepriceOrder.java:438)
        at atg.servlet.DynamoServlet.service(DynamoServlet.java:123)
        at atg.taglib.dspjsp.DropletTag.invokeServlet(Unknown Source)
        at atg.taglib.dspjsp.DropletTag.doAfterBody(Unknown Source)
        at org.apache.jsp.checkout.shippinginfo_jsp._jspx_meth_dsp_005fdroplet_005f8(shippinginfo_jsp.java:2128)
....stack trace CROPPED after 10 lines.
Caused by :atg.commerce.order.InvalidVersionException: This order (2293186179) is out of date. Changes have been made to the order and the operation should be resubmitted. Order version 4, Repository item version 9.
        at atg.commerce.order.OrderManager.updateOrder(OrderManager.java:2557)
        at atg.commerce.order.processor.ProcUpdateOrder.runProcess(ProcUpdateOrder.java:111)
        at atg.service.pipeline.PipelineLink.runProcess(PipelineLink.java:233)
        at atg.service.pipeline.PipelineChain.runProcess(PipelineChain.java:343)
        at atg.service.pipeline.PipelineChainContext.runProcess(PipelineChainContext.java:185)
        at atg.service.pipeline.PipelineManager.runProcess(PipelineManager.java:453)
        at atg.service.pipeline.servlet.PipelineChainInvocation.service(PipelineChainInvocation.java:267)
        at atg.commerce.order.purchase.RepriceOrder.service(RepriceOrder.java:438)
        at atg.servlet.DynamoServlet.service(DynamoServlet.java:123)
....stack trace CROPPED after 10 lines.

Tuesday, November 15, 2011

To start Motorprise application with oracle database

Below are the steps to start Motorprise application with oracle database.
To create Motorprise schema
Install oracle XE or oracle SE and connect using system/sys and create motorprise user.
create user motorprise identified by password1 default tablespace users temporary tablespace temp;
grant connect, resource to motorprise;
grant select any table to motorprise;
grant create any table to motorprise;
grant update any table to motorprise;
grant delete any table to motorprise;
grant alter any table to motorprise;
grant create any view to motorprise;
Install Motorprise scripts
Connect using motorprise user
Connect motorprise/password1
Run following script
@<ATGDir>\MotopriseJSP\sql\install\oracle\motorpriseall_ddl.sql
For example:
@C:\ATG\ATG9.1\MotorpriseJSP\sql\install\oracle\motorpriseall_ddl.sql
Commit.;
Exporting test data from Solid to Oracle
1) Check license files. If not available copy license files to your <ATGDir>\home\localconfig
2) Copy solid.db file from
Copy <ATGDir>\DAS\solid\atgdb\solid.db to <ATGDir>\DAS\solid\i486-unknown-win32\
For example
C:\ATG\ATG9.1\DAS\solid\atgdb\solid.db to C:\ATG\ATG9.1\DAS\solid\i486-unknown-win32\
3) Connect to solid database
Add below lines in <ATGDir>\home\localconfig\atg\dynamo\service\jdbc\FakeXADataSource.properties
For example:
C:\ATG\ATG9.1\home\localconfig\atg\dynamo\service\jdbc\FakeXADataSource.properties
$class=atg.service.jdbc.FakeXADataSource
driver=solid.jdbc.SolidDriver
URL=jdbc:solid://localhost:1313
user=motorprise
password=motorprise
4) Start solid database –
Execute <ATGDir>\DAS\solid\ i486-unknown-win32\solfe.exe
C:\ATG\ATG9.1\DAS\solid\i486-unknown-win32\solfe.exe
5) Exporting Motorprise data from solid to xml file
cd C:\ATG\ATG9.1\home
bin\startSQLRepository -m MotorpriseJSP -export all users.xml -repository /atg/userprofiling/ProfileAdapterRepository
bin\startSQLRepository -m MotorpriseJSP -export all products.xml -repository /atg/commerce/catalog/ProductCatalog
bin\startSQLRepository -m MotorpriseJSP -export all priceLists.xml -repository /atg/commerce/pricing/priceLists/PriceLists
bin\startSQLRepository -m MotorpriseJSP -export all inventory.xml -repository /atg/commerce/inventory/InventoryRepository
6) Connect to Oracle database
Now point FakeXADataSource to Oracle.
Add below lines in <ATGDir>\home\localconfig\atg\dynamo\service\jdbc\FakeXADataSource.properties
In C:\ATG\ATG9.1\home\localconfig\atg\dynamo\service\jdbc\FakeXADataSource.properties
$class=atg.service.jdbc.FakeXADataSource
URL=jdbc:oracle:thin:@localhost:1521:xe
user=motorprise
password=password1
needsSeparateUserInfo=true
readOnly=false
driver=oracle.jdbc.xa.client.OracleXADataSource
Note: Make sure you have classes12.jar or ojdbc.jar in classpath
CLASSPATH=C:\oraclexe\app\oracle\product\10.2.0\server\jdbc\lib\ojdbc14.jar;
If you are using oracle XE, run below steps to increase processes, sessions and transactions.
sqlplus sys/oracle as sysdba
alter system set processes = 150 scope = spfile;
alter system set sessions = 300 scope = spfile;
alter system set transactions = 330 scope = spfile;
shutdown immediate;
startup;
7) Import Motorprise data from xml to oracle
cd C:\ATG\ATG9.1\home
bin\startSQLRepository -m MotorpriseJSP -import users.xml -repository /atg/userprofiling/ProfileAdapterRepository
bin\startSQLRepository -m MotorpriseJSP -import products.xml -repository /atg/commerce/catalog/ProductCatalog
bin\startSQLRepository -m MotorpriseJSP -import priceLists.xml -repository /atg/commerce/pricing/priceLists/PriceLists
bin\startSQLRepository -m MotorpriseJSP -import inventory.xml -repository /atg/commerce/inventory/InventoryRepository
Build Motorprise.ear
Create Motorprise.ear – go to <ATGDir>\home\bin
cd C:\ATG\ATG9.1\home\bin
runAssembler C:\ATG\ATG9.1\Motorprise.ear -m DCS.PublishingAgent DafEar.admin MotorPriseJSP
Note: DCS.PublishingAgent is optional. Keep it if you have CA/BCC installed.
Please make sure that your FakeXADataSource is pointing to your local oracle.
Create FakeXADataSource.properties and JTDataSource.properties in
<ATGDir>\ \MotorpriseJSP\config\atg\dynamo\service\jdbc
In FakeXADataSource.properties
$class=atg.service.jdbc.FakeXADataSource
URL=jdbc:oracle:thin:@localhost:1521:xe
user=motorprise
password=password1
needsSeparateUserInfo=true
readOnly=false
driver=oracle.jdbc.xa.client.OracleXADataSource
In JTDataSource.properties
$class=atg.service.jdbc.MonitoredDataSource
dataSource=/atg/dynamo/service/jdbc/FakeXADataSource
Finally Deploy Motorprise.ear
Use http://localhost:<port>/Motorprise/ to bring up the login page of Motorprise application.

Friday, November 11, 2011

To Prevent Cross site scripting.

By default when submitting form using dsp:form or dsp:a requiresSessionConfirmation is set to true.
Set warnOnSessionConfirmationFailure set to true and enforceSessionConfirmation set to true in /atg/dynamo/Configuration
Once you have above settings, for every dsp:form and dsp:a there will be a hidden _dynSessConf number associated with it and will be same across all forms in that session.

At the time form submit,
if session is invalidated or timed-out or if the _dynSessConf number doesn't match with one in current session,
then
    if requiresSessionConfirmation and enforceSessionConfirmation set to true,
          response code is set 409 and form is not processed.

Source - http://download.oracle.com/docs/cd/E23095_01/Platform.93/PageDevGuide/html/s0606preventingcrosssiteattacks01.html

Issues with jsessionid in url

When application server will append jsessionid to url?
If the request is first from client and there are no cookies (related to that commerce site) at client side,
   then application server will create new session and have that sessionid appended to url (as url agrument parameter seperated by ";" its not query parameter "?")
 and will also set jsessionid cookie at the client side.

Why application server is appending to url?  
To support browsers which do not allow cookies.
As mentioned in  http://randomcoder.org/articles/jsessionid-considered-harmful
"Cookieless sessions are achieved in Java by appending a string of the format ;jsessionid=SESSION_IDENTIFIER to the end of a URL. To do this, all links emitted by your website need to be passed through either HttpServletResponse.encodeURL(), either directly or through mechanisms such as the JSTL <c:out /> tag. Failure to do this for even a single link can result in your users losing their session forever." 

 For example: in url
 http://localhost:8080/store/store/product/view_product_details.jsp;jsessionid=EF8300224952E06E951475223380F9B0?_DARGS=/store/store/product/includes/vpd_product_selection_display_fg.jsp

And in the generated html page - 
<form action="/store/store/product/view_product_details.jsp;jsessionid=EF08475F9F98B45556DCA1946F35C013_DARGS=/store/store/product/includes/vpd_product_selection_display_fg.jsp" method="post" id="add-to-cart">

Issues having jsessionid in url
But having "jsessionid" in the url leads to below problems
1) If we are caching the common pages at AKAMAI level.
     For example:
User 1 requested page product_details.jsp and Since it is the first request, appserver includes jsessionid in the urls in the generated page. 
Akamai cached the page (product_details.jsp) which got generated for user 1. 
When user 2 requested same page, Akamai serves that cached page, the request wont even go to web server or app server. 
Now if the user 2 tries to add an item, the form action url contains a session id of user 1 and when it comes to webserver/load balancer
which redirects the request based on sessionid to appserver, the appserver will add the item to user 2 session.

2) Google bot problems as mentioned in http://randomcoder.org/articles/jsessionid-considered-harmful
    "To prevent abuse, search engines such as Google associate web content with a single URL, and penalize sites which have identical content reachable from multiple, unique URLs. Because a URL-encoded session is unique per visit, multiple visits by the same search engine bot will return identical content with different URLs. This is not an uncommon problem; a test search for ;jsessionid in URLs returned around 79 million search results."

   "Because the session identifier is included in the URL, an attacker could potentially impersonate a victim by getting the victim to follow a session-encoded URL to your site. If the victim logs in, the attacker is logged in as well - exposing any personal or confidential information the victim has access to. This can be mitigated somewhat by using short timeouts on sessions, but that tends to annoy legitimate users." 

How to fix above issues?
Below are three ways to fix this issue.
1) Fix using filter is mentioned in http://randomcoder.org/articles/jsessionid-considered-harmful

Assuming you have mod_rewrite enabled in your Apache instance, use this configuration in your apache config:



# This should strip out jsessionids from google
RewriteCond %{HTTP_USER_AGENT} (googlebot) [NC]
ReWriteRule ^(.*);jsessionid=[A-Za-z0-9]+(.*)$ $1$2 [L,R=301]
This rule says for request where the user agent contains “googlebot” (with case insensitive matching), rewrite the URL without the jsessionid. It seems to work nicely.
By using above way url rewriting is done at web server level.

3)Other way is to use http://www.tuckey.org/urlrewrite/
"Based on the popular and very useful mod_rewrite for apache, UrlRewriteFilter is a Java Web Filter for any J2EE compliant web application server (such as ResinOrion or Tomcat), which allows you to rewrite URLs before they get to your code. It is a very powerful tool just like Apache's mod_rewrite."

Example - 
in /WEB-INF/web.xml
      <filter>
            <filter-name>UrlRewriteFilter</filter-name>
            <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
            <init-param>
                  <param-name>logLevel</param-name>
                  <param-value>INFO</param-value>
            </init-param>

            <init-param>
                  <param-name>confPath</param-name>
                  <param-value>/WEB-INF/urlrewrite.xml</param-value>
            </init-param>
      </filter>

      <filter-mapping>
            <filter-name>UrlRewriteFilter</filter-name>
            <url-pattern>/*</url-pattern>
      </filter-mapping>

in urlrewrite.xml
     <rule>
          <from>^(.*);jsessionid=[A-Za-z0-9\-\+\*\._]+(.*)$</from>
          <to type="redirect">$1$2</to>
     </rule>
Please note that this filter mapping should be before PageFilter, so that before handling request to PageFilter the jsessionid should be stripped from url.

Wednesday, November 9, 2011

ATG session management

Please go through below ATG Tech notes on how ATG manages session on third party servers.
https://docs.google.com/open?id=0B8rpgofYlaJnYTBiNGNmNjMtODU0MS00NDNmLThmOWItNGQzN2UwODBjODRi

Application server creates separate session for each web application (war) in EAR.
For example in your application (EAR) test.ear you have common.war (context is /) and store.war (context is store).

In jboss, you can see how many sessions are created for each web application.

For example go to http://localhost:8080/jmx-console/
and search for host=localhost,path=/<web-app-context>,type=Manager, once you click on that link you will find many settings related to session like activeSessions, maxInactiveInterval etc. You can also list all sessions created for this context by invoking listSessionIds()
host=localhost,path=/,type=Manager to see how many sessions are created with context "/"
host=localhost,path=/store,type=Manager to see how many session are created with context "/store"

As mentioned in above ATG Tech note -
By default, DafEar\base\j2ee-components\atg-bootstrap.war is the parent application with a context root of /dyn.  
All web applications to define the atg.session.parentContextName and atg.dafear.bootstrapContextName parameters in their web.xml to point to the parent web application.

ATG Nuclues components live outside the application servers session
So a listener (atg.servlet.SessionBindingReporter) is added to each web application session as attribute.
When ever application server creates a new session
      SessionBindingReporter will increment SessionNameContext.mNumWrappingNameContexts
When ever application server invalidates a session
      SessionBindingReporter will decrement SessionNameContext.mNumWrappingNameContexts

Thus when SessionNameContext.mNumWrappingNameContexts reaches zero, which means all parent and child web-app sessions are expired.

ATG Nuclues session scoped components for that session are removed.
Even if you have different timeout set at each war level, highest amongst them is considered for session expiration because ATG will remove session scoped components only when all web app sessions are invalidated.

Handling session expiration during form submission.
To redirect the user to session expiration page when his/her session is invalidated during form submission.

set checkForValidSession to true in your formhandler properties file or set using dsp:input at the time of form submission.
Above way will work only if formhandler extends GenericFormHandler and using "checkFormRedirect" method to redirect to success or failure page.

If above property is set, in GenericFormHandler.checkFormRedirect a form exception is added with key "sessionExpired" and redirected to failure page.

For some OOTB formhandler's like ShoppingCartModifier and CartModifierFormHandler there is other way to do

In both ShoppingCartModifier and CartModifierFormHandler we have property "sessionExpirationURL"
Set this property (as hidden in form) to page where you want the user to redirect if his/her session got invalidated.
Example:
<dsp:input bean="ShoppingCartModifier.sessionExpirationURL" type="hidden" value="../../common/SessionExpired.jsp"/>
Regarding session timeout
Note that since sessions are created and managed by application servers like JBOSS, Weblogic or Websphere. Session timeout set at application server is used (/atg/dynamo/servlet/sessiontracking/SessionManager.sessionInvalidationTime is not considered).

ATG session timeout can be set at each web-app level (web.xml) or at global level
By default session timeout is set to 30 mins in JBOSS
In /cygdrive/c/Ecomm/jboss-eap-4.2/jboss-as/server/<server>/deploy/jboss-web.deployer/conf
   <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
In weblogic - weblogic-application.xml
<!--
        ============================================================
   weblogic-application.xml
   This file is used to configure
   cookie name, session timeout, id length
    ============================================================
-->
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wls="http://www.bea.com/ns/weblogic/90" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd http://www.bea.com/ns/weblogic/90 http://www.bea.com/ns/weblogic/90/weblogic-application.xsd">
  <wls:application-param>
     <wls:param-name>webapp.encoding.default</wls:param-name>
      <wls:param-value>UTF-8</wls:param-value>
    </wls:application-param>
  <wls:session-descriptor>
    <wls:cookie-name>JSESSIONID</wls:cookie-name>
    <wls:timeout-secs>1200</wls:timeout-secs>
    <wls:id-length>14</wls:id-length>
    <wls:sharing-enabled>true</wls:sharing-enabled>
    <wls:url-rewriting-enabled>false</wls:url-rewriting-enabled>
  </wls:session-descriptor>
</wls:weblogic-application>
Warn user for session expiration
To notify user for session expiration like a pop up we need to use javascript. Mostly this javascript should be included in header page  so that it will be executed in all pages.
function sessionTimeout(){
        var millsec = 29 * 60 * 1000; // depending on your session timeout.
        setTimeout("sessionWarning()", millsec);
 }

function sessionWarning(){
    warningURL = "/common/sessiontimeoutlogoutwarning.htm";
    var win = window.open(warningURL, "sessionTimeoutWarning", "width=450,height=175");
    win.focus();
}

// calling session timeout method.
sessionTimeout();

Monday, October 17, 2011

"Redirection" after "dsp:include" wont work in ATG.

Normally in J2EE, we can redirect to another page after  any "include" in the page.

For example this will work
<@include page="test2.jsp"/>
  <jsp:forward page="test3.jsp"/>
or
<jsp:include page="test2.jsp"/>
<jsp:forward page="test3.jsp"/>

But once you have dsp:include in page, you will not be able to redirect to any other page using any redirection operation like "Redirect" droplet or "jsp:forward" or request dispatcher etc.

For example:
test1.jsp
<%@ taglib uri="dsp" prefix="dsp"%>
<dsp:page>
   I am in test1.jsp <br>

  <%-- including test2.jsp --%>
  <dsp:include page="test2.jsp"/>
 
  <%-- redirect to page test3.jsp --%>
  <dsp:droplet name="/atg/dynamo/droplet/Redirect">
    <dsp:param name="url" value="test3.jsp"/>
  </dsp:droplet>

  end of test1.jsp <br>
</dsp:page>
test2.jsp
<%@ taglib uri="dsp" prefix="dsp"%>
<dsp:page>
 I am in test2.jsp <br>
</dsp:page>
test3.jsp
<%@ taglib uri="dsp" prefix="dsp"%>
<dsp:page>
  I am in test3.jsp <br>
</dsp:page>
if you access http://localhost:8080/test1.jsp
output -
I am in test1.jsp
I am in test2.jsp
end of test1.jsp
 If you remove " <dsp:include page="test2.jsp"/>" in test1.jsp and
access http://localhost:8080/test1.jsp
you will be redirected to test3.jsp
I am in test3.jsp
If you use jsp:include or @include instead of dsp:include like below
  <jsp:include page="test2.jsp"/>
  <dsp:droplet name="/atg/dynamo/droplet/Redirect">
    <dsp:param name="url" value="test3.jsp"/>
  </dsp:droplet>
You will be redirected to test3.jsp. 

Tuesday, October 11, 2011

Using ATG development operation tags.

Generating DDL's of any repository
If you have created a new repository or new item-descriptor in reposiotry in your module.
To generate ddl,just start the instance (ignore table missing errors).
Once instance is up, go to that particular repository in dynamo component browser (/dyn/admin/)
In "Run XML Operation Tags on the Repository"
use
<print-ddl/>
Which will give you DDL for that repository.

output -
-- drop table www_instock_wic;
CREATE TABLE www_instock_wic (
    primary_wic         INTEGER    NOT NULL,
    primary_sku_id         varchar2(254)    NOT NULL,
    instock_wic         INTEGER    NOT NULL,
    ship_sub_wic         INTEGER    NOT NULL,
    ship_sub_sku_id     varchar2(254)    NOT NULL,
    update_dttm         DATE    NULL,
    sub_wic_desc         varchar2(250)    NULL,
    sub_start_date         DATE    NULL,
    PRIMARY KEY(primary_wic)
);
If your repository is version repository since $class for that repository is pointing to
 atg.adapter.version.VersionRepository

DDL generated will have all columns which are needed for versioning. 
-- drop table dcs_catalog;
CREATE TABLE dcs_catalog (
    catalog_id         varchar2(254)    NOT NULL,
    asset_version         INTEGER    NOT NULL,
    version         INTEGER    NULL,
    creation_date         DATE    NULL,
    last_mod_date         DATE    NULL,
    migration_status     INTEGER    NULL,
    migration_index     INTEGER    NULL,
    item_acl         CLOB    NULL,
    display_name         varchar2(254)    NOT NULL,
    version_deleted     number(1)    NULL,
    version_editable     number(1)    NULL,
    pred_version         INTEGER    NULL,
    workspace_id         varchar2(254)    NULL,
    branch_id         varchar2(254)    NULL,
    is_head         number(1)    NULL,
    checkin_date         DATE    NULL,
    CHECK (version_deleted IN (0, 1)),
    CHECK (version_editable IN (0, 1)),
    CHECK (is_head IN (0, 1)),
    PRIMARY KEY(catalog_id, asset_version)
); 
You can also generate ddl's using startSQLRepository.

In <ATG-HOME>/bin/
bin\startSQLRepository -m <module-name> -repository /atg/commerce/catalog/ProductCatalog -outputSQLFile product_catalog.sql

Testing RQL's - query-items
<query-items item-descriptor="inventory"> catalogRefId="sku386558" </query-items>

output -
Query: catalogRefId="sku386558" returns 1 items:
<add-item item-descriptor="inventory" id="inv1000">
  <!-- export is false   <set-property name="version"><![CDATA[4]]></set-property>  -->
  <set-property name="catalogRefId"><![CDATA[sku386558]]></set-property>
  <set-property name="creationDate"><![CDATA[__NULL__]]></set-property>
  <set-property name="startDate"><![CDATA[1/14/2005 05:10:17]]></set-property>
  <set-property name="availabilityStatus"><![CDATA[OUTOFSTOCK]]></set-property>
</add-item>
--- Querying using date or timestamp by using RQL
With date
<query-items item-descriptor="order"> creationDate>date("2011-10-10") and state="SUBMITTED" </query-items>

With timestamp
<query-items item-descriptor="order"> creationDate>date("2011-10-10 10:00:00 EST") and state="SUBMITTED" </query-items>

To check what items are in cache
<dump-caches item-descriptors="skuSpecialPriceRules" dump-type="both"/>
output -  
Desc: skuSpecialPriceRules mItemCache: 2 items, 0, weak items
LRU: [skuSpecialPriceRules:9400059, skuSpecialPriceRules:5100045]
mItems: {5100045=skuSpecialPriceRules:5100045,
9400059=skuSpecialPriceRules:9400059}
mWeakItems: {}
OnDeckItemCache: null
NullItemCache: {}

*** begin pre-cache XML output
<load-items item-descriptor="skuSpecialPriceRules">5100045,9400059</load-items>
*** end pre-cache XML output
Other operation tags
<add-item>, <update-item>, <remove-item> -to add or update or remove items
<export-items>, <import-items> - to export and import items
<print-item> - to print item
<transaction> - to maintain transactions while adding or removing items.

Reference - http://download.oracle.com/docs/cd/E23095_01/Platform.93/RepositoryGuide/html/s1201sqlrepositoryreference01.html

Monday, October 10, 2011

How to bypass dynamo logs from log4J in JBOSS?

By default,  when using JBOSS server ATG overrides
$class with  $class=atg.nucleus.logging.commons.CommonsLoggingLogListener
in \DafEar\Tomcat\config\tomcatconfig.jar/atg/dynamo/service/logging/ScreenLog.properties

So logs with colorizer will be like below (click to enlarge)


If you observe all debugs and infos will be in green and error log is split into multiple lines.
To get back normal and clean dynamo logs with ATG colorizer

If EAR is non-standalone
    In <ATG-HOME>/<LOCALCONFIG>/atg/dynamo/service/logging/ScreenLog.properties
If EAR is standalone
    In <ATG-DATA>/<LOCALCONFIG>/atg/dynamo/service/logging/ScreenLog.properties
have below lines
$class=atg.nucleus.logging.PrintStreamLogger
useNucleusPathForClassName=true
useFullPaths=true
suppressTimestamp=false
and comment log4j section in <JBOSS-HOME>/server/<server-name>/conf/jboss-service.xml
   <!-- Log4j Initialization --> 
   <!--
   <mbean code="org.jboss.logging.Log4jService"
      name="jboss.system:type=Log4jService,service=Logging"
      xmbean-dd="resource:xmdesc/Log4jService-xmbean.xml">
      <attribute name="ConfigurationURL">resource:jboss-log4j.xml</attribute>
      <attribute name="Log4jQuietMode">true</attribute>
      <attribute name="RefreshPeriod">60</attribute>
   </mbean>
   -->
Now you will get plain old dynamo logs - neater and simple. (click to enlarge)


 You can observe the difference between screenshots with same log statements. I feel the later one is better :)

Friday, October 7, 2011

How to take thread dumps in JBOSS?

If you see any request is taking a lot of time, in order to know what might be the issue, you can go through thread dumps.  Thread may be waiting for some resource (it can be database call or any third party call), you can know exact cause by looking at thread dumps.

What is a thread dump?
   Current snapshot of threads which are running in JVM.

By using jboss jmx-console
http://localhost:8080/jmx-console
and search "serverInfo" and click on that link
Click invoke under listThreaddump
Which will give you current snapshot of threads which are running in JVM.

By using twiddle
go to <JBOSS-HOME>/bin
In windows - 
twiddle.bat invoke "jboss.system:type=ServerInfo" listThreadDump > dump.html
dump.html will have your thread dump.

In unix-based machines
/jboss/bin/twiddle.sh invoke "jboss.system:type=ServerInfo" listThreadDump > dump.html 2>&1
Using "Interrupt" signal - SIGQUIT
Use kill -3 <process-id> to generate thread dump. You will find the thread dumps in server logs.

Typically to know exact cause you need to take at least 2 to 3 thread dumps with 30 seconds gap, so that you will know what is the common thread which is waiting for long time.


Thursday, October 6, 2011

How to correct projects in unknown state in BCC/CA ?

Often deployments will fail due to some reason and even if we use cancel button sometimes ATG fails to revert the project to previous state. And because of JVM crash or normal bounce of BCC instance, currently deploying projects will go to unknown state, follow below steps to correct such type of projects.


If BCC instance is bounced while deploying a project to staging
we can get back the project previous state so that we can start from Author state
-- remove asset locks on the project. if any
delete from avm_asset_lock where workspace_id =
(select id from avm_devline where name =
(select workspace from epub_project where project_id = 'prj49001'));

-- change states. Locked to false and editable to true
--   locked to 0 and editable to 1
update epub_project
set locked=0
where project_id = 'prj49001';

update epub_project
set editable=1
where project_id = 'prj49001';

-- change states. Locked to false and editable to true
--   locked to 0 and editable to 1
update avm_workspace
set locked=0
where ws_id =
(select id from avm_devline where name =
(select workspace from epub_project where project_id = 'prj49001'));

update avm_workspace
set editable=1
where ws_id =
(select id from avm_devline where name =
(select workspace from epub_project where project_id = 'prj49001'));

-- finally change state to author which is 3 for all workflows
update epub_ind_workflow
set state=3
where process_id =
(select process_id from epub_process where project='prj49001');
commit;

Invalidate below repository caches
/atg/epub/PublishingRepository/
/atg/epub/version/VersionManagerRepository/
If BCC instance is bounced while deploying a project to production
we can get back the project previous state so that we can redeploy to
production.
--If project with workflow editCommerceAssets.wdl
--stuck while deploying to production

update epub_ind_workflow
set state=11
where process_id =
(select process_id from epub_process where project = 'prj49001');
commit;

-- For projects which are created by auto import workflow

-- If project with /Commerce/autoImportAssets.wdl workflow
-- stuck while production

update epub_ind_workflow
set state=10
where process_id =
(select process_id from epub_process where project = 'prj49001');
commit;

Invalidate only /atg/epub/PublishingRepository/

Completely delete a project
-- Removing locks of the project if any
delete from avm_asset_lock where workspace_id in
(select id from avm_devline where name in
(select workspace from epub_project where project_id = 'prj243002'))

-- delete history of the project
delete from EPUB_PR_HISTORY where project_id in
(select project_id from epub_project where project_id = 'prj243002');

-- delete the project
delete from epub_project where project_id = 'prj243002';

-- delete history of the process
delete from EPUB_PROC_HISTORY where process_id in
(select process_id from epub_process where project = 'prj49001');

-- delete task information of process
delete from EPUB_PROC_TASKINFO where id in
(select process_id from epub_process where project = 'prj49001');

-- delete states of project (if any)
delete  from EPUB_IND_WORKFLOW where process_id in
(select process_id from epub_process where project = 'prj49001');

-- finally delete the process
delete from epub_process where where project = 'prj49001';

commit;
---- Extra Sqls that may give more info --

To give all conflicting projects
select * from epub_project where workspace in
(select name from avm_devline where id in
(select workspace_id from avm_asset_lock))
To give all conflicting projects for a particular asset.
select * from epub_project where workspace in
(select name from avm_devline where id in
(select workspace_id from dcs_category where category_id = 'cat3484800002' and category_id in
(select repository_id from avm_asset_lock where descriptor_name='category')))

Wednesday, October 5, 2011

Understanding dead locks in Inventory Repository

How locking happens in InventoryManager?
Whenever we call InventoryManager.purchase and InventoryManager.setAvailabilityStatus to update inventory stock level and availability status ATG acquires row-level locking on the inventory item which it is trying to update.

What is row-level locking?
Once a dml query is fired on a row, oracle OOTB will acquire lock on that row and lock on that row will be released once commit or rollback is done on that row. In between acquiring and releasing lock, no other session can acquire lock on same row to update (but still others can read that row). Other transactions (which wants to update the same row) will wait until the lock on that row is released. You can find all locks in v$ object tables like v$lock, v$session ..

Typically, while order is getting placed or once order is placed we need to update inventory.

$class of  InventoryManager OOTB points to atg.commerce.inventory.RepositoryInventoryManager
In atg.commerce.inventory.RepositoryInventoryManager
purchase() {
  //  transaction is required.
  // call getInventoryItemForUpdate
  // call updateItem()
  // end transaction
}

getInventoryItemForUpdate() {
   //  call lock()
   //  get inventory item to update
}

lock(){
    // ensure transaction
    // until InventoryManager.maximumRetriesPerRowLock (default 5)
    // open connection - ((atg.adapter.gsa.GSARepository)getRepository()).getDataSource().getConnection()
    // ps = c.prepareStatement(sql); (sql is InventoryManager.inventoryRowLockSQL - UPDATE dcs_inventory SET inventory_lock = ? WHERE catalog_ref_id = ?)

    // ps.setString(1, lock);  (lock - "inventoryManager")
    // ps.setString(2, pItemId); (pItemId - "skuId")
    // rowcount = ps.executeUpdate();
    // ps.close();
    //        close the connection
    // c.close();
}
Once lock() method is called for one InventoryItem, it will fire udpate sql on that particular row  (the value which it is trying to update is not important here. Which row it is trying to update is important), by default in one transaction if one dml query is fired oracle OOTB acquires lock on that row (you can find those locks in v$objects if you have dba access) and lock on that row is released only once the transaction is either roll backed or committed.


How inventory dead locks happen?
For example
person1 cart has 2 sku's - sku-A and sku-B
person 2 cart has 2 sku's - sku-B and sku-A

If both are trying to place orders at same time.
thread-x of person1 got lock on sku-A inventory item.
thread-y of person 2 got lock on sku-B inventory item.
thread-x waits to get lock on sku-B since thread-y locked it.
thread-y waits to get lock on sku-A since thread-x locked it.
There is dead-lock between thread-x and thread-y

How to avoid those dead locks?
ATG OOTB we have atg.commerce.inventory.RepositoryInventoryManager.acquireInventoryLocks(List pItemIds)
which will sorts the sku's before getting locks on them.
You need to pass list of sku id's which are in the cart to acquireInventoryLocks method.
Once you call acquireInventoryLocks and then call purchase method you can avoid dead locks.

so now

person1 cart has 2 sku's - sku-A and sku-B
person 2 cart has 2 sku's - sku-B and sku-A

If both are trying to place orders at same time.
// first call acquireInventoryLocks - sort and get locks
// second call purchase

thread-x of person1 got lock on sku-A inventory item.
thread-y of person2 waits to get lock on sku-A since thread-x  has lock on it.

Note: OOTB we have releaseInventoryLocks which does nothing, it wont release locks. Locks are only released once the transaction is committed or roll backed.

Note: for some reason, because of out-of-memory or normal bounce if the instance is bought down, then the row-locks which are acquired will be still be locked forever.  If this updates are being done in a processor. Move this processor to last in the pipeline (and use sql JMS to update inventory status/level).