svn commit: r1535171 [2/4] - in /ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23: ./ applications/product/ applications/product/config/ applications/product/dtd/ applications/product/src/org/ofbiz/product/category/ applications/product/src/org...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1535171 [2/4] - in /ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23: ./ applications/product/ applications/product/config/ applications/product/dtd/ applications/product/src/org/ofbiz/product/category/ applications/product/src/org...

jleroux@apache.org
Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,498 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.product.category;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.ParserConfigurationException;
+
+import javolution.util.FastList;
+import javolution.util.FastMap;
+
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilURL;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.UtilXml;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * SeoConfigUtil - SEO Configuration file utility.
+ *
+ */
+public class SeoConfigUtil {
+
+    private static final String module = SeoConfigUtil.class.getName();
+    private static Perl5Compiler perlCompiler = new Perl5Compiler();
+    private static boolean isInitialed = false;
+    private static boolean debug = false;
+    private static boolean categoryUrlEnabled = true;
+    private static boolean categoryNameEnabled = false;
+    private static String categoryUrlSuffix = null;
+    public static final String DEFAULT_REGEXP = "^.*/.*$";
+    private static Pattern regexpIfMatch = null;
+    private static boolean useUrlRegexp = false;
+    private static boolean jSessionIdAnonEnabled = false;
+    private static boolean jSessionIdUserEnabled = false;
+    private static Map<String, String> seoReplacements = null;
+    private static Map<String, Pattern> seoPatterns = null;
+    private static Map<String, String> forwardReplacements = null;
+    private static Map<String, Pattern> forwardPatterns = null;
+    private static Map<String, Integer> forwardResponseCodes = null;
+    private static Map<String, String> nameFilters = null;
+    private static List<Pattern> userExceptionPatterns = null;
+    private static Set<String> allowedContextPaths = null;
+    private static Map<String, String> specialProductIds = null;
+    public static final String ELEMENT_REGEXPIFMATCH = "regexpifmatch";
+    public static final String ELEMENT_DEBUG = "debug";
+    public static final String ELEMENT_CONFIG = "config";
+    public static final String ELEMENT_DESCRIPTION = "description";
+    public static final String ELEMENT_FORWARD = "forward";
+    public static final String ELEMENT_SEO = "seo";
+    public static final String ELEMENT_URLPATTERN = "url-pattern";
+    public static final String ELEMENT_REPLACEMENT = "replacement";
+    public static final String ELEMENT_RESPONSECODE = "responsecode";
+    public static final String ELEMENT_JSESSIONID = "jsessionid";
+    public static final String ELEMENT_ANONYMOUS = "anonymous";
+    public static final String ELEMENT_VALUE = "value";
+    public static final String ELEMENT_USER = "user";
+    public static final String ELEMENT_EXCEPTIONS = "exceptions";
+    public static final String ELEMENT_NAME_FILTERS = "name-filters";
+    public static final String ELEMENT_FILTER = "filter";
+    public static final String ELEMENT_CHARACTER_PATTERN = "character-pattern";
+    public static final String SEO_CONFIG_FILENAME = "SeoConfig.xml";
+    public static final int DEFAULT_RESPONSECODE = HttpServletResponse.SC_MOVED_PERMANENTLY;
+    public static final String DEFAULT_ANONYMOUS_VALUE = "disable";
+    public static final String DEFAULT_USER_VALUE = "disable";
+    public static final String DEFAULT_CATEGORY_URL_VALUE = "enable";
+    public static final String DEFAULT_CATEGORY_NAME_VALUE = "disable";
+    public static final String ALLOWED_CONTEXT_PATHS_SEPERATOR = ":";
+    /**
+     * Initialize url regular express configuration.
+     *
+     * @return result to indicate the status of initialization.
+     */
+    public static void init() {
+        FileInputStream configFileIS = null;
+        String result = "success";
+        seoPatterns = new HashMap<String, Pattern>();
+        seoReplacements = new HashMap<String, String>();
+        forwardReplacements = new HashMap<String, String>();
+        forwardPatterns = new HashMap<String, Pattern>();
+        forwardResponseCodes = new HashMap<String, Integer>();
+        userExceptionPatterns = FastList.newInstance();
+        specialProductIds = FastMap.newInstance();
+        nameFilters = FastMap.newInstance();
+        try {
+            Document configDoc = UtilXml.readXmlDocument(UtilURL.fromResource(SEO_CONFIG_FILENAME), false);
+            Element rootElement = configDoc.getDocumentElement();
+
+            String regexIfMatch = UtilXml.childElementValue(rootElement,
+                    ELEMENT_REGEXPIFMATCH, DEFAULT_REGEXP);
+            try {
+                regexpIfMatch = perlCompiler.compile(regexIfMatch,
+                        Perl5Compiler.DEFAULT_MASK);
+            } catch (MalformedPatternException e1) {
+                // do nothing
+            }
+            debug = Boolean.parseBoolean(UtilXml.childElementValue(
+                    rootElement, ELEMENT_DEBUG, "false"));
+
+            // parse jsessionid element
+            try {
+                Element jSessionId = UtilXml.firstChildElement(rootElement,
+                        ELEMENT_JSESSIONID);
+                if (jSessionId != null) {
+                    Element anonymous = UtilXml.firstChildElement(jSessionId, ELEMENT_ANONYMOUS);
+                    if (anonymous != null) {
+                        String anonymousValue = UtilXml.childElementValue(anonymous, ELEMENT_VALUE, DEFAULT_ANONYMOUS_VALUE);
+                        if (DEFAULT_ANONYMOUS_VALUE.equalsIgnoreCase(anonymousValue)) {
+                            jSessionIdAnonEnabled = false;
+                        } else {
+                            jSessionIdAnonEnabled = true;
+                        }
+                    }
+                    
+                    Element user = UtilXml.firstChildElement(jSessionId, ELEMENT_USER);
+                    if (user != null) {
+                        String userValue = UtilXml.childElementValue(user, ELEMENT_VALUE, DEFAULT_USER_VALUE);
+                        if (DEFAULT_USER_VALUE.equalsIgnoreCase(userValue)) {
+                            jSessionIdUserEnabled = false;
+                        } else {
+                            jSessionIdUserEnabled = true;
+                        }
+                        Element exceptions = UtilXml.firstChildElement(user, ELEMENT_EXCEPTIONS);
+                        if (exceptions != null) {
+                            List<? extends Element> exceptionUrlPatterns = UtilXml.childElementList(exceptions, ELEMENT_URLPATTERN);
+                            for (int i = 0; i < exceptionUrlPatterns.size(); i++) {
+                                Element element = (Element) exceptionUrlPatterns.get(i);
+                                String urlpattern = element.getTextContent();
+                                if (UtilValidate.isNotEmpty(urlpattern)) {
+                                    try {
+                                        Pattern pattern = perlCompiler.compile(
+                                                urlpattern, Perl5Compiler.DEFAULT_MASK);
+                                        userExceptionPatterns.add(pattern);
+                                    } catch (MalformedPatternException e) {
+                                        // skip this url replacement if any error happened
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (NullPointerException e) {
+                // no "jsessionid" element
+            }
+            
+            // parse name-filters elements
+            try {
+                NodeList nameFilterNodes = rootElement
+                        .getElementsByTagName(ELEMENT_FILTER);
+                for (int i = 0; i < nameFilterNodes.getLength(); i++) {
+                    Element element = (Element) nameFilterNodes.item(i);
+                    String charaterPattern = UtilXml.childElementValue(element,
+                            ELEMENT_CHARACTER_PATTERN, null);
+                    String replacement = UtilXml.childElementValue(element,
+                            ELEMENT_REPLACEMENT, null);
+                    if (UtilValidate.isNotEmpty(charaterPattern)
+                            && UtilValidate.isNotEmpty(replacement)) {
+                        try {
+                            perlCompiler.compile(
+                                    charaterPattern, Perl5Compiler.DEFAULT_MASK);
+                            nameFilters.put(charaterPattern,
+                                    replacement);
+                        } catch (MalformedPatternException e) {
+                            // skip this filter (character-pattern replacement) if any error happened
+                        }
+                    }
+                }
+            } catch (NullPointerException e) {
+                // no "name-filters" element
+            }
+
+            // parse config elements
+            try {
+                // construct seo patterns
+                NodeList seos = rootElement
+                        .getElementsByTagName(ELEMENT_SEO);
+                for (int i = 0; i < seos.getLength(); i++) {
+                    Element element = (Element) seos.item(i);
+                    String urlpattern = UtilXml.childElementValue(element,
+                            ELEMENT_URLPATTERN, null);
+                    String replacement = UtilXml.childElementValue(element,
+                            ELEMENT_REPLACEMENT, null);
+                    if (UtilValidate.isNotEmpty(urlpattern)
+                            && UtilValidate.isNotEmpty(replacement)) {
+                        try {
+                            Pattern pattern = perlCompiler.compile(
+                                    urlpattern, Perl5Compiler.DEFAULT_MASK);
+                            seoReplacements.put(urlpattern,
+                                    replacement);
+                            seoPatterns.put(urlpattern, pattern);
+                        } catch (MalformedPatternException e) {
+                            // skip this url replacement if any error happened
+                        }
+                    }
+                }
+
+                // construct forward patterns
+                NodeList forwards = rootElement
+                        .getElementsByTagName(ELEMENT_FORWARD);
+                for (int i = 0; i < forwards.getLength(); i++) {
+                    Element element = (Element) forwards.item(i);
+                    String urlpattern = UtilXml.childElementValue(element,
+                            ELEMENT_URLPATTERN, null);
+                    String replacement = UtilXml.childElementValue(element,
+                            ELEMENT_REPLACEMENT, null);
+                    String responseCode = UtilXml.childElementValue(element,
+                            ELEMENT_RESPONSECODE, String.valueOf(DEFAULT_RESPONSECODE));
+                    if (UtilValidate.isNotEmpty(urlpattern)
+                            && UtilValidate.isNotEmpty(replacement)) {
+                        try {
+                            Pattern pattern = perlCompiler.compile(
+                                    urlpattern, Perl5Compiler.DEFAULT_MASK);
+                            forwardReplacements.put(urlpattern,
+                                    replacement);
+                            forwardPatterns.put(urlpattern, pattern);
+                            if (UtilValidate.isNotEmpty(responseCode)) {
+                                Integer responseCodeInt = DEFAULT_RESPONSECODE;
+                                try {
+                                    responseCodeInt = Integer.valueOf(responseCode);
+                                } catch (NumberFormatException nfe) {
+                                    // do nothing
+                                }
+                                forwardResponseCodes.put(urlpattern, responseCodeInt);
+                            }
+                        } catch (MalformedPatternException e) {
+                            // skip this url replacement if any error happened
+                        }
+                    }
+                }
+
+            } catch (NullPointerException e) {
+                // no "config" element
+            }
+        } catch (SAXException e) {
+            result = "error";
+            Debug.logError(e, module);
+        } catch (ParserConfigurationException e) {
+            result = "error";
+            Debug.logError(e, module);
+        } catch (IOException e) {
+            result = "error";
+            Debug.logError(e, module);
+        } finally {
+            if (configFileIS != null) {
+                try {
+                    configFileIS.close();
+                } catch (IOException e) {
+                    result = "error";
+                    Debug.logError(e, module);
+                }
+            }
+        }
+        if (seoReplacements.keySet().isEmpty()) {
+            useUrlRegexp = false;
+        } else {
+            useUrlRegexp = true;
+        }
+        if (result.equals("success")) {
+            isInitialed = true;
+        }
+    }
+    
+    /**
+     * Check whether the configuration file has been read.
+     *
+     * @return a boolean value to indicate whether the configuration file has been read.
+     */
+    public static boolean isInitialed() {
+        return isInitialed;
+    }
+
+    /**
+     * Check whether debug is enabled.
+     *
+     * @return a boolean value to indicate whether debug is enabled.
+     */
+    public static boolean isDebugEnabled() {
+        return debug;
+    }
+
+    /**
+     * Check whether url regexp should be used.
+     *
+     * @return a boolean value to indicate whether url regexp should be used.
+     */
+    public static boolean checkUseUrlRegexp() {
+        return useUrlRegexp;
+    }
+
+    /**
+     * Get the general regexp pattern.
+     *
+     * @return the general regexp pattern.
+     */
+    public static Pattern getGeneralRegexpPattern() {
+        return regexpIfMatch;
+    }
+    
+    /**
+     * Check whether category url is enabled.
+     *
+     * @return a boolean value to indicate whether category url is enabled.
+     */
+    public static boolean checkCategoryUrl() {
+        return categoryUrlEnabled;
+    }
+
+    /**
+     * Check whether the context path is enabled.
+     *
+     * @return a boolean value to indicate whether the context path is enabled.
+     */
+    public static boolean isCategoryUrlEnabled(String contextPath) {
+        if (contextPath == null) {
+            return false;
+        }
+        if (UtilValidate.isEmpty(contextPath)) {
+            contextPath = "/";
+        }
+        if (categoryUrlEnabled) {
+            if (allowedContextPaths.contains(contextPath.trim())) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check whether category name is enabled.
+     *
+     * @return a boolean value to indicate whether category name is enabled.
+     */
+    public static boolean isCategoryNameEnabled() {
+        return categoryNameEnabled;
+    }
+
+    /**
+     * Get category url suffix.
+     *
+     * @return String category url suffix.
+     */
+    public static String getCategoryUrlSuffix() {
+        return categoryUrlSuffix;
+    }
+
+    /**
+     * Check whether jsessionid is enabled for anonymous.
+     *
+     * @return a boolean value to indicate whether jsessionid is enabled for anonymous.
+     */
+    public static boolean isJSessionIdAnonEnabled() {
+        return jSessionIdAnonEnabled;
+    }
+
+    /**
+     * Check whether jsessionid is enabled for user.
+     *
+     * @return a boolean value to indicate whether jsessionid is enabled for user.
+     */
+    public static boolean isJSessionIdUserEnabled() {
+        return jSessionIdUserEnabled;
+    }
+
+    /**
+     * Get user exception url pattern configures.
+     *
+     * @return user exception url pattern configures (java.util.List<Pattern>)
+     */
+    public static List<Pattern> getUserExceptionPatterns() {
+        return userExceptionPatterns;
+    }
+
+    /**
+     * Get name filters.
+     *
+     * @return name filters (java.util.Map<String, String>)
+     */
+    public static Map<String, String> getNameFilters() {
+        return nameFilters;
+    }
+
+    /**
+     * Get seo url pattern configures.
+     *
+     * @return seo url pattern configures (java.util.Map<String, Pattern>)
+     */
+    public static Map<String, Pattern> getSeoPatterns() {
+        return seoPatterns;
+    }
+
+    /**
+     * Get seo replacement configures.
+     *
+     * @return seo replacement configures (java.util.Map<String, String>)
+     */
+    public static Map<String, String> getSeoReplacements() {
+        return seoReplacements;
+    }
+
+    /**
+     * Get forward url pattern configures.
+     *
+     * @return forward url pattern configures (java.util.Map<String, Pattern>)
+     */
+    public static Map<String, Pattern> getForwardPatterns() {
+        return forwardPatterns;
+    }
+
+    /**
+     * Get forward replacement configures.
+     *
+     * @return forward replacement configures (java.util.Map<String, String>)
+     */
+    public static Map<String, String> getForwardReplacements() {
+        return forwardReplacements;
+    }
+
+    /**
+     * Get forward response codes.
+     *
+     * @return forward response code configures (java.util.Map<String, Integer>)
+     */
+    public static Map<String, Integer> getForwardResponseCodes() {
+        return forwardResponseCodes;
+    }
+
+    /**
+     * Check whether a product id is in the special list. If we cannot get a product from a lower cased
+     * or upper cased product id, then it's special.
+     *
+     * @return boolean to indicate whether the product id is special.
+     */
+    public static boolean isSpecialProductId(String productId) {
+        return specialProductIds.containsKey(productId);
+    }
+
+    /**
+     * Add a special product id to the special list.
+     *
+     * @param productId a product id get from database.
+     * @return true to indicate it has been added to special product id; false to indicate it's not special.
+     * @throws Exception to indicate there's already same lower cased product id in the list but value is a different product id.
+     */
+    public static boolean addSpecialProductId(String productId) throws Exception {
+        if (productId.toLowerCase().equals(productId) || productId.toUpperCase().equals(productId)) {
+            return false;
+        }
+        if (isSpecialProductId(productId.toLowerCase())) {
+            if (specialProductIds.containsValue(productId)) {
+                return true;
+            } else {
+                throw new Exception("This product Id cannot be lower cased for SEO URL purpose: " + productId);
+            }
+        }
+        specialProductIds.put(productId.toLowerCase(), productId);
+        return true;
+    }
+    
+    /**
+     * Get a product id is in the special list.
+     *
+     * @return String of the original product id
+     */
+    public static String getSpecialProductId(String productId) {
+        return specialProductIds.get(productId);
+    }
+
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+
+package org.ofbiz.product.category;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import javolution.util.FastList;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.StringUtil;
+import org.ofbiz.base.util.UtilHttp;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.common.UrlServletHelper;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.condition.EntityCondition;
+import org.ofbiz.entity.condition.EntityOperator;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.webapp.control.ContextFilter;
+import org.owasp.esapi.errors.EncodingException;
+
+public class SeoContentUrlFilter extends ContextFilter {
+    public final static String module = SeoContentUrlFilter.class.getName();
+    protected static String defaultLocaleString = null;
+    protected static String redirectUrl = null;
+    public static String defaultViewRequest = "contentViewInfo";
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+        Delegator delegator = (Delegator) httpRequest.getSession().getServletContext().getAttribute("delegator");
+
+        // Get ServletContext
+        ServletContext servletContext = config.getServletContext();
+        // Set request attribute and session
+        UrlServletHelper.setRequestAttributes(request, delegator, servletContext);
+        String urlContentId = null;
+        StringBuffer pathInfoBuffer = UtilHttp.getFullRequestUrl(httpRequest);
+        String pathInfo = pathInfoBuffer.toString();
+        if (UtilValidate.isNotEmpty(pathInfo)) {
+            String alternativeUrl = pathInfo.substring(pathInfo.lastIndexOf("/"));
+            if (alternativeUrl.endsWith("-content")) {
+                try {
+                    List<GenericValue> contentDataResourceViews = delegator.findByAnd("ContentDataResourceView", UtilMisc.toMap("drObjectInfo", alternativeUrl), null, false);
+                    if (contentDataResourceViews.size() > 0) {
+                        contentDataResourceViews = EntityUtil.orderBy(contentDataResourceViews, UtilMisc.toList("createdDate DESC"));
+                        GenericValue contentDataResourceView = EntityUtil.getFirst(contentDataResourceViews);
+                        List<GenericValue> contents = EntityUtil.filterByDate(delegator.findByAnd("ContentAssoc",
+                                UtilMisc.toMap("contentAssocTypeId", "ALTERNATIVE_URL", "contentIdTo", contentDataResourceView.getString("contentId")), null, false));
+                        if (contents.size() > 0) {
+                            GenericValue content = EntityUtil.getFirst(contents);
+                            urlContentId = content.getString("contentId");
+                        }
+                    }
+                } catch (Exception e) {
+                    Debug.logWarning(e.getMessage(), module);
+                }
+            }
+            if (UtilValidate.isNotEmpty(urlContentId)) {
+                StringBuilder urlBuilder = new StringBuilder();
+                if (UtilValidate.isNotEmpty(SeoControlServlet.controlServlet)) {
+                    urlBuilder.append("/" + SeoControlServlet.controlServlet);
+                }
+                urlBuilder.append("/" + config.getInitParameter("viewRequest") + "?contentId=" + urlContentId);
+
+                // Set view query parameters
+                UrlServletHelper.setViewQueryParameters(request, urlBuilder);
+                Debug.logInfo("[Filtered request]: " + pathInfo + " (" + urlBuilder + ")", module);
+                RequestDispatcher dispatch = request.getRequestDispatcher(urlBuilder.toString());
+                dispatch.forward(request, response);
+                return;
+            }
+
+            // Check path alias
+            UrlServletHelper.checkPathAlias(request, httpResponse, delegator, pathInfo);
+        }
+        // we're done checking; continue on
+        chain.doFilter(request, response);
+    }
+
+    public static String makeContentAltUrl(HttpServletRequest request, HttpServletResponse response, String contentId, String viewContent) {
+        if (UtilValidate.isEmpty(contentId)) {
+            return null;
+        }
+        Delegator delegator = (Delegator) request.getAttribute("delegator");
+        String url = null;
+        try {
+            List<EntityCondition> expr = FastList.newInstance();
+            expr.add(EntityCondition.makeCondition("caContentAssocTypeId", EntityOperator.EQUALS, "ALTERNATIVE_URL"));
+            expr.add(EntityCondition.makeCondition("caThruDate", EntityOperator.EQUALS, null));
+            expr.add(EntityCondition.makeCondition("contentIdStart", EntityOperator.EQUALS, contentId));
+            Set<String> fieldsToSelect = UtilMisc.toSet("contentIdStart", "drObjectInfo", "dataResourceId", "caFromDate", "caThruDate", "caCreatedDate");
+            List<GenericValue> contentAssocDataResources = delegator.findList("ContentAssocDataResourceViewTo", EntityCondition.makeCondition(expr), fieldsToSelect,
+                    UtilMisc.toList("-caFromDate"), null, true);
+            if (contentAssocDataResources.size() > 0) {
+                GenericValue contentAssocDataResource = EntityUtil.getFirst(contentAssocDataResources);
+                url = contentAssocDataResource.getString("drObjectInfo");
+                try {
+                    url = StringUtil.defaultWebEncoder.decodeFromURL(url);
+                    String mountPoint = request.getContextPath();
+                    if (!(mountPoint.equals("/")) && !(mountPoint.equals(""))) {
+                        url = mountPoint + url;
+                    }
+                } catch (EncodingException e) {
+                    Debug.logError(e, module);
+                }
+            }
+        } catch (Exception e) {
+            Debug.logWarning("[Exception] : " + e.getMessage(), module);
+        }
+
+        if (UtilValidate.isEmpty(url)) {
+            if (UtilValidate.isEmpty(viewContent)) {
+                viewContent = defaultViewRequest;
+            }
+            url = makeContentUrl(request, response, contentId, viewContent);
+        }
+        return url;
+    }
+
+    public static String makeContentUrl(HttpServletRequest request, HttpServletResponse response, String contentId, String viewContent) {
+        if (UtilValidate.isEmpty(contentId)) {
+            return null;
+        }
+        StringBuilder urlBuilder = new StringBuilder();
+        urlBuilder.append(request.getSession().getServletContext().getContextPath());
+        if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') {
+            urlBuilder.append("/");
+        }
+        if (UtilValidate.isNotEmpty(SeoControlServlet.controlServlet)) {
+            urlBuilder.append(SeoControlServlet.controlServlet + "/");
+        }
+
+        if (UtilValidate.isNotEmpty(viewContent)) {
+            urlBuilder.append(viewContent);
+        } else {
+            urlBuilder.append(defaultViewRequest);
+        }
+        urlBuilder.append("?contentId=" + contentId);
+        return urlBuilder.toString();
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.product.category;
+
+import static org.ofbiz.base.util.UtilGenerics.checkMap;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Matcher;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.StringUtil;
+import org.ofbiz.base.util.UtilHttp;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilObject;
+import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.entity.DelegatorFactory;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.condition.EntityCondition;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.security.Security;
+import org.ofbiz.service.LocalDispatcher;
+import org.ofbiz.webapp.control.ConfigXMLReader;
+import org.ofbiz.webapp.control.ConfigXMLReader.ControllerConfig;
+import org.ofbiz.webapp.control.ContextFilter;
+import org.ofbiz.webapp.control.WebAppConfigurationException;
+import org.ofbiz.webapp.website.WebSiteWorker;
+
+/**
+ * SeoContextFilter - Restricts access to raw files and configures servlet objects.
+ */
+public class SeoContextFilter extends ContextFilter {
+
+    public static final String module = SeoContextFilter.class.getName();
+
+    /**
+     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
+     */
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+        String uri = httpRequest.getRequestURI();
+        boolean forwarded = forwardUri(httpResponse, uri);
+        if (forwarded) {
+            return;
+        }
+
+        URL controllerConfigURL = ConfigXMLReader.getControllerConfigURL(config.getServletContext());
+        ControllerConfig controllerConfig = null;
+        Map<String, ConfigXMLReader.RequestMap> requestMaps = null;
+        try {
+            controllerConfig = ConfigXMLReader.getControllerConfig(controllerConfigURL);
+            requestMaps = controllerConfig.getRequestMapMap();
+        } catch (WebAppConfigurationException e) {
+            Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module);
+            throw new ServletException(e);
+        }
+        Set<String> uris = requestMaps.keySet();
+
+        // NOTE: the following part is copied from org.ofbiz.webapp.control.ContextFilter.doFilter method, please update this if framework is updated.
+        // Debug.logInfo("Running ContextFilter.doFilter", module);
+
+        // ----- Servlet Object Setup -----
+        // set the cached class loader for more speedy running in this thread
+        String disableCachedClassloader = config.getInitParameter("disableCachedClassloader");
+        if (disableCachedClassloader == null || !"Y".equalsIgnoreCase(disableCachedClassloader)) {
+            Thread.currentThread().setContextClassLoader(localCachedClassLoader);
+        }
+
+        // set the ServletContext in the request for future use
+        httpRequest.setAttribute("servletContext", config.getServletContext());
+
+        // set the webSiteId in the session
+        if (UtilValidate.isEmpty(httpRequest.getSession().getAttribute("webSiteId"))) {
+            httpRequest.getSession().setAttribute("webSiteId", WebSiteWorker.getWebSiteId(httpRequest));
+        }
+
+        // set the filesystem path of context root.
+        httpRequest.setAttribute("_CONTEXT_ROOT_", config.getServletContext().getRealPath("/"));
+
+        // set the server root url
+        StringBuffer serverRootUrl = UtilHttp.getServerRootUrl(httpRequest);
+        httpRequest.setAttribute("_SERVER_ROOT_URL_", serverRootUrl.toString());
+
+        // request attributes from redirect call
+        String reqAttrMapHex = (String) httpRequest.getSession().getAttribute("_REQ_ATTR_MAP_");
+        if (UtilValidate.isNotEmpty(reqAttrMapHex)) {
+            byte[] reqAttrMapBytes = StringUtil.fromHexString(reqAttrMapHex);
+            Map<String, Object> reqAttrMap = checkMap(UtilObject.getObject(reqAttrMapBytes), String.class, Object.class);
+            if (reqAttrMap != null) {
+                for (Map.Entry<String, Object> entry : reqAttrMap.entrySet()) {
+                    httpRequest.setAttribute(entry.getKey(), entry.getValue());
+                }
+            }
+            httpRequest.getSession().removeAttribute("_REQ_ATTR_MAP_");
+        }
+
+        // ----- Context Security -----
+        // check if we are disabled
+        String disableSecurity = config.getInitParameter("disableContextSecurity");
+        if (disableSecurity != null && "Y".equalsIgnoreCase(disableSecurity)) {
+            chain.doFilter(httpRequest, httpResponse);
+            return;
+        }
+
+        // check if we are told to redirect everthing
+        String redirectAllTo = config.getInitParameter("forceRedirectAll");
+        if (UtilValidate.isNotEmpty(redirectAllTo)) {
+            // little trick here so we don't loop on ourself
+            if (httpRequest.getSession().getAttribute("_FORCE_REDIRECT_") == null) {
+                httpRequest.getSession().setAttribute("_FORCE_REDIRECT_", "true");
+                Debug.logWarning("Redirecting user to: " + redirectAllTo, module);
+
+                if (!redirectAllTo.toLowerCase().startsWith("http")) {
+                    redirectAllTo = httpRequest.getContextPath() + redirectAllTo;
+                }
+                httpResponse.sendRedirect(redirectAllTo);
+                return;
+            } else {
+                httpRequest.getSession().removeAttribute("_FORCE_REDIRECT_");
+                chain.doFilter(httpRequest, httpResponse);
+                return;
+            }
+        }
+
+        // test to see if we have come through the control servlet already, if not do the processing
+        String requestPath = null;
+        String contextUri = null;
+        if (httpRequest.getAttribute(ContextFilter.FORWARDED_FROM_SERVLET) == null) {
+            // Debug.logInfo("In ContextFilter.doFilter, FORWARDED_FROM_SERVLET is NOT set", module);
+            String allowedPath = config.getInitParameter("allowedPaths");
+            String redirectPath = config.getInitParameter("redirectPath");
+            String errorCode = config.getInitParameter("errorCode");
+
+            List<String> allowList = StringUtil.split(allowedPath, ":");
+            allowList.add("/"); // No path is allowed.
+            if (UtilValidate.isNotEmpty(httpRequest.getServletPath())) {
+                allowList.add(""); // No path is allowed if servlet path is not empty.
+            }
+
+            if (debug) Debug.logInfo("[Domain]: " + httpRequest.getServerName() + " [Request]: " + httpRequest.getRequestURI(), module);
+
+            requestPath = httpRequest.getServletPath();
+            if (requestPath == null) requestPath = "";
+            if (requestPath.lastIndexOf("/") > 0) {
+                if (requestPath.indexOf("/") == 0) {
+                    requestPath = "/" + requestPath.substring(1, requestPath.indexOf("/", 1));
+                } else {
+                    requestPath = requestPath.substring(1, requestPath.indexOf("/"));
+                }
+            }
+
+            String requestInfo = httpRequest.getServletPath();
+            if (requestInfo == null) requestInfo = "";
+            if (requestInfo.lastIndexOf("/") >= 0) {
+                requestInfo = requestInfo.substring(0, requestInfo.lastIndexOf("/")) + "/*";
+            }
+
+            StringBuilder contextUriBuffer = new StringBuilder();
+            if (httpRequest.getContextPath() != null) {
+                contextUriBuffer.append(httpRequest.getContextPath());
+            }
+            if (httpRequest.getServletPath() != null) {
+                contextUriBuffer.append(httpRequest.getServletPath());
+            }
+            if (httpRequest.getPathInfo() != null) {
+                contextUriBuffer.append(httpRequest.getPathInfo());
+            }
+            contextUri = contextUriBuffer.toString();
+
+            List<String> pathItemList = StringUtil.split(httpRequest.getPathInfo(), "/");
+            String viewName = "";
+            if (pathItemList != null) {
+                viewName = pathItemList.get(0);
+            }
+
+            // Verbose Debugging
+            if (Debug.verboseOn()) {
+                for (String allow : allowList) {
+                    Debug.logVerbose("[Allow]: " + allow, module);
+                }
+                Debug.logVerbose("[Request path]: " + requestPath, module);
+                Debug.logVerbose("[Request info]: " + requestInfo, module);
+                Debug.logVerbose("[Servlet path]: " + httpRequest.getServletPath(), module);
+                Debug.logVerbose(
+                        "[Not In AllowList]: " + (!allowList.contains(requestPath) && !allowList.contains(requestInfo) && !allowList.contains(httpRequest.getServletPath())),
+                        module);
+                Debug.logVerbose("[Not In controller]: " + (UtilValidate.isEmpty(requestPath) && UtilValidate.isEmpty(httpRequest.getServletPath()) && !uris.contains(viewName)),
+                        module);
+            }
+
+            // check to make sure the requested url is allowed
+            if (!allowList.contains(requestPath) && !allowList.contains(requestInfo) && !allowList.contains(httpRequest.getServletPath())
+                    && (UtilValidate.isEmpty(requestPath) && UtilValidate.isEmpty(httpRequest.getServletPath()) && !uris.contains(viewName))) {
+                String filterMessage = "[Filtered request]: " + contextUri;
+
+                if (redirectPath == null) {
+                    if (UtilValidate.isEmpty(viewName)) {
+                        // redirect without any url change in browser
+                        RequestDispatcher rd = request.getRequestDispatcher(SeoControlServlet.defaultPage);
+                        rd.forward(request, response);
+                    } else {
+                        int error = 404;
+                        if (UtilValidate.isNotEmpty(errorCode)) {
+                            try {
+                                error = Integer.parseInt(errorCode);
+                            } catch (NumberFormatException nfe) {
+                                Debug.logWarning(nfe, "Error code specified would not parse to Integer : " + errorCode, module);
+                            }
+                        }
+                        filterMessage = filterMessage + " (" + error + ")";
+                        httpResponse.sendError(error, contextUri);
+                        request.setAttribute("filterRequestUriError", contextUri);
+                    }
+                } else {
+                    filterMessage = filterMessage + " (" + redirectPath + ")";
+                    if (!redirectPath.toLowerCase().startsWith("http")) {
+                        redirectPath = httpRequest.getContextPath() + redirectPath;
+                    }
+                    // httpResponse.sendRedirect(redirectPath);
+                    if (uri.equals("") || uri.equals("/")) {
+                        // redirect without any url change in browser
+                        RequestDispatcher rd = request.getRequestDispatcher(redirectPath);
+                        rd.forward(request, response);
+                    } else {
+                        // redirect with url change in browser
+                        httpResponse.setStatus(SeoConfigUtil.DEFAULT_RESPONSECODE);
+                        httpResponse.setHeader("Location", redirectPath);
+                    }
+                }
+                Debug.logWarning(filterMessage, module);
+                return;
+            }
+        }
+
+        // check if multi tenant is enabled
+        String useMultitenant = UtilProperties.getPropertyValue("general.properties", "multitenant");
+        if ("Y".equals(useMultitenant)) {
+            // get tenant delegator by domain name
+            String serverName = httpRequest.getServerName();
+            try {
+                // if tenant was specified, replace delegator with the new per-tenant delegator and set tenantId to session attribute
+                Delegator delegator = getDelegator(config.getServletContext());
+                List<GenericValue> tenants = delegator.findList("Tenant", EntityCondition.makeCondition("domainName", serverName), null, UtilMisc.toList("-createdStamp"), null, false);
+                if (UtilValidate.isNotEmpty(tenants)) {
+                    GenericValue tenant = EntityUtil.getFirst(tenants);
+                    String tenantId = tenant.getString("tenantId");
+
+                    // if the request path is a root mount then redirect to the initial path
+                    if (UtilValidate.isNotEmpty(requestPath) && requestPath.equals(contextUri)) {
+                        String initialPath = tenant.getString("initialPath");
+                        if (UtilValidate.isNotEmpty(initialPath) && !"/".equals(initialPath)) {
+                            ((HttpServletResponse) response).sendRedirect(initialPath);
+                            return;
+                        }
+                    }
+
+                    // make that tenant active, setup a new delegator and a new dispatcher
+                    String tenantDelegatorName = delegator.getDelegatorBaseName() + "#" + tenantId;
+                    httpRequest.getSession().setAttribute("delegatorName", tenantDelegatorName);
+
+                    // after this line the delegator is replaced with the new per-tenant delegator
+                    delegator = DelegatorFactory.getDelegator(tenantDelegatorName);
+                    config.getServletContext().setAttribute("delegator", delegator);
+
+                    // clear web context objects
+                    config.getServletContext().setAttribute("security", null);
+                    config.getServletContext().setAttribute("dispatcher", null);
+
+                    // initialize security
+                    Security security = getSecurity();
+                    // initialize the services dispatcher
+                    LocalDispatcher dispatcher = getDispatcher(config.getServletContext());
+
+                    // set web context objects
+                    request.setAttribute("dispatcher", dispatcher);
+                    request.setAttribute("security", security);
+
+                    request.setAttribute("tenantId", tenantId);
+                }
+
+                // NOTE DEJ20101130: do NOT always put the delegator name in the user's session because the user may
+                // have logged in and specified a tenant, and even if no Tenant record with a matching domainName field
+                // is found this will change the user's delegator back to the base one instead of the one for the
+                // tenant specified on login
+                // httpRequest.getSession().setAttribute("delegatorName", delegator.getDelegatorName());
+            } catch (GenericEntityException e) {
+                Debug.logWarning(e, "Unable to get Tenant", module);
+            }
+        }
+
+        // we're done checking; continue on
+        chain.doFilter(httpRequest, httpResponse);
+    }
+
+    /**
+     * Forward a uri according to forward pattern regular expressions. Note: this is developed for Filter usage.
+     *
+     * @param uri String to reverse transform
+     * @return String
+     */
+    protected static boolean forwardUri(HttpServletResponse response, String uri) {
+        Perl5Matcher matcher = new Perl5Matcher();
+        boolean foundMatch = false;
+        Integer responseCodeInt = null;
+
+        if (SeoConfigUtil.checkUseUrlRegexp() && SeoConfigUtil.getForwardPatterns() != null && SeoConfigUtil.getForwardReplacements() != null) {
+            Iterator<String> keys = SeoConfigUtil.getForwardPatterns().keySet().iterator();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                Pattern pattern = SeoConfigUtil.getForwardPatterns().get(key);
+                String replacement = SeoConfigUtil.getForwardReplacements().get(key);
+                if (matcher.matches(uri, pattern)) {
+                    for (int i = matcher.getMatch().groups(); i > 0; i--) {
+                        replacement = replacement.replaceAll("\\$" + i, matcher.getMatch().group(i));
+                    }
+                    uri = replacement;
+                    responseCodeInt = SeoConfigUtil.getForwardResponseCodes().get(key);
+                    foundMatch = true;
+                    // be careful, we don't break after finding a match
+                }
+            }
+        }
+
+        if (foundMatch) {
+            if (responseCodeInt == null) {
+                response.setStatus(SeoConfigUtil.DEFAULT_RESPONSECODE);
+            } else {
+                response.setStatus(responseCodeInt.intValue());
+            }
+            response.setHeader("Location", uri);
+        } else if (SeoConfigUtil.isDebugEnabled()) {
+            Debug.logInfo("Can NOT forward this url: " + uri, module);
+        }
+        return foundMatch;
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.product.category;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.webapp.control.ControlServlet;
+
+/**
+ * SeoControlServlet.java - SEO Master servlet for the web application.
+ */
+@SuppressWarnings("serial")
+public class SeoControlServlet extends ControlServlet {
+
+    public static final String module = SeoControlServlet.class.getName();
+
+    protected static String defaultPage = null;
+    protected static String controlServlet = null;
+
+    public SeoControlServlet() {
+        super();
+    }
+
+    /**
+     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+     */
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+
+        ServletContext context = this.getServletContext();
+        if (UtilValidate.isEmpty(defaultPage)) {
+            defaultPage = context.getInitParameter("defaultPage");
+        }
+        if (UtilValidate.isEmpty(defaultPage)) {
+            defaultPage = "/main";
+        }
+
+        if (defaultPage.startsWith("/") && defaultPage.lastIndexOf("/") > 0) {
+            controlServlet = defaultPage.substring(1);
+            controlServlet = controlServlet.substring(0, controlServlet.indexOf("/"));
+        }
+
+        SeoConfigUtil.init();
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.product.category;
+
+import org.ofbiz.base.util.UtilValidate;
+
+public class SeoUrlUtil {
+    public static String replaceSpecialCharsUrl(String url) {
+        if (UtilValidate.isEmpty(url)) {
+            url = "";
+        }
+        for (String characterPattern : SeoConfigUtil.getNameFilters().keySet()) {
+            url = url.replaceAll(characterPattern, SeoConfigUtil.getNameFilters().get(characterPattern));
+        }
+        return url;
+    }
+
+    public static String removeContextPath(String uri, String contextPath) {
+        if (UtilValidate.isEmpty(contextPath) || UtilValidate.isEmpty(uri)) {
+            return uri;
+        }
+        if (uri.length() > contextPath.length() && uri.startsWith(contextPath)) {
+            return uri.substring(contextPath.length());
+        }
+        return uri;
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,526 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.product.category;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.ParserConfigurationException;
+
+import javolution.util.FastList;
+import javolution.util.FastMap;
+import javolution.util.FastSet;
+
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.StringUtil;
+import org.ofbiz.base.util.UtilURL;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.UtilXml;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * UrlRegexpConfigUtil - Configuration file utility.
+ *
+ */
+public class UrlRegexpConfigUtil {
+
+    private static final String module = UrlRegexpConfigUtil.class.getName();
+    private static Perl5Compiler m_perlCompiler = new Perl5Compiler();
+    private static boolean m_isInitialed = false;
+    private static boolean m_debug = false;
+    private static boolean m_categoryUrlEnabled = true;
+    private static boolean m_categoryNameEnabled = false;
+    private static String m_categoryUrlSuffix = null;
+    private static Pattern m_regexpIfMatch = null;
+    private static boolean m_useUrlRegexp = false;
+    private static boolean m_jSessionIdAnonEnabled = false;
+    private static boolean m_jSessionIdUserEnabled = false;
+    private static Map<String, String> m_seoReplacements = null;
+    private static Map<String, Pattern> m_seoPatterns = null;
+    private static Map<String, String> m_forwardReplacements = null;
+    private static Map<String, Pattern> m_forwardPatterns = null;
+    private static Map<String, Integer> m_forwardResponseCodes = null;
+    private static Map<String, String> m_nameFilters = null;
+    private static List<Pattern> m_userExceptionPatterns = null;
+    private static Set<String> m_allowedContextPaths = null;
+    private static Map<String, String> m_specialProductIds = null;
+    public static final String DEFAULT_REGEXP = "^.*/.*$";
+    public static final String ELEMENT_REGEXPIFMATCH = "regexpifmatch";
+    public static final String ELEMENT_DEBUG = "debug";
+    public static final String ELEMENT_CATEGORY_URL = "category-url";
+    public static final String ELEMENT_ALLOWED_CONTEXT_PATHS = "allowed-context-paths";
+    public static final String ELEMENT_CATEGORY_NAME = "category-name";
+    public static final String ELEMENT_CATEGORY_URL_SUFFIX = "category-url-suffix";
+    public static final String ELEMENT_CONFIG = "config";
+    public static final String ELEMENT_DESCRIPTION = "description";
+    public static final String ELEMENT_FORWARD = "forward";
+    public static final String ELEMENT_SEO = "seo";
+    public static final String ELEMENT_URLPATTERN = "url-pattern";
+    public static final String ELEMENT_REPLACEMENT = "replacement";
+    public static final String ELEMENT_RESPONSECODE = "responsecode";
+    public static final String ELEMENT_JSESSIONID = "jsessionid";
+    public static final String ELEMENT_ANONYMOUS = "anonymous";
+    public static final String ELEMENT_VALUE = "value";
+    public static final String ELEMENT_USER = "user";
+    public static final String ELEMENT_EXCEPTIONS = "exceptions";
+    public static final String ELEMENT_NAME_FILTERS = "name-filters";
+    public static final String ELEMENT_FILTER = "filter";
+    public static final String ELEMENT_CHARACTER_PATTERN = "character-pattern";
+    public static final String URL_REGEXP_CONFIG_FILENAME = "urlregexp.xml";
+    public static final int DEFAULT_RESPONSECODE = HttpServletResponse.SC_MOVED_PERMANENTLY;
+    public static final String DEFAULT_ANONYMOUS_VALUE = "disable";
+    public static final String DEFAULT_USER_VALUE = "disable";
+    public static final String DEFAULT_CATEGORY_URL_VALUE = "enable";
+    public static final String DEFAULT_CATEGORY_NAME_VALUE = "disable";
+    public static final String ALLOWED_CONTEXT_PATHS_SEPERATOR = ":";
+
+    /**
+     * Initialize url regular express configuration.
+     *
+     * @return result to indicate the status of initialization.
+     */
+    public static void init() {
+        FileInputStream configFileIS = null;
+        String result = "success";
+        m_seoPatterns = new HashMap<String, Pattern>();
+        m_seoReplacements = new HashMap<String, String>();
+        m_forwardReplacements = new HashMap<String, String>();
+        m_forwardPatterns = new HashMap<String, Pattern>();
+        m_forwardResponseCodes = new HashMap<String, Integer>();
+        m_userExceptionPatterns = FastList.newInstance();
+        m_specialProductIds = FastMap.newInstance();
+        m_nameFilters = FastMap.newInstance();
+        try {
+            Document configDoc = UtilXml.readXmlDocument(UtilURL.fromResource(URL_REGEXP_CONFIG_FILENAME), false);
+            Element rootElement = configDoc.getDocumentElement();
+
+            String regexIfMatch = UtilXml.childElementValue(rootElement, ELEMENT_REGEXPIFMATCH, DEFAULT_REGEXP);
+            try {
+                m_regexpIfMatch = m_perlCompiler.compile(regexIfMatch, Perl5Compiler.DEFAULT_MASK);
+            } catch (MalformedPatternException e1) {
+                // do nothing
+            }
+            m_debug = Boolean.parseBoolean(UtilXml.childElementValue(rootElement, ELEMENT_DEBUG, "false"));
+
+            // parse category-url element
+            try {
+                Element categoryUrlElement = UtilXml.firstChildElement(rootElement, ELEMENT_CATEGORY_URL);
+                if (categoryUrlElement != null) {
+                    String enableCategoryUrlValue = UtilXml.childElementValue(categoryUrlElement, ELEMENT_VALUE, DEFAULT_CATEGORY_URL_VALUE);
+                    if (DEFAULT_CATEGORY_URL_VALUE.equalsIgnoreCase(enableCategoryUrlValue)) {
+                        m_categoryUrlEnabled = true;
+                    } else {
+                        m_categoryUrlEnabled = false;
+                    }
+
+                    if (m_categoryUrlEnabled) {
+                        String allowedContextValue = UtilXml.childElementValue(categoryUrlElement, ELEMENT_ALLOWED_CONTEXT_PATHS, null);
+                        m_allowedContextPaths = FastSet.newInstance();
+                        if (UtilValidate.isNotEmpty(allowedContextValue)) {
+                            List<String> allowedContextPaths = StringUtil.split(allowedContextValue, ALLOWED_CONTEXT_PATHS_SEPERATOR);
+                            for (String path : allowedContextPaths) {
+                                if (UtilValidate.isNotEmpty(path)) {
+                                    path = path.trim();
+                                    if (!m_allowedContextPaths.contains(path)) {
+                                        m_allowedContextPaths.add(path);
+                                    }
+                                }
+                            }
+                        }
+
+                        String categoryNameValue = UtilXml.childElementValue(categoryUrlElement, ELEMENT_CATEGORY_NAME, DEFAULT_CATEGORY_NAME_VALUE);
+                        if (DEFAULT_CATEGORY_NAME_VALUE.equalsIgnoreCase(categoryNameValue)) {
+                            m_categoryNameEnabled = false;
+                        } else {
+                            m_categoryNameEnabled = true;
+                        }
+
+                        m_categoryUrlSuffix = UtilXml.childElementValue(categoryUrlElement, ELEMENT_CATEGORY_URL_SUFFIX, null);
+                        if (UtilValidate.isNotEmpty(m_categoryUrlSuffix)) {
+                            m_categoryUrlSuffix = m_categoryUrlSuffix.trim();
+                            if (m_categoryUrlSuffix.contains("/")) {
+                                m_categoryUrlSuffix = null;
+                            }
+                        }
+                    }
+                }
+            } catch (NullPointerException e) {
+                // no "category-url" element
+            }
+
+            // parse jsessionid element
+            try {
+                Element jSessionId = UtilXml.firstChildElement(rootElement, ELEMENT_JSESSIONID);
+                if (jSessionId != null) {
+                    Element anonymous = UtilXml.firstChildElement(jSessionId, ELEMENT_ANONYMOUS);
+                    if (anonymous != null) {
+                        String anonymousValue = UtilXml.childElementValue(anonymous, ELEMENT_VALUE, DEFAULT_ANONYMOUS_VALUE);
+                        if (DEFAULT_ANONYMOUS_VALUE.equalsIgnoreCase(anonymousValue)) {
+                            m_jSessionIdAnonEnabled = false;
+                        } else {
+                            m_jSessionIdAnonEnabled = true;
+                        }
+                    }
+
+                    Element user = UtilXml.firstChildElement(jSessionId, ELEMENT_USER);
+                    if (user != null) {
+                        String userValue = UtilXml.childElementValue(user, ELEMENT_VALUE, DEFAULT_USER_VALUE);
+                        if (DEFAULT_USER_VALUE.equalsIgnoreCase(userValue)) {
+                            m_jSessionIdUserEnabled = false;
+                        } else {
+                            m_jSessionIdUserEnabled = true;
+                        }
+                        Element exceptions = UtilXml.firstChildElement(user, ELEMENT_EXCEPTIONS);
+                        if (exceptions != null) {
+                            List<? extends Element> exceptionUrlPatterns = UtilXml.childElementList(exceptions, ELEMENT_URLPATTERN);
+                            for (int i = 0; i < exceptionUrlPatterns.size(); i++) {
+                                Element element = (Element) exceptionUrlPatterns.get(i);
+                                String urlpattern = element.getTextContent();
+                                if (UtilValidate.isNotEmpty(urlpattern)) {
+                                    try {
+                                        Pattern pattern = m_perlCompiler.compile(urlpattern, Perl5Compiler.DEFAULT_MASK);
+                                        m_userExceptionPatterns.add(pattern);
+                                    } catch (MalformedPatternException e) {
+                                        // skip this url replacement if any error happened
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (NullPointerException e) {
+                // no "jsessionid" element
+            }
+
+            // parse name-filters elements
+            try {
+                NodeList nameFilters = rootElement.getElementsByTagName(ELEMENT_FILTER);
+                for (int i = 0; i < nameFilters.getLength(); i++) {
+                    Element element = (Element) nameFilters.item(i);
+                    String charaterPattern = UtilXml.childElementValue(element, ELEMENT_CHARACTER_PATTERN, null);
+                    String replacement = UtilXml.childElementValue(element, ELEMENT_REPLACEMENT, null);
+                    if (UtilValidate.isNotEmpty(charaterPattern) && UtilValidate.isNotEmpty(replacement)) {
+                        try {
+                            m_perlCompiler.compile(charaterPattern, Perl5Compiler.DEFAULT_MASK);
+                            m_nameFilters.put(charaterPattern, replacement);
+                        } catch (MalformedPatternException e) {
+                            // skip this filter (character-pattern replacement) if any error happened
+                        }
+                    }
+                }
+            } catch (NullPointerException e) {
+                // no "name-filters" element
+            }
+
+            // parse config elements
+            try {
+                // construct seo patterns
+                NodeList seos = rootElement.getElementsByTagName(ELEMENT_SEO);
+                for (int i = 0; i < seos.getLength(); i++) {
+                    Element element = (Element) seos.item(i);
+                    String urlpattern = UtilXml.childElementValue(element, ELEMENT_URLPATTERN, null);
+                    String replacement = UtilXml.childElementValue(element, ELEMENT_REPLACEMENT, null);
+                    if (UtilValidate.isNotEmpty(urlpattern) && UtilValidate.isNotEmpty(replacement)) {
+                        try {
+                            Pattern pattern = m_perlCompiler.compile(urlpattern, Perl5Compiler.DEFAULT_MASK);
+                            m_seoReplacements.put(urlpattern, replacement);
+                            m_seoPatterns.put(urlpattern, pattern);
+                        } catch (MalformedPatternException e) {
+                            // skip this url replacement if any error happened
+                        }
+                    }
+                }
+
+                // construct forward patterns
+                NodeList forwards = rootElement.getElementsByTagName(ELEMENT_FORWARD);
+                for (int i = 0; i < forwards.getLength(); i++) {
+                    Element element = (Element) forwards.item(i);
+                    String urlpattern = UtilXml.childElementValue(element, ELEMENT_URLPATTERN, null);
+                    String replacement = UtilXml.childElementValue(element, ELEMENT_REPLACEMENT, null);
+                    String responseCode = UtilXml.childElementValue(element, ELEMENT_RESPONSECODE, String.valueOf(DEFAULT_RESPONSECODE));
+                    if (UtilValidate.isNotEmpty(urlpattern) && UtilValidate.isNotEmpty(replacement)) {
+                        try {
+                            Pattern pattern = m_perlCompiler.compile(urlpattern, Perl5Compiler.DEFAULT_MASK);
+                            m_forwardReplacements.put(urlpattern, replacement);
+                            m_forwardPatterns.put(urlpattern, pattern);
+                            if (UtilValidate.isNotEmpty(responseCode)) {
+                                Integer responseCodeInt = DEFAULT_RESPONSECODE;
+                                try {
+                                    responseCodeInt = Integer.valueOf(responseCode);
+                                } catch (NumberFormatException nfe) {
+                                    // do nothing
+                                }
+                                m_forwardResponseCodes.put(urlpattern, responseCodeInt);
+                            }
+                        } catch (MalformedPatternException e) {
+                            // skip this url replacement if any error happened
+                        }
+                    }
+                }
+
+            } catch (NullPointerException e) {
+                // no "config" element
+            }
+        } catch (SAXException e) {
+            result = "error";
+            Debug.logError(e, module);
+        } catch (ParserConfigurationException e) {
+            result = "error";
+            Debug.logError(e, module);
+        } catch (IOException e) {
+            result = "error";
+            Debug.logError(e, module);
+        } finally {
+            if (configFileIS != null) {
+                try {
+                    configFileIS.close();
+                } catch (IOException e) {
+                    result = "error";
+                    Debug.logError(e, module);
+                }
+            }
+        }
+        if (m_seoReplacements.keySet().isEmpty()) {
+            m_useUrlRegexp = false;
+        } else {
+            m_useUrlRegexp = true;
+        }
+        if (result.equals("success")) {
+            m_isInitialed = true;
+        }
+    }
+
+    /**
+     * Check whether the configuration file has been read.
+     *
+     * @return a boolean value to indicate whether the configuration file has been read.
+     */
+    public static boolean isInitialed() {
+        return m_isInitialed;
+    }
+
+    /**
+     * Check whether debug is enabled.
+     *
+     * @return a boolean value to indicate whether debug is enabled.
+     */
+    public static boolean isDebugEnabled() {
+        return m_debug;
+    }
+
+    /**
+     * Check whether url regexp should be used.
+     *
+     * @return a boolean value to indicate whether url regexp should be used.
+     */
+    public static boolean checkUseUrlRegexp() {
+        return m_useUrlRegexp;
+    }
+
+    /**
+     * Get the general regexp pattern.
+     *
+     * @return the general regexp pattern.
+     */
+    public static Pattern getGeneralRegexpPattern() {
+        return m_regexpIfMatch;
+    }
+
+    /**
+     * Check whether category url is enabled.
+     *
+     * @return a boolean value to indicate whether category url is enabled.
+     */
+    public static boolean checkCategoryUrl() {
+        return m_categoryUrlEnabled;
+    }
+
+    /**
+     * Check whether the context path is enabled.
+     *
+     * @return a boolean value to indicate whether the context path is enabled.
+     */
+    public static boolean isCategoryUrlEnabled(String contextPath) {
+        if (contextPath == null) {
+            return false;
+        }
+        if (UtilValidate.isEmpty(contextPath)) {
+            contextPath = "/";
+        }
+        if (m_categoryUrlEnabled) {
+            if (m_allowedContextPaths.contains(contextPath.trim())) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check whether category name is enabled.
+     *
+     * @return a boolean value to indicate whether category name is enabled.
+     */
+    public static boolean isCategoryNameEnabled() {
+        return m_categoryNameEnabled;
+    }
+
+    /**
+     * Get category url suffix.
+     *
+     * @return String category url suffix.
+     */
+    public static String getCategoryUrlSuffix() {
+        return m_categoryUrlSuffix;
+    }
+
+    /**
+     * Check whether jsessionid is enabled for anonymous.
+     *
+     * @return a boolean value to indicate whether jsessionid is enabled for anonymous.
+     */
+    public static boolean isJSessionIdAnonEnabled() {
+        return m_jSessionIdAnonEnabled;
+    }
+
+    /**
+     * Check whether jsessionid is enabled for user.
+     *
+     * @return a boolean value to indicate whether jsessionid is enabled for user.
+     */
+    public static boolean isJSessionIdUserEnabled() {
+        return m_jSessionIdUserEnabled;
+    }
+
+    /**
+     * Get user exception url pattern configures.
+     *
+     * @return user exception url pattern configures (java.util.List<Pattern>)
+     */
+    public static List<Pattern> getUserExceptionPatterns() {
+        return m_userExceptionPatterns;
+    }
+
+    /**
+     * Get name filters.
+     *
+     * @return name filters (java.util.Map<String, String>)
+     */
+    public static Map<String, String> getNameFilters() {
+        return m_nameFilters;
+    }
+
+    /**
+     * Get seo url pattern configures.
+     *
+     * @return seo url pattern configures (java.util.Map<String, Pattern>)
+     */
+    public static Map<String, Pattern> getSeoPatterns() {
+        return m_seoPatterns;
+    }
+
+    /**
+     * Get seo replacement configures.
+     *
+     * @return seo replacement configures (java.util.Map<String, String>)
+     */
+    public static Map<String, String> getSeoReplacements() {
+        return m_seoReplacements;
+    }
+
+    /**
+     * Get forward url pattern configures.
+     *
+     * @return forward url pattern configures (java.util.Map<String, Pattern>)
+     */
+    public static Map<String, Pattern> getForwardPatterns() {
+        return m_forwardPatterns;
+    }
+
+    /**
+     * Get forward replacement configures.
+     *
+     * @return forward replacement configures (java.util.Map<String, String>)
+     */
+    public static Map<String, String> getForwardReplacements() {
+        return m_forwardReplacements;
+    }
+
+    /**
+     * Get forward response codes.
+     *
+     * @return forward response code configures (java.util.Map<String, Integer>)
+     */
+    public static Map<String, Integer> getForwardResponseCodes() {
+        return m_forwardResponseCodes;
+    }
+
+    /**
+     * Check whether a product id is in the special list. If we cannot get a product from a lower cased or upper cased product id, then it's special.
+     *
+     * @return boolean to indicate whether the product id is special.
+     */
+    public static boolean isSpecialProductId(String productId) {
+        return m_specialProductIds.containsKey(productId);
+    }
+
+    /**
+     * Add a special product id to the special list.
+     *
+     * @param productId a product id get from database.
+     * @return true to indicate it has been added to special product id; false to indicate it's not special.
+     * @throws Exception to indicate there's already same lower cased product id in the list but value is a different product id.
+     */
+    public static boolean addSpecialProductId(String productId) throws Exception {
+        if (productId.toLowerCase().equals(productId) || productId.toUpperCase().equals(productId)) {
+            return false;
+        }
+        if (isSpecialProductId(productId.toLowerCase())) {
+            if (m_specialProductIds.containsValue(productId)) {
+                return true;
+            } else {
+                throw new Exception("This product Id cannot be lower cased for SEO URL purpose: " + productId);
+            }
+        }
+        m_specialProductIds.put(productId.toLowerCase(), productId);
+        return true;
+    }
+
+    /**
+     * Get a product id is in the special list.
+     *
+     * @return String of the original product id
+     */
+    public static String getSpecialProductId(String productId) {
+        return m_specialProductIds.get(productId);
+    }
+
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain