com.konakart.actions.BaseAction.java Source code

Java tutorial

Introduction

Here is the source code for com.konakart.actions.BaseAction.java

Source

//
// (c) 2006 DS Data Systems UK Ltd, All rights reserved.
//
// DS Data Systems and KonaKart and their respective logos, are 
// trademarks of DS Data Systems UK Ltd. All rights reserved.
//
// The information in this document is free software; you can redistribute 
// it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//

package com.konakart.actions;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.SessionAware;

import com.konakart.al.KKAppEng;
import com.konakart.al.KKAppException;
import com.konakart.al.StoreInfo;
import com.konakart.app.CustomerEvent;
import com.konakart.app.KKCookie;
import com.konakart.app.KKException;
import com.konakart.appif.CustomerEventIf;
import com.konakart.appif.CustomerIf;
import com.konakart.appif.CustomerTagIf;
import com.konakart.appif.KKCookieIf;
import com.konakart.bl.ConfigConstants;
import com.konakart.servlet.AppEngServlet;
import com.opensymphony.xwork2.ActionSupport;

/**
 * Base Action for KonaKart application. All of the other action classes extend this class.
 */
public class BaseAction extends ActionSupport implements ServletRequestAware, SessionAware {
    private static final long serialVersionUID = 1L;

    /**
     * The <code>Log</code> instance for this application.
     */
    protected Log log = LogFactory.getLog(BaseAction.class);

    protected static final String KKLOGIN = "Login";

    protected static final String WELCOME = "Welcome";

    protected static final String GUEST_CUSTOMER_ID = "GUEST_CUSTOMER_ID";

    protected static final String CUSTOMER_LOCALE = "CUSTOMER_LOCALE";

    protected static final String CUSTOMER_NAME = "CUSTOMER_NAME";

    protected static final String CUSTOMER_UUID = "CUSTOMER_UUID";

    protected static final int COOKIE_MAX_AGE_IN_SECS = 365 * 24 * 60 * 60;

    /*
     * Event actions
     */
    protected static final int ACTION_NEW_CUSTOMER_VISIT = 1;

    protected static final int ACTION_CUSTOMER_LOGIN = 2;

    protected static final int ACTION_ENTER_CHECKOUT = 3;

    protected static final int ACTION_CONFIRM_ORDER = 4;

    protected static final int ACTION_PAYMENT_METHOD_SELECTED = 5;

    protected static final int ACTION_REMOVE_FROM_CART = 6;

    protected static final int ACTION_PRODUCT_VIEWED = 7;

    /*
     * Customer tags
     */
    protected static final String TAG_PRODUCTS_VIEWED = "PRODUCTS_VIEWED";

    protected static final String TAG_CATEGORIES_VIEWED = "CATEGORIES_VIEWED";

    protected static final String TAG_MANUFACTURERS_VIEWED = "MANUFACTURERS_VIEWED";

    protected static final String TAG_SEARCH_STRING = "SEARCH_STRING";

    protected static final String TAG_COUNTRY_CODE = "COUNTRY_CODE";

    protected static final String TAG_BIRTH_DATE = "BIRTH_DATE";

    protected static final String TAG_LOGIN_DATE = "LOGIN_DATE";

    protected static final String TAG_IS_MALE = "IS_MALE";

    protected static final String TAG_PROD_PAGE_SIZE = "PROD_PAGE_SIZE";

    protected static final String TAG_ORDER_PAGE_SIZE = "ORDER_PAGE_SIZE";

    protected static final String TAG_REVIEW_PAGE_SIZE = "REVIEW_PAGE_SIZE";

    /*
     * Constants for SEO
     */
    protected static final String SEO_DELIM = "_";

    protected static final String SEO_TYPE = ".action";

    protected static final int SEO_TYPE_LENGTH = SEO_TYPE.length();

    protected static final int SEO_SEL_CAT_CODE = 1;

    protected static final String SEO_SEL_CAT = "SelectCat";

    protected static final int SEO_SEL_PROD_CODE = 2;

    protected static final String SEO_SEL_PROD = "SelectProd";

    protected static final int SEO_SEARCH_BY_MANU_BY_LINK_CODE = 3;

    protected static final String SEO_SEARCH_BY_MANU_BY_LINK = "ShowSearchByManufacturerResultsByLink";

    protected static final int SEO_PRODS_FOR_MANU_CODE = 5;

    protected static final String SEO_PRODS_FOR_MANU = "ShowProductsForManufacturer";

    protected static final int SEO_OFF = 0;

    protected static final int SEO_PARAMETERS = 1;

    protected static final int SEO_DIRECTORY = 2;

    /*
     * Following tags declared public because used in managers
     */
    /** Customer tag */
    public static final String TAG_PRODUCTS_IN_CART = "PRODUCTS_IN_CART";

    /** Customer tag */
    public static final String TAG_PRODUCTS_IN_WISHLIST = "PRODUCTS_IN_WISHLIST";

    /** Customer tag */
    public static final String TAG_CART_TOTAL = "CART_TOTAL";

    /** Customer tag */
    public static final String TAG_WISHLIST_TOTAL = "WISHLIST_TOTAL";

    /** Struts session */
    public Map<String, Object> strutsSession;

    /** Copy of the KKAppEng */
    private static KKAppEng appEng;

    /**
     * Sets the variable kkAppEng to the KKAppEng instance saved in the session. If cannot be found,
     * then it is instantiated and attached.
     * 
     * @param request
     * @param response
     * @return Returns a KonaKart client engine instance
     * @throws KKException
     * @throws KKAppException
     */
    protected KKAppEng getKKAppEng(HttpServletRequest request, HttpServletResponse response)
            throws KKAppException, KKException {
        HttpSession session = request.getSession();
        KKAppEng kkAppEng = (KKAppEng) session.getAttribute(KKAppEng.KONAKART_KEY);
        if (kkAppEng == null) {
            if (log.isInfoEnabled()) {
                log.info("KKAppEng not found on the session");
            }

            String storeIdForNewEngine = getStoreIdFromRequest(request);

            StoreInfo si = new StoreInfo();
            si.setStoreId(storeIdForNewEngine);
            kkAppEng = new KKAppEng(si, session);

            if (log.isInfoEnabled()) {
                log.info("Set KKAppEng on the session for storeId " + si.getStoreId());
            }

            /*
             * Add KKAppEng to the struts and the HTTP sessions. In order for the struts jsp tags to
             * see it, it has to be added to the struts session as well.
             */
            session.setAttribute(KKAppEng.KONAKART_KEY, kkAppEng);
            strutsSession.put(KKAppEng.KONAKART_KEY, kkAppEng);

            String customerUuid = manageCookies(request, response, kkAppEng);
            if (customerUuid != null) {
                // Get the locale from the cookie
                String savedLocale = getKKCookie(customerUuid, CUSTOMER_LOCALE, kkAppEng);
                if (savedLocale != null) {
                    // Set the engine with the new locale
                    kkAppEng.setLocale(savedLocale);
                }
            }

            // Insert event
            insertCustomerEvent(kkAppEng, ACTION_NEW_CUSTOMER_VISIT);
        }

        kkAppEng.setPageTitle(kkAppEng.getMsg("seo.default.title"));
        kkAppEng.setMetaDescription(kkAppEng.getMsg("seo.default.meta.description"));
        kkAppEng.setMetaKeywords(kkAppEng.getMsg("seo.default.meta.keywords"));

        /* Save a copy in the base action */
        appEng = kkAppEng;

        return kkAppEng;
    }

    /**
     * In a multi-store scenario, you should implement your own logic here to extract the storeId
     * from the request and return it.
     * 
     * @param request
     * @return Returns the storeId
     */
    private String getStoreIdFromRequest(HttpServletRequest request) {
        /*
         * If the server name could contain store1.localhost or store2.localhost which both point to
         * localhost, we could get the store id from the server name.
         */
        /*
         * if (request != null && request.getServerName() != null) { String[] nameArray =
         * request.getServerName().split("\\."); if (nameArray.length > 1) { String storeId =
         * nameArray[0]; return storeId; } }
         */

        return AppEngServlet.getDefaultStoreId();
    }

    /**
     * A common method that contains the code to deal with exceptions
     * 
     * @param request
     * @param e
     * @return Returns a string
     */
    protected String handleException(HttpServletRequest request, Exception e) {

        if (e != null && e.getClass().getName().equals("com.konakart.app.KKException")) {
            KKException ex = (KKException) e;

            switch (ex.getCode()) {
            case KKException.KK_STORE_DELETED:
                if (appEng != null) {
                    addActionError(appEng.getMsg("unavailable.deleted"));
                }
                return new String("Unavailable");
            case KKException.KK_STORE_DISABLED:
                if (appEng != null) {
                    addActionError(appEng.getMsg("unavailable.disabled"));
                }
                return new String("Unavailable");
            case KKException.KK_STORE_UNDER_MAINTENANCE:
                if (appEng != null) {
                    addActionError(appEng.getMsg("unavailable.maintenance"));
                }
                return new String("Unavailable");
            }
        }

        String customerExceptionMessage = this.getExceptionMessage(e, /* full */false);
        log.error("A customer has received the following exception message:"
                + customerExceptionMessage.replace("<br>", " : ") + "<br>The full exception is:", e);

        if (appEng != null) {
            addActionError(appEng.getMsg("exception.detailed.message", customerExceptionMessage));
        }
        return new String("Exception");
    }

    /**
     * Returns a string containing the stack trace of the exception and its cause
     * 
     * @param e
     * @return Returns a string containing the stack trace
     */
    private String getExceptionMessage(Exception e, boolean full) {
        StringBuffer sb = new StringBuffer();

        // Get the stack trace of the exception
        sb.append("<br>Exception Name = ");
        sb.append(e.getClass().getName());
        sb.append("<br>Exception Message = ");
        String msg = e.getMessage();
        if (msg == null || msg.equalsIgnoreCase("null")) {
            msg = "No further details available";
        } else {
            msg = StringEscapeUtils.escapeHtml(msg);
        }
        sb.append(msg);

        if (log.isDebugEnabled() || full) {
            sb.append("<br>Exception Stack Trace = ");
            StackTraceElement[] ste = e.getStackTrace();
            for (int i = 0; i < ste.length; i++) {
                sb.append("<br>   at ");
                sb.append(ste[i].toString());
            }

            // Get the stack trace of the exception cause
            Throwable eCause = e.getCause();
            if (eCause != null) {
                sb.append("<br><br>Exception Cause = ");
                sb.append(eCause.getClass().getName());
                StackTraceElement[] ste1 = eCause.getStackTrace();
                for (int i = 0; i < ste1.length; i++) {
                    sb.append("<br>   at ");
                    sb.append(ste1[i].toString());
                }
            }
        }

        return sb.toString();
    }

    /**
     * Checks to see whether we are logged in.
     * 
     * @param kkAppEng
     *            The KonaKart client engine instance
     * @param forwardAfterLogin
     *            tells us which page to forward to after login.
     * @param request
     * @param response
     * @return Returns the CustomerId if logged in. Otherwise a negative number.
     * @throws KKException
     * @throws KKAppException
     */
    protected int loggedIn(HttpServletRequest request, HttpServletResponse response, KKAppEng kkAppEng,
            String forwardAfterLogin) throws KKException, KKAppException {
        // If the session is null, set the forward and return a negative number.
        if ((kkAppEng.getSessionId() == null)) {
            if (forwardAfterLogin != null) {
                kkAppEng.setForwardAfterLogin(forwardAfterLogin);
            }
            return -1;
        }

        // If an exception is thrown, set the forward and return it
        int custId;
        try {
            custId = kkAppEng.getEng().checkSession(kkAppEng.getSessionId());
        } catch (KKException e) {
            log.debug(e.getMessage());
            if (forwardAfterLogin != null) {
                kkAppEng.setForwardAfterLogin(forwardAfterLogin);
            }

            kkAppEng.getCustomerMgr().logout();

            // Ensure that the guest customer is the one in the cookie
            manageCookieLogout(request, response, kkAppEng);

            return -1;
        }
        // At this point we return a valid customer Id
        return custId;
    }

    /**
     * If the application sends us a KKException with the cause equal to a more detailed exception
     * such as KKPasswordDoesntMatchException, sometimes the application has to be able to detect
     * this so that it can handle the exception by sending an error message back to the screen.
     * 
     * @param request
     * @param e
     *            The exception that has been captured
     * @param cause
     *            The name of the cause Exception to look for i.e. KKPasswordDoesntMatchException
     * @param message
     *            The message to send out to the application
     * @param forward
     *            The forward if we detect the cause
     * @return ActionForward
     */
    protected String getForward(HttpServletRequest request, Exception e, String cause, String message,
            String forward) {

        if (e.getCause() != null && e.getCause().getClass().getName().equals(cause)) {
            addActionError(message);
            return forward;
        }

        // For the web services case the cause turns out to be an org.apache.axis.AxisFault.
        // However within the message there is a message that says e.g. nested exception is:
        // com.konakart.app.KKPasswordDoesntMatchException so we search for the name of the
        // exception class that is the cause
        if (e.getMessage() != null && e.getMessage().indexOf(cause) > -1) {
            addActionError(message);
            return forward;
        }

        return handleException(request, e);
    }

    /**
     * Determines whether we are using SSL or not. If we are logged in, then we should be using SSL.
     * SSL can also be forced by setting the forceSSL boolean. If we should be using SSL but aren't
     * (or vice versa) we do a redirect by returning an action forward with the correct URL to
     * redirect to. Otherwise we return null
     * 
     * @param request
     * @param custId
     *            The customer id
     * @param forceSSL
     *            Set to true if we should force SSL.
     * @return ActionForward
     * @throws KKException
     */
    protected String checkSSL(KKAppEng eng, HttpServletRequest request, int custId, boolean forceSSL)
            throws KKException {
        try {
            if (eng == null) {
                throw new KKException("checkSSL called with KKAppEng set to null");
            }
            String sslPort = eng.getSslPort();
            String standardPort = eng.getStandardPort();
            boolean activateCheck = eng.isEnableSSL();
            String sslBaseUrl = eng.getSslBaseUrl();

            if (activateCheck && request != null) {
                boolean isSSL = false;
                StringBuffer redirectUrl;

                if (request.getRequestURL() == null) {
                    throw new KKException(
                            "Cannot determine whether SSL is being used because the method request.getRequestURL() returns null");
                }

                if (request.getRequestURL().substring(0, 5).equalsIgnoreCase("https")) {
                    isSSL = true;
                }

                if (log.isDebugEnabled()) {
                    log.debug("getServerName = " + request.getServerName());
                    log.debug("getServerPort = " + request.getServerPort());
                    log.debug("getServletPath = " + request.getServletPath());
                    log.debug("getRequestURI = " + request.getRequestURI());
                    log.debug("getRequestURL = " + request.getRequestURL());
                    log.debug("isSSL = " + isSSL);
                    log.debug("custId = " + custId);
                }

                if (!isSSL && (custId > -1 || forceSSL)) {
                    // We aren't using SSL but should be
                    redirectUrl = new StringBuffer();
                    if (sslBaseUrl != null) {
                        redirectUrl.append(sslBaseUrl);
                        redirectUrl.append(request.getRequestURI());
                    } else {
                        redirectUrl.append("https://");
                        redirectUrl.append(request.getServerName());
                        // Insert the port if it is non standard
                        if (sslPort != null && !sslPort.equals("443")) {
                            redirectUrl.append(":");
                            redirectUrl.append(sslPort);
                        }
                        redirectUrl.append(request.getRequestURI());
                    }

                    /*
                     * The following is called for security reasons. In some cases (such as when
                     * using Tomcat) the session id is appended to the URL in the browser (i.e.
                     * jsessionid=E2D1B0B2B8C5478B7F6F3C3C5D9BB0FB). If a hacker managed to get this
                     * session id while the customer wasn't logged in, he could use it to access
                     * sensitive information once the customer has logged in since the session id
                     * doesn't change. The following method creates a new session and substitutes
                     * it.
                     */
                    changeSession(request);
                } else if (isSSL && (custId < 0) && !forceSSL) {
                    // We are using SSL but shouldn't be
                    redirectUrl = new StringBuffer();
                    redirectUrl.append("http://");
                    redirectUrl.append(request.getServerName());
                    // Insert the port if it is non standard
                    if (standardPort != null && !standardPort.equals("80")) {
                        redirectUrl.append(":");
                        redirectUrl.append(standardPort);
                    }
                    redirectUrl.append(request.getRequestURI());
                } else {
                    // Don't need to do anything
                    return null;
                }

                // Get the parameters
                StringBuffer parms = new StringBuffer();
                Enumeration<String> en = request.getParameterNames();
                while (en.hasMoreElements()) {
                    String paramName = en.nextElement();
                    String paramValue = request.getParameter(paramName);
                    if (parms.length() > 0) {
                        parms.append("&");
                    } else {
                        parms.append("?");
                    }
                    parms.append(paramName);
                    parms.append("=");
                    parms.append(paramValue);
                }

                // Append the parameters to the redirect url
                redirectUrl.append(parms);
                if (log.isDebugEnabled()) {
                    log.debug("redirectUrl = " + redirectUrl);
                }
                return redirectUrl.toString();
            }

            return null;
        } catch (Exception e) {
            log.error(e);
            return null;
        }
    }

    /**
     * Returns true if we are running in a portal
     * 
     * @param kkAppEng
     * @return Returns true if we are running in a portal
     */
    protected boolean runningInPortal(KKAppEng kkAppEng) {
        if (log.isDebugEnabled()) {
            if (kkAppEng.isPortlet()) {
                log.debug("Running in portal");
            }
        }
        return kkAppEng.isPortlet();
    }

    /**
     * Returns true if configured for one page checkout
     * 
     * @param kkAppEng
     * @return Returns true if configured for one page checkout
     */
    protected boolean isOnePageCheckout(KKAppEng kkAppEng) {
        boolean ret = false;

        // Check to see whether we are running as a portlet
        boolean portlet = runningInPortal(kkAppEng);

        // Don't allow one page checkout if running as a portlet
        if (portlet) {
            return ret;
        }

        // Check to see whether one page checkout is configured
        String onePageCheckout = kkAppEng.getConfig(ConfigConstants.ONE_PAGE_CHECKOUT);
        if (onePageCheckout != null && onePageCheckout.equalsIgnoreCase("true")) {
            ret = true;
        }

        return ret;
    }

    /**
     * Method used to create a browser cookie when a customer first accesses the application. If the
     * cookie already exists then we retrieve the guest customer id from the cookie which will be
     * used to retrieve and cart items that the customer added to the cart on his last visit.
     * 
     * @param request
     * @param response
     * @param kkAppEng
     * @return Returns the Customer UUID
     * @throws KKException
     * @throws KKAppException
     */
    private String manageCookies(HttpServletRequest request, HttpServletResponse response, KKAppEng kkAppEng)
            throws KKException, KKAppException {
        if (!kkAppEng.isKkCookieEnabled()) {
            return null;
        }

        /*
         * The current customer should at this point be a guest customer with a negative customer id
         */
        CustomerIf currentCustomer = kkAppEng.getCustomerMgr().getCurrentCustomer();
        if (currentCustomer == null) {
            log.warn("Current customer is set to null in the manageCookies method. This should never happen");
            return null;
        }

        /*
         * Get the customerUuid from the browser cookie. A new cookie is created if it doesn't exist
         */
        String customerUuid = getCustomerUuidFromBrowserCookie(request, response);

        /*
         * Get the guestCustomerId from the KK database.
         */
        String guestCustomerIdStr = getKKCookie(customerUuid, GUEST_CUSTOMER_ID, kkAppEng);

        if (guestCustomerIdStr == null) {
            /*
             * If it doesn't exist, then we create it
             */
            setKKCookie(customerUuid, GUEST_CUSTOMER_ID, Integer.toString(currentCustomer.getId()), kkAppEng);

        } else {
            /*
             * Set the current customer id with the one retrieved from the cookie and fetch any cart
             * items that he may have.
             */
            currentCustomer.setId(Integer.parseInt(guestCustomerIdStr));
            kkAppEng.getBasketMgr().getBasketItemsPerCustomer();
            if (kkAppEng.getWishListMgr().allowWishListWhenNotLoggedIn()) {
                kkAppEng.getWishListMgr().fetchCustomersWishLists();
            }

            // Get the product page size
            String prodPageSizeStr = getKKCookie(customerUuid, TAG_PROD_PAGE_SIZE, kkAppEng);
            if (prodPageSizeStr != null && prodPageSizeStr.length() > 0) {
                try {
                    int prodPageSize = Integer.parseInt(prodPageSizeStr);
                    kkAppEng.getProductMgr().setMaxDisplaySearchResults(prodPageSize);
                } catch (NumberFormatException e) {
                    log.warn("The product page size value stored in the cookie for customer with guest id "
                            + guestCustomerIdStr + " is not a numeric value: " + prodPageSizeStr);
                }
            }

            // Get the order page size
            String orderPageSizeStr = getKKCookie(customerUuid, TAG_ORDER_PAGE_SIZE, kkAppEng);
            if (orderPageSizeStr != null && orderPageSizeStr.length() > 0) {
                try {
                    int orderPageSize = Integer.parseInt(orderPageSizeStr);
                    kkAppEng.getOrderMgr().setPageSize(orderPageSize);
                } catch (NumberFormatException e) {
                    log.warn("The order page size value stored in the cookie for customer with guest id "
                            + guestCustomerIdStr + " is not a numeric value: " + orderPageSizeStr);
                }
            }

            // Get the review page size
            String reviewPageSizeStr = getKKCookie(customerUuid, TAG_REVIEW_PAGE_SIZE, kkAppEng);
            if (reviewPageSizeStr != null && reviewPageSizeStr.length() > 0) {
                try {
                    int reviewPageSize = Integer.parseInt(reviewPageSizeStr);
                    kkAppEng.getReviewMgr().setPageSize(reviewPageSize);
                } catch (NumberFormatException e) {
                    log.warn("The review page size value stored in the cookie for customer with guest id "
                            + guestCustomerIdStr + " is not a numeric value: " + reviewPageSizeStr);
                }
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("GUEST_CUSTOMER_ID cookie value = " + getKKCookie(customerUuid, GUEST_CUSTOMER_ID, kkAppEng));
            log.debug("CUSTOMER_NAME cookie value = " + getKKCookie(customerUuid, CUSTOMER_NAME, kkAppEng));
            log.debug("CUSTOMER_LOCALE cookie value = " + getKKCookie(customerUuid, CUSTOMER_LOCALE, kkAppEng));
            log.debug("PROD_PAGE_SIZE cookie value = " + getKKCookie(customerUuid, TAG_PROD_PAGE_SIZE, kkAppEng));
            log.debug("ORDER_PAGE_SIZE cookie value = " + getKKCookie(customerUuid, TAG_ORDER_PAGE_SIZE, kkAppEng));
            log.debug(
                    "REVIEW_PAGE_SIZE cookie value = " + getKKCookie(customerUuid, TAG_REVIEW_PAGE_SIZE, kkAppEng));
        }

        /*
         * Call class where you can place custom code
         */
        CustomCookieAction cca = new CustomCookieAction();
        cca.manageCookiesOnEntry(request, response, kkAppEng);

        return customerUuid;

    }

    /**
     * Utility method that can be used to set a KKCookie. It attempts to get the UUID from the
     * browser cookie and creates a new browser cookie if it doesn't find one.
     * 
     * @param attrId
     * @param attrValue
     * @param request
     * @param response
     * @param kkAppEng
     * @throws KKException
     */
    protected void setKKCookie(String attrId, String attrValue, HttpServletRequest request,
            HttpServletResponse response, KKAppEng kkAppEng) throws KKException {
        /*
         * Get the CustomerUuid from the browser cookie and create the cookie if it doesn't exist.
         */
        String uuid = getCustomerUuidFromBrowserCookie(request, response);

        /*
         * Now we can save the KKCookie
         */
        setKKCookie(uuid, attrId, attrValue, kkAppEng);

    }

    /**
     * Utility method to set a KKCookie when we have the customerUuid
     * 
     * @param customerUuid
     * @param attrId
     * @param attrValue
     * @param kkAppEng
     * @throws KKException
     */
    protected void setKKCookie(String customerUuid, String attrId, String attrValue, KKAppEng kkAppEng)
            throws KKException {
        KKCookieIf kkCookie = new KKCookie();
        kkCookie.setCustomerUuid(customerUuid);
        kkCookie.setAttributeId(attrId);
        kkCookie.setAttributeValue(attrValue);
        kkAppEng.getEng().setCookie(kkCookie);
    }

    /**
     * Utility method to read a KKCookie. It attempts to get the UUID from the browser cookie and
     * creates a new browser cookie if it doesn't find one.
     * 
     * @param attrId
     * @param request
     * @param response
     * @param kkAppEng
     * @return the value of the cookie
     * @throws KKException
     */
    protected String getKKCookie(String attrId, HttpServletRequest request, HttpServletResponse response,
            KKAppEng kkAppEng) throws KKException {
        /*
         * Get the CustomerUuid from the browser cookie and create the cookie if it doesn't exist.
         */
        String uuid = getCustomerUuidFromBrowserCookie(request, response);

        /*
         * Now get the KKCookie
         */
        return getKKCookie(uuid, attrId, kkAppEng);
    }

    /**
     * Utility method to read a KKCookie when we have the CustomerUuid
     * 
     * @param customerUuid
     * @param attrId
     * @param kkAppEng
     * @return the value of the cookie
     * @throws KKException
     */
    protected String getKKCookie(String customerUuid, String attrId, KKAppEng kkAppEng) throws KKException {
        KKCookieIf kkCookie = kkAppEng.getEng().getCookie(customerUuid, attrId);
        if (kkCookie != null) {
            return kkCookie.getAttributeValue();
        }
        return null;
    }

    /**
     * Utility method to get the CustomerUuid from the browser cookie and create the cookie if it
     * doesn't exist.
     * 
     * @param request
     * @return Returns the CustomerUuid
     */
    private String getCustomerUuidFromBrowserCookie(HttpServletRequest request, HttpServletResponse response) {
        /*
         * Try to find the cookie we are looking for
         */
        Cookie[] cookies = request.getCookies();
        String uuid = null;
        if (cookies != null) {
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                String cookieName = cookie.getName();
                if (cookieName.equals(CUSTOMER_UUID)) {
                    /*
                     * If we find the cookie we get the value and update the max age.
                     */
                    uuid = cookie.getValue();
                    cookie.setMaxAge(COOKIE_MAX_AGE_IN_SECS);
                    cookie.setPath("/");
                    response.addCookie(cookie);
                }
            }
        }

        /*
         * If the browser cookie doesn't exist then we have to create it and store a newly created
         * UUID string
         */
        if (uuid == null) {
            UUID uuidObject = UUID.randomUUID();
            uuid = uuidObject.toString();
            /*
             * Create a browser cookie with the UUID
             */
            Cookie uuidCookie = new Cookie(CUSTOMER_UUID, uuid);
            uuidCookie.setMaxAge(COOKIE_MAX_AGE_IN_SECS);
            uuidCookie.setPath("/");
            response.addCookie(uuidCookie);
        }

        return uuid;
    }

    /**
     * When we log out, ensure that the new guest customer that is created has the id saved in the
     * browser cookie.
     * 
     * @param request
     * @param response
     * @param kkAppEng
     * @throws KKException
     * @throws KKAppException
     */
    protected void manageCookieLogout(HttpServletRequest request, HttpServletResponse response, KKAppEng kkAppEng)
            throws KKException, KKAppException {
        if (!kkAppEng.isKkCookieEnabled()) {
            return;
        }

        CustomerIf currentCustomer = kkAppEng.getCustomerMgr().getCurrentCustomer();
        if (currentCustomer != null) {
            String guestCustomerIdStr = getKKCookie(GUEST_CUSTOMER_ID, request, response, kkAppEng);
            // Only get the basket items if we can retrieve a temporary customer from the cookie
            if (guestCustomerIdStr != null) {
                try {
                    currentCustomer.setId(Integer.parseInt(guestCustomerIdStr));
                    kkAppEng.getBasketMgr().getBasketItemsPerCustomer();
                } catch (NumberFormatException e) {

                }
            }
        }

        /*
         * Call class where you can place custom code
         */
        CustomCookieAction cca = new CustomCookieAction();
        cca.manageCookiesAfterLogout(request, response, kkAppEng);
    }

    /**
     * Returns a customer event object with the action and customer id attributes populated. If
     * events aren't enabled, then null is returned.
     * 
     * @param kkAppEng
     *            App eng instance
     * @param action
     *            Event action
     * @return Returns a customer event object or null if events aren't enabled
     */
    protected CustomerEventIf getCustomerEvent(KKAppEng kkAppEng, int action) {
        String enabled = kkAppEng.getConfig(ConfigConstants.ENABLE_CUSTOMER_EVENTS);
        if (enabled != null && enabled.equalsIgnoreCase("true")) {
            CustomerEventIf event = new CustomerEvent();
            event.setAction(action);
            CustomerIf currentCust = kkAppEng.getCustomerMgr().getCurrentCustomer();
            if (currentCust != null) {
                event.setCustomerId(currentCust.getId());
            }
            return event;
        }
        return null;
    }

    /**
     * Inserts a customer event where all of the available parameters are passed
     * 
     * @param kkAppEng
     *            App eng instance
     * @param action
     *            Event action
     * @param str1
     * @param str2
     * @param int1
     * @param int2
     * @param dec1
     * @param dec2
     * @throws KKException
     */
    protected void insertCustomerEvent(KKAppEng kkAppEng, int action, String str1, String str2, int int1, int int2,
            BigDecimal dec1, BigDecimal dec2) throws KKException {
        CustomerEventIf event = getCustomerEvent(kkAppEng, action);
        if (event != null) {
            event.setData1Str(str1);
            event.setData2Str(str2);
            event.setData1Int(int1);
            event.setData2Int(int2);
            event.setData1Dec(dec1);
            event.setData2Dec(dec2);
            kkAppEng.getEng().insertCustomerEvent(event);
        }
    }

    /**
     * Shortcut method for inserting a customer event passing no custom event data
     * 
     * @param kkAppEng
     * @param action
     * @throws KKException
     */
    protected void insertCustomerEvent(KKAppEng kkAppEng, int action) throws KKException {
        insertCustomerEvent(kkAppEng, action, null, null, 0, 0, null, null);
    }

    /**
     * Shortcut method for inserting a customer event passing an integer as event data
     * 
     * @param kkAppEng
     * @param action
     * @param int1
     * @throws KKException
     */
    protected void insertCustomerEvent(KKAppEng kkAppEng, int action, int int1) throws KKException {
        insertCustomerEvent(kkAppEng, action, null, null, int1, 0, null, null);
    }

    /**
     * Shortcut method for inserting a customer event passing a string as event data
     * 
     * @param kkAppEng
     * @param action
     * @param str1
     * @throws KKException
     */
    protected void insertCustomerEvent(KKAppEng kkAppEng, int action, String str1) throws KKException {
        insertCustomerEvent(kkAppEng, action, str1, null, 0, 0, null, null);
    }

    /**
     * Shortcut method for inserting a customer event passing a decimal as event data
     * 
     * @param kkAppEng
     * @param action
     * @param dec1
     * @throws KKException
     */
    protected void insertCustomerEvent(KKAppEng kkAppEng, int action, BigDecimal dec1) throws KKException {
        insertCustomerEvent(kkAppEng, action, null, null, 0, 0, dec1, null);
    }

    /**
     * Method called when a customer logs in or logs out. When logging in we need to decide whether
     * to update the customer's PRODUCTS_VIEWED tag value from the value of the guest customer's
     * tag. When logging out we need to make the same decision in the opposite direction. We only do
     * the updates if the tag value of the "oldTag" is more recent than the tag value of the
     * "newTag".
     * 
     * @param oldTag
     *            When logging in, it is the tag of the guest customer. When logging out, it is the
     *            tag of the logged in customer.
     * @param newTag
     *            When logging in, it is the tag of the logged in customer. When logging out, it is
     *            the tag of the guest customer.
     * @throws KKException
     * @throws KKAppException
     */
    protected void updateRecentlyViewedProducts(KKAppEng kkAppEng, CustomerTagIf oldTag, CustomerTagIf newTag)
            throws KKAppException, KKException {
        if (oldTag != null && oldTag.getDateAdded() != null && oldTag.getValue() != null
                && oldTag.getValue().length() > 0) {
            if (newTag == null || newTag.getDateAdded() == null
                    || newTag.getDateAdded().before(oldTag.getDateAdded())) {
                /*
                 * If new tag doesn't exist or old tag is newer than new tag, then give newTag the
                 * value of old tag
                 */
                kkAppEng.getCustomerTagMgr().insertCustomerTag(TAG_PRODUCTS_VIEWED, oldTag.getValue());
            }
        }
    }

    /**
     * Creates a new session when we switch to SSL to avoid hackers using the current session id
     * (which may have been visible on the URL) to log into the application
     * 
     * @param request
     *            HttpServletRequest
     */

    private void changeSession(HttpServletRequest request) {
        /* Used to temporarily store objects from current session */
        HashMap<String, Object> currentSessionMap = new HashMap<String, Object>();

        /* Loop through all objects saved in the current session and place them in the hash map */
        HttpSession currentSession = request.getSession();

        Enumeration<String> atrNameEnum = currentSession.getAttributeNames();
        while (atrNameEnum.hasMoreElements()) {
            String attrName = atrNameEnum.nextElement();
            currentSessionMap.put(attrName, currentSession.getAttribute(attrName));
        }

        /* Invalidate the current session */
        currentSession.invalidate();
        currentSession = null;

        /* Create a new session */
        HttpSession newSession = request.getSession(true);

        /* Load the new session with objects saved in the hash map */
        Set<String> atrNameSet = currentSessionMap.keySet();
        for (String attrName : atrNameSet) {
            newSession.setAttribute(attrName, currentSessionMap.get(attrName));
        }

        return;
    }

    /**
     * Common code for setting up the redirect response. A few headers are set to stop the browser
     * from caching the permanent redirect.
     * 
     * @param response
     */
    protected void setupResponseForSEORedirect(HttpServletResponse response) {
        response.setStatus(301);
        response.setHeader("Expires", "Wed, 11 Jan 1984 05:00:00 GMT");
        response.setHeader("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Connection", "close");
    }

    /**
     * Common code for setting up the redirect response when switching from HTTP to HTTPS and vice
     * versa
     * 
     * @param response
     */
    protected void setupResponseForSSLRedirect(HttpServletResponse response, String redirForward) {
        response.setStatus(302);
        response.setHeader("Location", redirForward);
    }

    /**
     * A URL encoder which converts spaces to "-" and doesn't encode forward or back slashes since
     * many app servers don't accept these because of security issues.
     * 
     * @param in
     * @return Returns an encoded string
     * @throws UnsupportedEncodingException
     */
    protected String kkURLEncode(String in) throws UnsupportedEncodingException {
        if (in == null) {
            return "";
        }
        String inp = in.replace(" ", "-");
        String encoded = URLEncoder.encode(inp, "UTF-8");

        // Remove encoded forward slash
        encoded = encoded.replace("%2F", "-");

        // Remove encoded backwards slash
        encoded = encoded.replace("%5C", "-");

        return encoded;
    }

    /**
     * Required for implementing ServletRequestAware interface
     */
    public void setServletRequest(HttpServletRequest arg0) {

    }

    /**
     * Required for implementing SessionAware interface
     */
    public void setSession(Map<String, Object> session) {
        this.strutsSession = session;
    }

    /**
     * Utility method called from JSPs to return an empty string when the value of the input string
     * is null.
     * 
     * @param in
     * @return Returns an empty string if the input string is null
     */
    public String noNull(String in) {
        return (in == null) ? "" : in;
    }

    /**
     * Returns the base redirect URL used in AJAX calls
     * 
     * @param request
     *            The HttpServletRequest
     * 
     * @return Returns the base redirect URL used in AJAX calls
     */
    public String getRedirectURL(HttpServletRequest request) {
        String redirectURL = request.getRequestURI().toString();
        String servletPath = request.getServletPath();
        redirectURL = redirectURL.substring(0, redirectURL.length() - servletPath.length());
        return redirectURL;
    }

}