All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.searchpromote.Search Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2011 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.day.cq.searchpromote;

import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.osgi.services.HttpClientBuilderFactory;
import org.apache.http.util.EntityUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.osgi.annotation.versioning.ProviderType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;

import com.day.cq.searchpromote.impl.DecompressingMethod;
import com.day.cq.searchpromote.xml.form.SearchForm;
import com.day.cq.searchpromote.xml.form.SearchFormParser;
import com.day.cq.searchpromote.xml.result.Banner;
import com.day.cq.searchpromote.xml.result.BreadCrumb;
import com.day.cq.searchpromote.xml.result.BreadCrumbItem;
import com.day.cq.searchpromote.xml.result.CustomerResult;
import com.day.cq.searchpromote.xml.result.Facet;
import com.day.cq.searchpromote.xml.result.Menu;
import com.day.cq.searchpromote.xml.result.Pagination;
import com.day.cq.searchpromote.xml.result.Query;
import com.day.cq.searchpromote.xml.result.Result;
import com.day.cq.searchpromote.xml.result.ResultParser;
import com.day.cq.searchpromote.xml.result.ResultSet;
import com.day.cq.searchpromote.xml.result.Suggestion;
import com.day.cq.searchpromote.xml.result.Suggestions;
import com.day.cq.wcm.webservicesupport.Configuration;

@ProviderType
@Deprecated
public final class Search {
    
    private final Logger log = LoggerFactory.getLogger(getClass());
    
    /** Valid query pattern */
    public static final String VALID_QUERY_PATTERN = ".*?q\\d*=.*?";
    
    /** Query parameter */
    public static final String QUERY_PARAM_NAME = "q";
    
    /** Property name member ID */
    public static final String PN_MEMBER_ID = "memberid";
    
    /** Property name account number */
    public static final String PN_ACCOUNT_NUMBER = "accountno";
    
    /** Property name search form XML */
    public static final String PN_SEARCHFORMXML = "searchformxml";
    
    /** Search form parser */
    private SearchFormParser searchFormParser;
    
    /** Search form */
    private SearchForm searchForm;
    
    /** Result parser */
    private ResultParser resultsParser;
    
    /** customer result */
    private CustomerResult customerResult;
        
    /** Sling request */
    private SlingHttpServletRequest request;
    
    /** Configuration */
    private Configuration configuration;
    
    /** HTTPClient Builder Factory */
    private HttpClientBuilderFactory httpClientBuilderFactory;
    
    /** Query string from request */
    private String queryString;
    
    /** Time in ms for requesting results */
    private Long requestTime;
    
    /** Time in ms for parsing results */
    private Long parsingTime;
    
    /** Time in ms for connection timeout */
    private int connectionTimeout = SearchPromoteConstants.DEFAULT_CONNECTION_TIMEOUT;
    
    /** Time in ms for socket timeout */
    private int socketTimeout = SearchPromoteConstants.DEFAULT_SOCKET_TIMEOUT;
    
    /**
     * Creates a new instance of a Search object.
     * 
     * @param request {@link SlingHttpServletRequest}
     * @param configuration {@link Configuration} 
     * @throws SearchPromoteException {@link SearchPromoteException} if an error occurs
     * @deprecated Use {@link com.day.cq.searchpromote.Search#Search(SlingHttpServletRequest, Configuration, HttpClientBuilderFactory)} instead.
     */
    @Deprecated
    public Search(SlingHttpServletRequest request, Configuration configuration) throws SearchPromoteException {
        this.request = request;
        this.configuration = configuration;
        try {
            this.searchFormParser = new SearchFormParser();
            this.resultsParser = new ResultParser();
        }catch(Exception e) {
            throw new SearchPromoteException(e.getMessage(), e);
        }

        consumeRequestParameters();

        if(searchForm == null) {
            parseSearchForm();
        }

        if(queryString != null) {
            parseResults();
        }
    }
    
    /**
     * Creates a new instance of a Search object.
     * 
     * @param request The {@link SlingHttpServletRequest}
     * @param configuration The {@link Configuration}
     * @param httpClientBuilderFactory The {@link HttpClientBuilderFactory}
     * @param connectionTimeout Timeout in milliseconds until a connection is
     *            established. A timeout value of 0 is interpreted as an
     *            infinite timeout.
     * @param socketTimeout Timeout in milliseconds, which is the timeout for
     *            waiting for data or a maximum period of inactivity between two
     *            consecutive data packets. A timeout value of 0 is
     *            interpreted as an infinite timeout.
     * @throws SearchPromoteException if an error occurs
     */
    public Search(SlingHttpServletRequest request, Configuration configuration, HttpClientBuilderFactory httpClientBuilderFactory, int connectionTimeout, int socketTimeout) throws SearchPromoteException {
        if (httpClientBuilderFactory == null) {
            throw new IllegalArgumentException("HttClientBuilderFactory cannot be null");
        }
        if (connectionTimeout < 0) {
            throw new IllegalArgumentException("Connection timeout value cannot be less than 0");
        }
        if (socketTimeout < 0) {
            throw new IllegalArgumentException("Socket timeout value cannot be less than 0");
        }
        
        this.request = request;
        this.configuration = configuration;
        this.httpClientBuilderFactory = httpClientBuilderFactory;
        this.connectionTimeout = connectionTimeout;
        this.socketTimeout = socketTimeout;
        
        try {
            this.searchFormParser = new SearchFormParser();
            this.resultsParser = new ResultParser();
        } catch(Exception e) {
            throw new SearchPromoteException(e.getMessage(), e);
        }
        
        consumeRequestParameters();
        
        if(searchForm == null) {
            parseSearchForm();
        }
        
        if(queryString != null) {
            parseResults();
        }
    }
    
    /**
     * Creates a new instance of a Search object.
     * 
     * @param request {@link SlingHttpServletRequest}
     * @param configuration {@link Configuration} 
     * @param httpClientBuilderFactory {@link HttpClientBuilderFactory}
     * @throws SearchPromoteException {@link SearchPromoteException} if an error occurs
     */
    public Search(SlingHttpServletRequest request, Configuration configuration, HttpClientBuilderFactory httpClientBuilderFactory) throws SearchPromoteException {
        this(request, configuration, httpClientBuilderFactory, SearchPromoteConstants.DEFAULT_CONNECTION_TIMEOUT, SearchPromoteConstants.DEFAULT_SOCKET_TIMEOUT);
    }
    
    /**
     * Returns a query parameter from the provided query string. Additional to
     * the delimiter & the Search&Promote parameter delimiters ; and / are
     * supported.
     * 
     * @param queryString queryString
     * @param parameter parameter
     * @return query parameter
     */
    public static String getQueryParameter(String queryString, String parameter) {
        if(queryString != null && !"".equals(queryString)) {
            Pattern pm = Pattern.compile(".*?" + parameter + "=([^;&/]*)?.*?");
            Matcher m = pm.matcher(queryString);
            if(m.find()) {
                return m.group(1);
            }
        }
        return null;
    }
    
    /**
     * Returns current query string.
     * 
     * @return query string
     */
    public String getQueryString() {
        return queryString;
    }
    
    /**
     * Sets query string.
     * 
     * @param query querystring
     */
    public void setQueryString(String query) {
        this.queryString = query;
    }
    
    /**
     * Returns the {@link SearchForm}.
     * 
     * @return {@link SearchForm}
     */
    public SearchForm getSearchForm() {
        return searchForm;
    }
    
    /**
     * Returns the {@link Query}.
     * 
     * @return {@link Query}
     */
    public Query getQuery() {
        if(customerResult != null) {
            return customerResult.getQuery();
        }
        return null;
    }
    
    /**
     * Returns a {@link List} of {@link BreadCrumbItem}'s.
     * 
     * @return {@link List} of {@link BreadCrumbItem}s
     */
    public List getBreadcrumbs() {
        if(customerResult != null && customerResult.getBreadCrumbList() != null) {
            for(BreadCrumb b : customerResult.getBreadCrumbList().getItems()){
                if("default".equalsIgnoreCase(b.getName())) {
                    return b.getItems();
                }
            }
        }
        return new ArrayList();
    }
    
    /**
     * Returns the {@link Pagination}.
     * 
     * @return {@link Pagination}
     */
    public Pagination getPagination() {
        if(customerResult != null) {
            return customerResult.getPagination();
        }
        return null;
    }
    
    /**
     * Returns a {@link List} of {@link Result}'s.
     * 
     * @return {@link List} of {@link Result}s.
     */
    public List getResults() {
        if(customerResult != null && customerResult.getResultList() != null) {
            for(ResultSet rs : customerResult.getResultList().getResultSets()) {
                if("default".equalsIgnoreCase(rs.getName())) {
                    return rs.getItems();
                }
            }
        }
        return new ArrayList();
    }
    
    /**
     * Returns a {@link String} containing the url to redirect to.
     * 
     * @return {@link String} containing the url to redirect to
     */
    public String getRedirect() {
        if(shouldRedirect()) {
            return customerResult.getRedirect().getTarget();
        }
        return "";
    }

    /**
     * Returns a {@link Boolean} wether the request should be redirected
     * 
     * @return {@link Boolean} wether the request should be redirected
     */
    public boolean shouldRedirect() {
        return (
          customerResult != null && 
          customerResult.getRedirect() != null && 
          customerResult.getRedirect().getTarget() != null);
    }

    /**
     * Returns a {@link Suggestions} object.
     * 
     * @return {@link Suggestions} object
     */
    public Suggestions getSuggestion() {
        if(customerResult != null) {
            return customerResult.getSuggestions();
        }
        return null;
    }
    
    /**
     * Returns a {@link List} of {@link Suggestion}'s.
     * 
     * @return {@link List} of {@link Suggestion}s
     */
    public List getSuggestions() {
        if(customerResult != null && customerResult.getSuggestions() != null) {
            return customerResult.getSuggestions().getItems();
        }
        return new ArrayList();
    }
    
    /**
     * Returns a {@link List} of {@link Facet}'s.
     * 
     * @return {@link List} of {@link Facet}s.
     */
    public List getFacets() {
        if(customerResult != null && customerResult.getFacetList() != null) {
            return customerResult.getFacetList().getFacets();
        }
        return new ArrayList();
    }
    
    /**
     * Returns the {@link Facet} with the specified name or
     * null if it could not be found.
     * 
     * @param name facet name
     * @return {@link Facet} with specified name
     */
    public Facet getFacet(String name) {
        for(Facet facet : getFacets()) {
            if(name.equals(facet.getTitle().trim())) {
                return facet;
            }
        }
        return null;
    }
    
    /**
     * Returns a {@link List} of {@link Banner}s.
     * 
     * @return {@link List} of {@link Banner}s
     * 
     */
    public List getBanners() {
        if(customerResult != null && customerResult.getBannerList() != null) {
            return customerResult.getBannerList().getBanners();
        }
        return new ArrayList();
    }
    
    /**
     * Returns the {@link Banner} for the specified bannerArea or
     * null if it could not be found.
     * 
     * @param bannerArea bannerArea
     * @return {@link Banner}
     */
    public Banner getBanner(String bannerArea) {
        for(Banner banner: getBanners()){
            if(banner.getArea().equals(bannerArea)){
                return banner;
            }
        }
        return null;
    }
    
    
    /**
     * Returns a {@link List} of {@link Menu}s.
     * 
     * @return list of menus
     */
    public List getMenus() {
        if(customerResult != null && customerResult.getMenuList() != null) {
            return customerResult.getMenuList().getMenus();
        }
        return new ArrayList();
    }
    
    /**
     * Returns the {@link Menu} for the specified menuName or
     * null if it could not be found.
     * 
     * @param menuName menuName
     * @return {@link Menu}
     */
    public Menu getMenu(String menuName) {
        for(Menu menu: getMenus()){
            if(menu.getName().equals(menuName)){
                return menu;
            }
        }
        return null;
    }
    
    
    /**
     * Returns query execution time. The execution time is built on the sum of
     * request time and parsing time.
     * 
     * @return Execution time in milliseconds
     */
    public Long getExecutionTime() {
        return requestTime + parsingTime;
    }
    
    /**
     * Consumes the query string of the request and sets the query string. The
     * query string is considered valid if it contains a parameter with name 'q'
     * which is not empty. If the query string does not contain the parameter
     * 'view=xml' it will be added with the provided delimiter. As default
     * delimiter ; is used.
     */
    private void consumeRequestParameters() {
        try {
            String q = request.getQueryString();
            if(isValidQueryString(q)) {
                String query = new String(q.getBytes("ISO-8859-1"), "UTF-8");
                query = clearQuery(query);
                setQueryString(query);
            }
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }     
    }
    
    /**
     * Parses the search form XML from the property PN_SEARCHFORM.
     * Before parsing the XML is unescaped.
     * 
     * @throws SearchPromoteException {@link SearchPromoteException}
     */
    private void parseSearchForm() throws SearchPromoteException {
        try {
            String searchformXml = configuration.get(PN_SEARCHFORMXML, null);
            if(searchformXml == null) {
                throw new SearchPromoteException("Search&Promote configuration is missing the SearchForm XML");
            }
            handleXMLResponse(searchformXml);
            searchformXml = StringEscapeUtils.unescapeXml(searchformXml);
            searchForm = searchFormParser.parse(new InputSource(new StringReader(searchformXml)));
            searchForm.setRequest(request);
        } catch(Exception e) {
            log.error(e.getMessage(), e);
            throw new SearchPromoteException(e.getMessage(), e);
        }
    }
    
    /**
     * Parses the results XML.
     * 
     * @throws SearchPromoteException {@link SearchPromoteException}
     */
    private void parseResults() throws SearchPromoteException {
        try {
            final Long startRequest = System.currentTimeMillis();
            String resultsXml = getResultsXML();
            requestTime = System.currentTimeMillis() - startRequest;
            log.debug(resultsXml);
            handleXMLResponse(resultsXml);
            
            final Long startParse = System.currentTimeMillis();
            customerResult = resultsParser.parse(resultsXml);
            parsingTime = System.currentTimeMillis() - startParse;
        } catch(SearchPromoteException e) {
            throw e;
        } catch(Exception e) {
            throw new SearchPromoteException(e.getMessage(), e);
        }
    }
    
    /**
     * Checks response {@link String} for HTML tag and input field named
     * sp_password. In case of invalid credentials Search&Promote redirects to
     * log in page.
     * 
     * @param responseXML responseXML
     * @throws SearchPromoteException  {@link SearchPromoteException}
     */
    private void handleXMLResponse(String responseXML) throws SearchPromoteException {
        if(responseXML == null || responseXML.contains("" + 
                              "";
                      }
                  }
            } else if (status != HttpStatus.SC_OK) {
                return null;
            } else {
                return method.getResponseBodyAsString();
            }
        }catch(UnknownHostException e) {
            throw new SearchPromoteException("Unknown host: " + e.getMessage(), e);  
        }catch(IOException e) {
            throw new SearchPromoteException(e.getMessage(), e);
        }finally{
            method.releaseConnection();
        }
    }
    
    /**
     * Executes the provided {@link HttpMethod}.
     * 
     * @param method {@link HttpUriRequest} HTTP request to execute
     * @return XML response
     * @throws SearchPromoteException {@link SearchPromoteException} if an error occurs
     */
    private String executeMethod(HttpUriRequest method) throws SearchPromoteException {
        CloseableHttpClient client = null;
        try {
            RequestConfig config = RequestConfig.custom()
                    .setConnectTimeout(connectionTimeout)
                    .setConnectionRequestTimeout(connectionTimeout)
                    .setSocketTimeout(socketTimeout)
                    .build();

            client = httpClientBuilderFactory.newBuilder().setDefaultRequestConfig(config).disableRedirectHandling().build();

            CloseableHttpResponse response = client.execute(method);
            StatusLine statusLine = response != null ? response.getStatusLine() : null;

            int status = statusLine != null ? statusLine.getStatusCode() : -1;
            if ((status == HttpStatus.SC_MOVED_PERMANENTLY) ||   /* 301 */
                (status == HttpStatus.SC_MOVED_TEMPORARILY) ||   /* 302 */
                (status == HttpStatus.SC_SEE_OTHER) ||           /* 303 */
                (status == HttpStatus.SC_TEMPORARY_REDIRECT)) {  /* 307 */

                  HeaderIterator headerIt = response != null ? response.headerIterator("location") : null;
                  if (headerIt == null || !headerIt.hasNext()) {
                      return null;
                  }

                  Header header = headerIt.nextHeader();
                  String redirectUri = header.getValue();
                  if ((redirectUri == null) || (redirectUri.equals(""))) {
                      return null;
                  } else {
                      return 
                          "" + 
                          "";
                  }
            } else if (status != HttpStatus.SC_OK) {
                return null;
            } else {
                HttpEntity entity = response != null ? response.getEntity() : null;
                if (entity != null) {
                    return EntityUtils.toString(entity);
                }
                return null;
            }
        } catch (UnknownHostException e) {
            throw new SearchPromoteException("Unknown host: " + e.getMessage(), e);  
        } catch (IOException e) {
            throw new SearchPromoteException(e.getMessage(), e);
        } finally {
            try {
                if (client != null) {
                    client.close();
                }
            } catch (IOException e) {
                log.error("Failed to close HTTP connection with the Search&Promote Server.", e);
            }
        }
    }
    
    /**
     * Checks if query is valid. It is considered valid if it is not
     * null, not an empty string and matches the
     * VALID_QUERY_PATTERN.
     * 
     * @param query query
     * @return true if valid
     */
    private Boolean isValidQueryString(String query) {
        if(query != null && !"".equals(query) 
                && query.matches(VALID_QUERY_PATTERN) ) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }
    
    /**
     * Clears the query string.
     * 
     * 
  • encodes pipes (used joining facets)
  • *
  • adds 'view=xml' if necessary.
  • * * @param query query * @return cleared query */ private String clearQuery(String query) { String cleared = query; String delim = getDelimeter(query); if(cleared.contains("|")) { cleared = cleared.replaceAll("\\|", "%7C"); } if(!cleared.contains("view=")){ cleared += delim + "view=xml"; } return cleared; } /** * Evaluates the used delimiter. In Search&Promote the following chars can be used as query delimiters. *
  • ; (default)
  • *
  • &
  • *
  • /
  • * @param query query * @return Delimiter */ private String getDelimeter(String query) { String delim = ";"; if(query.indexOf('&') > -1) { delim = "&"; }else if(query.indexOf('/')>-1) { delim = "/"; } return delim; } }




    © 2015 - 2025 Weber Informatics LLC | Privacy Policy