Saturday, December 24, 2011

Holiday Readiness | Performance tips for WCS sites?

1. One of the important thing to start with, DB clean jobs for guest users\guest orders\CTXMGMT\CTXDATA, need to setup and monitored regularly.
2. Expired promotions should be monitored and deactivated.--This was a problem in previous versions
3. The number of times OrderCalculateCmd is called should be minimal. This is a very heavy command, as it calls calculation engine for tax\shipping\promotions. One of the sites, I worked on this was called multiple times during merge cart, cart, checkout. It is a very heavy command. If the number of items in the cart remains same and promotions are not added at the run time, should try to avoid a call to this.
4. Junk cart should be assigned to a custom user.This is to safeguard against any valid scenario invoking JunkCart on a live site.
5. Dyna cache enhances greatly the performance and should be used as it reduces the CPU usage greatly. One could also create CacacheableControllerCommands to taking Dyna to the next level and I have used this and it helps improve performance a lot.
6. Monitor production errors and calls made on web server access logs and fixing any errors, would help enhance performance. Reducing errors helps reduce load on application server.
7. Pricing is one of the heavy calls in WCS and we have built a pricing registry to enhance the pricing performance problem, it helped greatly.
8. On a couple of sites, I worked on Ajax calls were used heavily and most of our AJAX calls are Dyna cached and that helps heavily.Also sections of the page such as cart page where calls to bring up accessories or other merchandising associations should be converted to Ajax calls and it wouldn't block the main functionality of the page such as cart.
9. DB performance, making sure the stats are gathered correctly and finding out the top 10 performance intensive queries and run maximum number of times and making sure for all these queries, the indexes are correctly defined and tune the queries to make sure they efficient (for oracle use Explain plan).
10. Make sure all custom tables have opt counter triggers other wise this could cause race conditions.
11. A lot of performance is achieved from Edge Caching. We use Akamai where all the product and category pages are completely caches. Edge servers are refreshed every night after stageprop. (This is not required if new content is not refreshed every night).
12. Number of items in cart limit, there are some bots that try to add 100's of products to cart and that it self doesn't break but a combination of that and if there are automatic promotions could cause instability. I would advise a configurable parameter for cart limit and writing custom code for this. If you do not want to do a code fix, also you can do


ALTER TABLE SCHEMA_NAME.ORDERITEMS ADD
CONSTRAINT check_orderitems_1
CHECK (quantity <= 100) ENABLE
VALIDATE

The constraint will bring the user back to home page on trying to add to cart.

13. Extra objects from memory can be controlled specially while using beans in JSP's by making sure the scope is correctly defined
e.g. Specially this starts to matter for Order tunnel pages as there are multiple imports to different JSP's. It helps by giving the scope to request.

14. Reduce the guest user creation in the system by making sure, the isGeneric returns true, if a command needs to be used for generic users. We found multiple commands when turining the isGeneric to true reduce the creation of thousands of guest users. By default isGeneric is false.

15. If you are not using OOB payments, there are some commands that still could call and populate these tables on top of disabling from wc-server.xml , the following update is required.

INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.PrimePaymentCmd','com.ibm.commerce.edp.commands.PrimePaymentVoidCmdImpl','Local'); INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.ReservePaymentCmd','com.ibm.commerce.edp.commands.ReservePaymentVoidCmdImpl','Local'); INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.FinalizePaymentCmd','com.ibm.commerce.edp.commands.FinalizePaymentVoidCmdImpl','Local'); INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.TriggerPaymentActionsCmd','com.ibm.commerce.edp.commands.TriggerPaymentActionsVoidCmdImpl','Local'); INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.CancelOrderCmd','com.ibm.commerce.edp.commands.CancelOrderVoidCmdImpl','Local'); INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.StoreAndValidatePaymentCmd','com.ibm.commerce.edp.commands.StoreAndValidatePaymentVoidCmdImpl','Local'); INSERT INTO CMDREG(STOREENT_ID, INTERFACENAME, CLASSNAME, TARGET) VALUES (0,'com.ibm.commerce.edp.commands.PIAddCmd','com.ibm.commerce.edp.commands.PIAddVoidCmdImpl','Local');

16. Multiple Invalid cookie errors were caused due to contention as we have multiple AJAX calls in pages and due to contention. Implemented a CustomFilter and convert requests into stateless so that commerce
can process as generic user requests.
// Check for stateless URL
if (uri != null && urlsToFilter != null && urlsToFilter.indexOf(uri) >= 0) {
request = new CustomHttpServletRequestWrapper(request, response);
response = new CustomHttpServletResponseWrapper(response);
}

chain.doFilter(request, response);

LoadRunner | Holiday Readyness

Most large B2C enterprises perform, holiday readiness to make sure the site remains stable and can handle the holiday web traffic smoothly. As a part of the exercise multiple load test runs are performed  and usually  monitored in a performance environment with production data and any bottle necks and performance issues found during this are fixed.

Important Analysis pointers:
Java Garbage collection: If there are increases in response time in bursts. it can be attributed to Garbage collection.
The graphs for hits per second should follow a similar pattern to Throughput otherwise there is something in
Correlating failed transactions with errors.

Monitor CPU/IO/Memory during load tests: It is a good idea to run this every 30 seconds for the duration of the load tests. This is for App server and DB server.

vmstat - reports information about processes, memory, paging, block IO, traps, and cpu activity
iostat - Used for reporting CPU statistics and input/output statistics for devices, partitions and network filesystems (NFS)
Load Runner has 3 main components:
VU Gen (Virtual User Generator)
--Load generators are installed on multiple machines to spread the load for generating the traffic.
--Depending on the traffic required, this could change but usually 3-5
Load Runner Controller (Console)
--This is the main terminal that can be used to connect to consoles , Load scripts and run load runner tests.
Analysis
--This is used to view multiple charts and graphs for analysis data points from tests run.

Some Key Terminology when using Load Runner:
Virtual User & Distribution of virtual users: Virtual users are used to emulate the behavior of real users.
Transaction Mix:  This is based on business scenarios. Usually this is divided among-st various scripts based on traffic patterns from web analytics (Omniture\Google\CoreMetrics, etc any of these)
Ramp up\Ram down:  The load on the performance site is gradually increased and gradually decreased. Gradually increasing helps with cached pages for accurate test results.
Load is usually gradually increased and usually a 2-3  interval is provided to add virtual users.
Thinking Time: This is the time between 2 actions. In trying to emulate human behavior, it is the time, human takes to think between page clicks.
Total Duration of test = RampUp+Duration of load test+RampDown

Key Analysis parameters:
Response Time per Transaction: Time taken for the application to complete a transaction or a business scenario. This time includes Network Transmission+ Network Latency+Web Server processing time+App Server Processing time+ DB server Processing time.
This is very helpful parameter to determine during peak loads, who the system would really behave
Hits/Second: Number of requests hitting webs server per second. This is helpful in conjunction with transaction time to see, how the performance of the site behaves as the number of hits/sec increases.

Through Put: Is the amount of data in kilobytes received by a user per second

HTTP Response Counts: Most valid responses are 200, 303 (redirection) and It is a good idea to keep the tab on 403 (forbidden), 404 (page not found), 500 (Internal server error)

Error count: Keeps track of error counts, this is a good measure to make sure there are no application server errors, This is really helpful, if the correct build and database changes are not present in performance.









Saturday, November 12, 2011

Locale | Importance of LangPair !!

Locale is a combination of country and language and is very useful when creating a commerce site for multiple countries as it helps define globalized cultural attributes for various features address, number formatting, currency formatting, character classification, date\time formatting.etc.

Using in JSP with fmt tag:
fmt:setLocale value="${CommandContext.locale}" />

If you are using fmt for number formatting\date time formatting. Can set the locale from command context.
e.g. locale="en_US";

If you need a snippet to create a locale from language and country:
String language=locale.substring(0, locale.indexOf(LOCALE_SEPERATOR));
String country=locale.substring(locale.indexOf(LOCALE_SEPERATOR)+1,locale.length());
Locale localeObj = new Locale(language, country);

LangPair:

This is very useful when supporting a multi-language sites. All the relations for a particular Storetntity e.g. CATENTDESC, if does not have an entry for all the SKU descriptions, for the language in context. It would fall back to the default language in the langpair.

e.g.:
insert into langpair values (20120, -2, -1, 0, null);

Sunday, October 30, 2011

Read/Write Order access to users from same organization


I am sure you have seen a similar error when trying to give access to commands for different roles.

Do not just override the command and return null from getResources() method that would void any access controls on the order commands associated and could causes security issues, the correct way of fixing such issues, is to figure out the correct PolicyGroup and create custom policies with the corresponding roles. An example would be as below. In this case, you need to give users Buyer(buy-side) role to create\copy orders.
CMN1501E: User does not have the authority to perform action "com.ibm.commerce.order.commands.OrderCopyCmd" on resource "com.ibm.commerce.user.objects._Organization_Stub" for command "AjaxOrderCopy


<Policy Name="CustomUsersFromSameBuyerOrgExecuteOrderCreateCommandsOnOrganizationResource"
         OwnerID="RootOrganization"
         UserGroup="AllUsers"
         ActionGroupName="OrderCreateCommands"
         ResourceGroupName="OrderDataResourceGroup"
         RelationGroupName="Buyer (buy-side)->BuyerOrganizationalEntity"
         PolicyType="groupableStandard">
    </Policy>      
     <Policy Name="CustomUsersFromSameBuyerOrgExecuteOrderCreateReadCommandsOnOrganizationResource"
         OwnerID="RootOrganization"
         UserGroup="AllUsers"
         ActionGroupName="OrderReadCommands"
         ResourceGroupName="OrderDataResourceGroup"
         RelationGroupName="Buyer (buy-side)->BuyerOrganizationalEntity"
         PolicyType="groupableStandard">
    </Policy>      
<Policy Name="CustomUsersFromSameBuyerOrgExecuteOrderCreateWriteCommandsOnOrganizationResource"
         OwnerID="RootOrganization"
         UserGroup="AllUsers"
         ActionGroupName="OrderWriteCommands"
         ResourceGroupName="OrderDataResourceGroup"
         RelationGroupName="Buyer (buy-side)->BuyerOrganizationalEntity"
         PolicyType="groupableStandard">
    </Policy>

    <PolicyGroup Name="CommonShoppingPolicyGroup" OwnerID="RootOrganization">
      <!-- Define policies in this policy group -->
      <PolicyGroupPolicy   Name="CustomUsersFromSameBuyerOrgExecuteOrderCreateCommandsOnOrganizationResource" PolicyOwnerID="RootOrganization" />
      <PolicyGroupPolicy   Name="CustomUsersFromSameBuyerOrgExecuteOrderCreateReadCommandsOnOrganizationResource" PolicyOwnerID="RootOrganization" />
      <PolicyGroupPolicy   Name="CustomUsersFromSameBuyerOrgExecuteOrderCreateWriteCommandsOnOrganizationResource" PolicyOwnerID="RootOrganization" />
    </PolicyGroup>

Sunday, October 23, 2011

Order history read access users from same organization.


Read only access to users belonging to same organization and Organization Participant role for order history related databeans, the XML policies below is from the reference link below but they have an issue where on the role Buyer (buy-side) role name so that's fixed.
Buyer(buy-side) roles needs to defined on the organization using Org admin console.
if there is an hierarchy of organizations and the bottom organization is the parent organization for the users. All roles have to be defined to Organizations from Top-Down.
Add the xml fragments below to resource_acpolicies.xml and run ACPLoad.
Add new policy to allow participants to display the order beans for others in the organization:
Relation group: This defines all roles in this case Buyer(buy-side) to BuyingOrganizationalEntity

     <RelationGroup Name="Buyer (buy-side)->BuyerOrganizationalEntity" OwnerID="RootOrganization">
        <RelationCondition><![CDATA[
        <profile>
        <openCondition name="RELATIONSHIP_CHAIN">
        <parameter name="ROLE" value="Buyer (buy-side)"/>
        <parameter name="RELATIONSHIP"
        value="BuyingOrganizationalEntity"/>
        </openCondition>
        </profile>
        ]]></RelationCondition>
    </RelationGroup>
   

 Create a new Policy and use the RelationGroupName created above to the ResourceGroup, OrderDatabeanResourceGroup and ActionGroup, DisplayDatabeanActionGroup

        <Policy Name="ParticipantsOfOrgDisplayOrderDatabeanResourceGroup"
            OwnerID="RootOrganization"
            UserGroup="AllUsers"
            ActionGroupName="DisplayDatabeanActionGroup"
            ResourceGroupName="OrderDatabeanResourceGroup"
            RelationGroupName="ParticipantOf->BuyerOrganizationalEntity"
            PolicyType="groupableStandard">
    </Policy>
    <Policy Name="BuyersOfOrgDisplayOrderDatabeanResourceGroup"
            OwnerID="RootOrganization"
            UserGroup="AllUsers"
            ActionGroupName="DisplayDatabeanActionGroup"
            ResourceGroupName="OrderDatabeanResourceGroup"
            RelationGroupName="Buyer (buy-side)->BuyerOrganizationalEntity"
            PolicyType="groupableStandard">
    </Policy>
     

  Subscribe organizations to the new Access Control policies:         
     <PolicyGroup Name="B2BPolicyGroup" OwnerID="RootOrganization">
        <PolicyGroupPolicy
        Name="ParticipantsOfOrgDisplayOrderDatabeanResourceGroup"
        PolicyOwnerID="RootOrganization" />
        <PolicyGroupPolicy Name=
        "BuyersOfOrgDisplayOrderDatabeanResourceGroup"
        PolicyOwnerID="RootOrganization" />
    </PolicyGroup>   


Once the ACPLoad is run, you can validate using a SQL query.
select * from acpolicy where policyname like 'BuyersOfOrgDisplayOrderDatabeanResourceGroup'
select * from acpolicy where policyname like 'ParticipantsOfOrgDisplayOrderDatabeanResourceGroup'

References:
http://www.ibm.com/developerworks/websphere/library/techarticles/0908_callaghan/0908_callaghan1.html
http://www.ibm.com/developerworks/websphere/library/techarticles/0805_callaghan/0805_callaghan.html

Thursday, October 6, 2011

Generated columns UP_* for case insensitive search in V7


You might not find anything while searching about this info center. At least not when I did the search.

In V7 data migration generates some new generated fields and these fields are tablename.UP_*  columns. These fields are used by out of the box case insensitive searches. The grouping functions such as to_lower or to_upper are expensive operations for search when dealing with vast data and that is one of the performance enhancements in V7.

I am pretty sure, if you use out of the box data migration tool for migrating to V7. It should take care of  these generated fields. These fields should not be populated from code, there are triggers in database that would take care of populating these fields.

e.g.
  "UP_MFNAME" VARCHAR(64) GENERATED ALWAYS AS (UPPER(MFNAME)) ,
  "UP_MFPARTNUMBER" VARCHAR(64) GENERATED ALWAYS AS (UPPER(MFPARTNUMBER))
  "UP_PARTNUMBER" VARCHAR(64) GENERATED ALWAYS AS (UPPER(PARTNUMBER)) )

References:
https://www-304.ibm.com/support/docview.wss?uid=swg21498502
http://www.ibm.com/developerworks/data/library/techarticle/0203adamache/0203adamache.html
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=%2Fcom.ibm.db2.luw.admin.dbobj.doc%2Fdoc%2Fc0020109.html

Order state diagram !!

Orde state Diagram
This is probably one of the interesting info center article regarding the state diagram of order status. It shows pretty good transition between states. If you decide to use custom statuses as a best practice choose lower case letters.




Reference:
http://publib.boulder.ibm.com/infocenter/wchelp/v7r0m0/index.jsp?topic=%2Fcom.ibm.commerce.developer.doc%2Frefs%2Frosordstattran.htm

http://publib.boulder.ibm.com/infocenter/wchelp/v7r0m0/index.jsp?topic=%2Fcom.ibm.commerce.user.doc%2Fconcepts%2Fcossostat.htm



Saturday, September 24, 2011

Troubleshooting User log-in and organization relationship

CommerceSrvr E AccManager isAllowed CMN1501E: User 6002 does not have the authority to perform action "Display" on resource "com.ibm.commerce.user.beans.UserDataBean" for command "Logon".
 CommerceSrvr A DataBeanManager directActivate(DataBean,CommandContext) The user does not have the authority to run this command "com.ibm.commerce.user.beans.UserRegistrationDataBean".
t have the authority to perform action "Display" on resource "com.ibm.commerce.user.beans.OrganizationDataBean"

Troubleshooting: The above error was caused as there was a missing relationship in MBRREL to the parent organization.

DN should be lowercase and no spaces between orgs.

If you have Manually imported orgentity table through data load, The DN relationship is stored between the ancestor and descendant in MBRREL. If the tree has multiple parents, all the parent-child relationship should be defined in MBRREL.

A member_id from MEMBER table can be a ORGENTITY_ID, USERS_ID,MBRGRP_ID

 Some queries to help for debugging:

select * from mbrrel where descendant_id=;


select * from orgentity where orgentity_id in (
select ancestor_id from mbrrel where descendant_id=);



USERS:

 MBRREL; ( USER to all parent organization relationship as per dn is loaded here)

MBRROLE & ROLE; (Role for each user is assigned to an organization, e.g. -29 for registered customer)

SELECT DN FROM USERS (DN SHOULD BE THE CORRECT ORGENTITY)


Wednesday, September 21, 2011

Retrieving WCParam value from scriptlet



Using JSPHelper to retrieve WCParam values:

JSTL:
wcparam value AdddressId:<c:out value="${WCParam.addressId}"/>

 above example using JSP Scriptlet:
<%@ page import="com.ibm.commerce.server.JSPHelper" %>

<%
String addressId=(String)com.ibm.commerce.server.JSPHelper.getParameter(request, "addressId") ;
System.out.println("addressId: "+addressId);
 %>


<%
CommandContext jstlCommandContext = (CommandContext)request.getAttribute(ECConstants.EC_COMMANDCONTEXT );
System.out.println("JSTLEnvironmentSetup languageId: "+jstlCommandContext.getLanguageId());

 %>


Using PageContext to retrieve session values in a JSP page:

<c:set var="rsOrderId" value="${WCParam.orderId}"/>

System.out.println("RS OrderID: "+pageContext.getAttribute("rsOrderId"));


Sunday, September 11, 2011

Override Access authority from Commands

checkResourcePermission The user does not have the authority to run this command. CMN0410E: The system failed to retrieve the message with  key "_ERR_USER_AUTHORITY" from "com.ibm.commerce.ras.properties.ecServerMessages"
I am sure all of us working in commerce have seen the above error  a million times and some times it is because we didn't correctly define the access privileges when creating a new command and running ACPLoad but some times it is important to override the out of the box access policies.

I would not recommend returning plain null but also find below some more restricted overrides of getResources.


public AccessVector getResources() {
  return null;
}
public AccessVector getResources()
{          final String METHOD_NAME = "getResources";            
            java.util.Vector resourcesVector = new java.util.Vector();
            Long parentOrgId= null;
            UserAccessBean userAccessBean = getCommandContext().getUser();
            Long[] userAncestors = userAccessBean.getAncestors();
            if(userAncestors != null)   {      parentOrgId = userAncestors[0];   }   
            else   {   return null;     }            
            OrganizationAccessBean orgAccessBean = new OrganizationAccessBean();
            orgAccessBean.setInitKey_MemberId(parentOrgId.toString());
            orgAccessBean.refreshCopyHelper();
            resourcesVector.addElement(orgAccessBean.getEJBRef());
            return new AccessVector(resourcesVector);
   }  
public AccessVector getResources()
{     
 final String METHOD_NAME = "getResources";
        resources = new AccessVector();
        try {
            if (resources.isEmpty()) {
                orderId = getRequestProperties().getString(ORDER_ID,
                        null);
                OrderAccessBean orderAccessBean= new OrderAccessBean();
                oab.setInitKey_orderId(orderId);  
                oab.refreshCopyHelper();
                resources.addElement(
orderAccessBean);
            }
    return resources;
}



Saturday, September 3, 2011

Order & Login flow commands

This post was requested in the comments section last week and  I tried to cover if not all, most of the commands in these flows.

Order Flow:
.
Flow
Controller Commands
Task Commands
Add/Update Items to Cart
OrderItemAddCmdImpl
OrderItemUpdateCmdImpl

Display Cart:
OrderItemDisplayCmdImpl
CheckInventoryCmdImpl
OrderItemDeleteCmdImpl
DoInventoryActionCmdImpl
Checkout/
Review
OrderPrepareCmdImpl

Submit/Place Order
OrderProcessCmdImpl


Order Calculate Command: OrderCalculateCmdImpl, This command is very heavy and is used for calculations on promotions/taxes/shipping discounts. It could be called from Cart display page and Order checkout page, it depends on the requirements and also on cart merge during login. Make sure this command is called less frequently.

Login/Registration:


Flow
Controller Commands
Registration Add/Update
UserRegistrationAddCmdImpl/
UserRegistrationUpdateCmdImpl

Registration using Admin access
UserRegistrationAdminAddCmdImpl/
UserRegistrationUpdateCmdImpl
Login
LogonCmdImpl
Password Reset
ResetPasswordCmdImpl
Asynchronous event after Password Reset
SendPasswordNotificationCmdImpl
(This one’s a task command)
Log off
LogoffCmdImpl
The Asynchronous event after password reset, there is an event that triggers SendPasswordNotificationCmd.

Friday, August 26, 2011

Programming a Schedule Job !!! Dynamic job

I find dynamic schedule jobs a very useful feature in Websphere commerce. It is very useful for jobs that require to be dynamically generated based on some programming logic in a command. e.g. if there are emails that need to be triggered based on a event or any admin action that requires a schedule job to run to generate reports.

AddJobCmd addJobCmd =
(AddJobCmd) CommandFactory.createCommand(
AddJobCmd.class.getName(),
commandContext.getStoreId());
addJobCmd.setQueryString();
addJobCmd.setForUserId();
addJobCmd.setStartTime();
addJobCmd.setPathInfo("");
addJobCmd.setUrl(); //Even though this is not checked in validate parameters, this is required
addJobCmd.setStoreId(new Integer(SITE_STORE));
addJobCmd.setCommandContext(commandContext);
addJobCmd.setAccCheck(false);
addJobCmd.execute();
addJobCmd.setAccCheck(true);


recurring Jobs needs these extra methods to be configured:

addJobCmd.setAttempts()
addJobCmd.setDelay()
addJobCmd.setInterval()


Thursday, August 11, 2011

Application of Activity Token | Multiple ways to getActivityToken

Activity Token is the equivalent of sessionid and thought not advised but sometimes, it is required to use activityId for some custom implementations:
1. If you are implementing a custom XSRF, even though V6 Fix Pack 10 has this feature.
2. Custom WebService across devices that can't use cookies but need to maintain commerce session.

public ActivityToken getActivityToken(HttpServletRequest request)

{
// Check, is it available as a request attribute?
ActivityToken token = null;
token = (ActivityToken) request.getAttribute(ECAttributes.ATTR_EC_ACTIVITY_TOKEN);
// that's not true, try request handle
if (token == null) {
RequestHandle handle = (RequestHandle)request.getAttribute(ECAttributes.ATTR_EC_REQUEST_HANDLE);
token = (handle != null) ? handle.getActivityToken() : null;
}
// Try command context , if above 2 returne
if (token == null) {
CommandContext commandContext = (CommandContext) request.getAttribute("CommandContext");
token = (commandContext != null) ? commandContext.getActivityToken() : null;
}
return token;
}

This is my 50th post, if you guys like reading my blogs :), please drop a comment.

Friday, August 5, 2011

ServerJDBCHelperAccessBean, should you or should you not ?

It is highly recommended to use EJB's for any insert\updates for the overall integrity of the data in commerce but ServerJDBCHelperAccessBean is a good friend that comes handy in
several scenarios when required to insert\update tables outside of EJB's and also do selects from multiple tables. Also the flush() method is used to flush the cache in EJB container for the DB updates.
Internally uses preparedStatement but you don't have to map each param and could send an array of params to the executeQuery method in the same order as query.

It is also useful to use if you have dynamic SQLs for any kind of search.

code Example:

sqlQuery could be dynamic query based on logic being built or static.
databaseRows is an object of vector -> vectors.

ServerJDBCHelperAccessBean jdbcHelper = new ServerJDBCHelperAccessBean();
Vector databaseRows = new Vector();
databaseRows = jdbcHelper.executeParameterizedQuery(sqlQuery, params);
for(Object obj:databaseRows) {
Vector EachRow=(Vector)obj;
Long catentryId= (Long)EachRow.get(0);
String catGroupIdentifier =(String)EachRow.get(0);
Timestamp timePlaced = (Timestamp)EachRow.get(3);
}

Note: If you are using to update a row in DB, do make sure to update the OPTCounter.

Thursday, August 4, 2011

Dynacache - 101 !!!!

Dynacache service is an in-memory cache system that has disk offload capacity.
What is CacheSpec.xml?
The objects to be cached are specified in cachespec.xml.
Location of cachespec in toolkit\Stores\WebContent\WEB-INF
On server: \Stores.war\WEB-INF\
What can be cached? Servlet\JSP's and commands that extend from CacheableCommand interface can be cached using DynaCache.

What is DynaCache Monitor? This is an application provided by IBM for dynacache statistics, it can be installed on toolkit as well as server.

Example from cachespec for JSP\Servlet caching entries:
Servlet: .e.g. storecatalogdisplay:
<cache-id>
<component id="" type="pathinfo">
<required>true</required>
<value>/StoreCatalogDisplay</value>
</component>
<component id="storeId" type="parameter">
<value>10151</value>
<required>true</required>
</component>
<component id="catalogId" type="parameter">
<required>true</required>
</component>
<component id="langId" type="parameter">
<required>false</required>
</component>
<component id="trackingEvent" type="parameter">
<required>false</required>
</component>
<priority>30</priority>
</cache-id>


JSP: e.g. header.jsp
<cache-entry>
<class>servlet</class>
<name>ABCStorefront/include/header.jsp</name>
<property name="do-not-consume">true</property>
<property name="save-attributes">false</property>
<property name="ignore-get-post">true</property>
<sharing-policy>not-shared</sharing-policy>

<cache-id>
<component id="storeId" type="parameter">
<required>true</required>
</component>
<component id="catalogId" type="parameter">
<required>true</required>
</component>

<component id="DC_lang" type="attribute">
<required>false</required>
</component>
<component id="userImpersonated" type="parameter">
<required>false</required>
</component>
<priority>100</priority>
</cache-id>

<!-- Dependency Ids -->
<dependency-id>TTL:STAGEPROP</dependency-id>

<dependency-id>storeId
<component id="storeId" type="parameter">
<required>true</required>
</component>
</dependency-id>
<dependency-id>catalogId
<component id="catalogId" type="parameter">
<required>true</required>
</component>
</dependency-id>
<!-- Ends Dependency Ids -->
</cache-entry>


Enable Servlet caching:
1. In the administrative console, click Servers > Application servers > server_name > Web container settings > Web container in the console navigation tree.
2. Select Enable servlet caching under the Configuration tab.
3. Click Apply.
4. Restart the Server.

Performance and CPU usage: performance of the overall site and CPU usage on the app server is considerably improved with Dynacache enabled. Having a good Dynacache strategy is very important for the performance of the site. Will write another blog on that topic.


Wednesday, August 3, 2011

JSTL | Top 10 JSTL statements most often used !!!

It is highly recommended to use JSTL to build JSP Pages in WCS: ${...} --Expression Language
  • Naming Convention of methods in Bean for boolean check
                      boolean itemsReturned=false;
                      public boolean isItemsReturned() {
                             return itemsReturned;
                       }
    
<if test="${!orderStatus. itemsReturned }">
                  </c:if>
  • Setting Variables in a JSP:



  • <c:set var="status" value="P"/>
  • <c:set var="errorZipCounter" value="0" />
  • <c:set var="accDisplaycount" value="${accDisplaycount + 1 }" />
  • If, Else this is the equivalent of If Else as JSTL does not have the usual If Else
<c:choose>
<c:when test="${!empty WCParam.source}">
<c:set var="source" value="${WCParam.source}" />
</c:when>
<c:otherwise>
<c:set var="source" value="${source}" />
</c:otherwise>
</c:choose>
  • For statement: counter is current status variable, you could get index\first\last\current\count status.
<c:forEach items="${orderList}" var="order" varStatus="counter">
If the orderList is a list of orderdatabeans or something, you could loop through them
</c:forEach>
  • If statment example:
<c:if test="${(!empty WCParam.orderStatus && (WCParam.orderStatus eq 'Y'))}">
You can use eq,ne,lt,gt,ge,le other relational operators
</c:if>
  • Constructing hyperlinks: Prep-ends the name of current servlet context,URl encoding of request params and values, URl re-writing for session management.
<a href="<c:url value='/content/sitemap.jsp'/>">View sitemap</a>

<c:url var="productDisplayURL" value="ProductDisplay">
<c:param name="catalogId" value="${WCParam.catalogId}"/>
<c:param name="storeId" value="${WCParam.storeId}"/>
<c:param name="langId" value="${WCParam.langId}"/>
<c:param name="productId" value="${accessoryItem.itemId}"/>
</c:url>
  • Include content of a JSP into another JSP:
<c:import url="../../CurrentOrderSection/OrderItemDetailsDisplay.jsp">
<c:param name="orderId" value="${WCParam.orderId}"/>
</c:import>

<% out.flush(); %>
<c:import url="${storeDir}include/footer.jsp"/>
<% out.flush(); %>

<% out.flush(); %><c:import url="{storeDir}include/header.jsp"/><% out.flush(); %>
  • Removes a variable from scope:
<c:remove var="wishListURL" scope="page"/>
  • Print the value: Also helpful to print values in JavaScript. The <c:out> tag evaluates an expression and outputs the result on the page
status:<c:out value="${WCParam.status}"/>
firstName:<c:out value="${orderDetailsVB.orderBillingAddress.firstName}"
<script language="javascript" src="<c:out value="${storeJavascriptDir}/browser_detect.js" />"></script>
  • Using functions: There are several functions that are very helpful
var paramvalues = "<c:out value="${fn:substring(config.params, 1, -1)}" escapeXml="false"/>";
<c:set var="dispalyPrice" value="${fn:substringAfter(orderItemTotalPrice,)}"/>
<c:when test="${fn:indexOf(locale, 'en') >= 0}">
</c:when>
  • useBean to instantiate and auto populate the bean
<wcbase:useBean id="orderDetailsVB" classname="com.abc.order.beans.OrderDetailsViewBean">
<c:set target="${orderDetailsVB}" property="orderId" value="${WCParam.orderId}"/>
<c:set target="${orderDetailsVB}" property="orderStatus" value="${status}"/>
</wcbase:useBean>
<wcbase:useBean id="productVB" classname="com.abc.catalog.beans.ItemViewBean" scope="request" >
<c:set target="${productVB}" property="itemId" value="${WCParam.catentryId}" />
</wcbase:useBean>

Friday, July 29, 2011

Krypto | crazyness !!!

When ever a view or command is defined as HTTPS true in struts-config-ext.xml
commerce while redirecting converts all parameters in the URL querty string to a krypto value.
Krypto = encrypted(querystringParameters,merchantKey).
You can bypass params from this krypto by the configuration mentioned below in wc-server.xml

e.g.
storeId=10151&
catalogId=10101&
langId=-1&
orderId=55011&
myName=y

krypto:=7Agv%2BGyHJCuZcMIjIs%2Fd68CfRxuGbxeKNDAGlIEEXgm....

If there are parameters that are required to be not encrypted, commerce provides a configuration to define in wc-server.xml

<NonEncryptedParameters display="false">
<Parameter name="storeId"/>
<Parameter name="langId"/>
<Parameter name="catalogId"/>
<Parameter name="categoryId"/>
<Parameter name="productId"/>
</NonEncryptedParameters>

Problems associated with krypto:
  • Caching nightmare with Krypto. If the params are encrypted, it would be very difficult to define caching parameters so make sure, to add them in the above block. As a best practise all catalog related params, add them to the blocks
  • krypto keeps getting longer and in the older versions of browser, it was a problem where developers had to remove the params and only keep the required one's in request\response.

Sunday, July 24, 2011

Unraveling the code patches !!

For supporting an existing commerce site, performing a code patch is an important feature for the success of an eCommerce platform.
A complete code deploy is not always an option as it could involve a longer down time and even though commerce offers the feature of deploying code on 1 node at a time through Deployment Manager (DM) without bringing down the complete site. The above step is only possible if you don't have database changes.
Also the regression testing for complete code deploy does not make it very ideal. Performing a java code patch e.g. ExtLogonControllerCmdImpl class in package com/company/user/commands

1. If there are any DB related changes. Make them first.
2.Stop 1 server at a time.
3 CD opt/IBM/WebSphere/AppServer/profiles//installedApps/WC__cell/WC_.ear/Stores.war/WEB-INF/classes/
4. Create the complete directory structure of the package and drop the class file. com/company/user/commands/ExtLogonControllerCmdImpl.class.
5. Restart the server.

If you have only JSP changes, drop the JSP in the appropriate folder and delete the JSP cache in temp folder and refresh DynaCache. Does not require a server restart.

Sunday, July 17, 2011

Character Verification on Registration\Forgot Password | Captcha

Recaptcha is a free solution hosted and owned by Google.
Pros: Widely used in the industry, services based solution really easy to impelemnt
Cons:The biggest minus on this is, if you have a strong firewall policy where the outgoing IP/Ports for app servers are blocked. This solution will not work as the IP addresses will change some times and hence it might not be feasible to dynamically open up the firewalls for the new addresses.
Kaptcha is a free Apache license based java solution works with JDK 1.4.2,
Pros: Really easy to implement and drop the jar files, no external webservices required. if you are using V6 and it also works with later versions of JDK hence this would work for V7. Kaptcha uses pixels and previous versions image libraries to refresh the image.
Cons: Does not support audio.


How to use in commerce?

1. Entry in web.xml for the servlet mapping, make this entry after dynacache and other OOB filters.
<servlet>
<servlet-name>Kaptcha</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Kaptcha</servlet-name>
<url-pattern>/kaptcha</url-pattern>
</servlet-mapping>


2. The default implementation has .jpg but if you are using edge caching such as Akamai, removed tha .jpg.
3. Add the external jar file and making a corresponding entry in META_INF (I have a blog on this for reference http://www.ibmwcs.com/2011/06/adding-external-jar-file-to-wcs.html)
4. The API returns HTML collection, you can access in your javascript as follows:
var kaptchaVariable=document.getElementsByName("kaptcha").item(1).value;
5. If you need to refresh the Kaptcha implement a javascript on click and you can do HTML write attribute.
.writeAttribute('src','/webapp/wcs/stores/kaptcha?' + Math.floor(Math.random()*100) );
6. You can also implement an on the img and use the following method
$(AnchorelementPassed).down().writeAttribute('src', '/webapp/wcs/stores/kaptch?' + Math.floor(Math.random()*100) );
7. You can implement in a ajax call or a command but you can validate in validateParameters.
HttpServletRequest request =
((com.ibm.commerce.webcontroller.HttpControllerRequestObject) this
.getCommandContext()
.getRequest())
.getHttpRequest();

String kaptchaValueExpected = (String)request.getSession().
(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);

Reference: http://www.google.com/recaptcha
http://code.google.com/p/kaptcha/

Tuesday, July 12, 2011

Multiple fulfillment center | Remove the default override

When designing certain eCommerce applications, some businesses have multiple fulfillment centers to choose from and this choice is some times provided to the customers.

Commerce by default overrides the FFMCENTER_ID (fulfillment center) information with the default STORE.FFMCENTER_ID, during order item add functionality.

If you want this value to be not overridden and manually given into the order tunnel commands.
In the ORDERITEMS.PREPAREFLAGS. 3rd bit needs to set.

By default, the prepared flags value is 2048 (100000000000) Binary. Setting the 3rd bit to 1 becomes
(100000000100) =2048+4=2052

int currentPrepFlag =orderItems[i].getPrepareFlagsInEJBType().intValue();
currentPrepFlag |=OrderConstants.PREPAREFLAGS_FULFILLMENT_CENTER_OVERRIDE;
orderItems[i].setPrice(String.valueOf(finalprice));
orderItems[i].setPrepareFlags(new Integer(currentPrepFlag ));
orderItems[i].commitCopyHelper();


Tuesday, July 5, 2011

Components Services | BOD versus SOI confusion !

Component Services are a set of services that follow the
Service Component Architecture (SCA) paradigm, which is a set of specifications which describe a model for building applications and systems using a Service-Oriented Architecture. SCA extends and complements prior approaches to implementing services, and SCA builds on open standards such as Web services.


Earlier versions of component services were based on SOI and the newer versions of the services are based on BOD framework from IBM.

The BOD is definitely IBM's future direction so if your building a new service you should consider BOD but if you want to use existing IBM services and do some customization, the decision to choose BOD vs SOI, lies on How commerce implemented these services.

Main difference between BOD and SOI, BOD uses pattern matching and DSL to the enter the database VS SOI uses controller commands\data beans \access beans.

E.g.

If your developing something in V7, the decision is purely based on what services are available for the requirements that one has.

BOD: SyncInventoryAvailability uses INVAVL tables and Distribute order management as inventory model.

SOI: SyncProductAvailability uses Inventory table with non-ATP inventory model.

If your store model is using non-ATP, go with SOI and if it's using DOM, the BOD service would be the way to go.

References:
http://www.ibm.com/developerworks/library/specification/ws-sca/

Tuesday, June 14, 2011

Adding an external JAR file to WCS.

JAR files are used to distribute Java applications or libraries.
There is always an interesting API that is required and need to import an external JAR file.

Steps.
1. Add the jar file to the enterprise project WC/lib.
2. Make an entry for JAR file in the Stores.war\META-INF\MANIFEST.MF (the file has specific formatting with a space before and after each line and also blank line at the end.
3. Add the jar file to the WebSphereCommerceServerExtensionsData classpath. Mak sure the exported flag is true.
<classpathentry exported="true" kind="lib" path="/WC/lib/custom/recaptcha4j.jar"/>
4. Build
5. Restart

JAR basic commands:

Unzip: Jar -xvf .jar

Zip: java -jar .jar

Troubleshoot:
When you start the server, make sure the jar file is added in the system out logs:
[6/14/11 15:39:10:087 PDT] 00000019 WebApp A SRVE0180I: [InitializationServlet] [/InitializationServlet] [Servlet.LOG]: InitServlet: ServletContext attributes:
C:\WCToolkitEE60_Raj\workspace\WC\lib\custom\recaptcha4j.jar

Thursday, May 26, 2011

Impersonation ! Hiccup with WCSV7

Impersonation: In E-Comm site, this is a important flow where essentially a telesales rep or admin places an order for a regular user, the admin is essentially impersonating the user and WebSphere commerce provides out of box flows to use this behavior.

If your using SalesCenter or have your own Telesales tool customization to work with websphere commerce, you need to add a flag in wc-server.xml after V7 migration.

Until V7 by default impersonation feature was available and would work but essentially there is a flag introduce that needs to be added in wc-server.xml in the instance section during V7 migration if you are using impersonation.

<OrderLockFeature enabled="false"/>

API Background on Impersonation in WCS
getCallerId() --> returns the admin user Id when doing impersonation.
getUserid() --> Returns the userId of the user in the current session.

Important commands for Impersonation for setting user and admin for various commerce operations.
RunAsUserSetInSessionCmd and RestoreOriginalUserSetInSessionCmd

Tuesday, May 24, 2011

Magic of Servlet Filter for Mobile\ROR\Blocking requests for Performance

Java Servlet Filters are very powerful and have been around for a while and they are used for intercepting the requests and responses dynamically and can transform\modify response for different devices. The WCS framework allows programmers to extend this feature and use if solve various interesting problems.
I have to warn that servlet filter should only be used very carefully as every request/response goes through it and if not done right could impact the performance of the site.


There are 3 parts to writing a filter in commerce.
1. Defining the Filter in web.xml (Web Deployment descriptor)

<filter>
<display-name>XXBlockServletFilter</display-name>
<filter-name>XXBlockServletFilter</filter-name>
<filter-class>com.company.webcontroller.XXBlockServletFilter</filter-class>
<init-param>
<param-name>listOfParams</param-name>
<param-value>Param1,Param2,Param3</param-value>
</init-param>
</filter>

2. Where to add filter mapping in web.xml The order of filter mapping specified in the web deployment descriptor is used for filter chaining. I have worked on a couple and have them before DynaCache filter and the rational behind that is, we wanted to intercept the request before it goes into the DynaCache filter.
All requests in commerce are processed through multiple filters. It is important to understand where you want to place the filter.

3. Writing the java code for XXBlockServletFilter by overriding the doFilter method: All the processing happens here so if you are using it for Mobile or XML for any ruby on rails front end or blocking certain kinds of commands.


For More information on Commerce flow of request:
http://publib.boulder.ibm.com/infocenter/wchelp/v7r0m0/topic/com.ibm.commerce.developer.doc/concepts/csdrequestsummary.htm

Sunday, May 1, 2011

Configuration | Must know for Development


Changing the DB configuration on development instance:

setdbtype oracle C:\oracle\product\10.2.0\db_1 orcl10g system oracle devjan07 devjan07 localhost 1521

Where devjan07 is the name of the schema and the password.

When you run the command above a log is created in \logs\setdbtype.log and wc-server.xml is updated with the new database details.

Disabling Access control on development instance:

AccessControlUnitTest="true"

This property is not there by default in wc-server.xml in V6 and beyond. Add this property under <Instance xml element.

Accelerator XML configuration for instantly reflecting in Development environment: This is very useful when doing Acceleraor development.

    If you want the changes made in XML file to automatically be reflected in development environment

<ToolsGeneralConfig DTDPath="tools/common;tools/devtools;tools/bi;tools/catalog;schema/xml;sar" XMLCacheSize="1000" XMLPath="tools;tools/devtools;WEB-INF/xml/tools;WEB-INF" developmentMode="true"

    Make this change in wc-server.xml

Saturday, April 30, 2011

Punchout with Commerce systems

Punchout is a common paradigm called for sharing catalog and transfer cart at run time between different systems procurement and e-commerce systems.

Different 3rd party systems popular for PunchOut
e.g. Ariba Supplier Network, Oracle Fulfillment, SMAPS

There are 2 ways to communicate, I have used point to point and using Ariba supplier network.

cXML is used for communication. (Commerce eXtensible Markup Language) is a protocol intended for communication of business documents between e-commerce and procurement applications. It is really easy to understand and use.

Punch out on Ariba: You need to configure in the admin tool
administration->configuration->electronic order routing
catalogs->create punchout Only

Integrating WCS with various Punchout system is very interesting. Will do a another blog on the details.

Friday, April 29, 2011

Create custom Registry for Performance\Store configuration

Creating registry is a great alternate solution for things you can not cache using dynacache. Another purpose would be to store configuration data for store. If you use registry, you don't need a server restart and org admin console provides a nice interface to refresh registry. You can load the registry on server start and connect a table to the registry and if you need to add stuff dynamically, you would need a registry refresh from admin console.

Add the Registry entry into wc-server.xml

<registry
name="CustomConfigRegistry" regClassName="com.vendor.registry.CustomConfigRegistry"/>


public class CustomConfigRegistry implements Registry {
private static final String CLASSNAME = "CustomConfigRegistry";
private static CustomConfigRegistry singleton = new CustomConfigRegistry();
private Timestamp lastRefreshTimeStamp = null;
private final int INITIAL_CACHE_SIZE = 20000;

private Hashtable configCache = null;

public CustomConfigRegistry () {}

public void initialize() throws Exception {
singleton = this;
configCache = new Hashtable(INIT_CACHE_SIZE);
}


public static CustomConfigRegistry getInstance() {
return singleton;
}


public boolean enabled() {
return (singleton != null && configCache != null);
}

public void refresh() throws Exception {
if (getCacheSize() > 0) {
System.out.println(" [CustomConfigRegistry] cache size = " + getCacheSize());
}
configCache = new Hashtable(INIT_CACHE_SIZE);
lastRefreshTimeStamp = new Timestamp(System.currentTimeMillis());
PerformLogic();
}

public int getCacheSize() {
if (enabled()) {
return configCache.size();
} else {
return -1;
}
}

public void performLogic(){
}
}

Login to Admin Console->Registry->check the CustomConfigRegistry registry and update.
or
Directly refresh
http://myhostname/webapp/wcs/stores/servlet/RefreshRegistry?URL=/webapp/wcs/Admin/homepg&registryName=CustomConfigRegistry