com.esri.gpt.catalog.search.SearchEngineRest.java Source code

Java tutorial

Introduction

Here is the source code for com.esri.gpt.catalog.search.SearchEngineRest.java

Source

/* See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Esri Inc. 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 com.esri.gpt.catalog.search;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.esri.gpt.framework.collection.StringSet;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.http.CredentialProvider;
import com.esri.gpt.framework.http.HttpClientRequest;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.search.SearchXslProfile;
import com.esri.gpt.framework.search.SearchXslRecord;
import com.esri.gpt.framework.util.ResourcePath;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.framework.xml.DomUtil;
import com.esri.gpt.framework.xml.XmlIoUtil;

/**
 * The class SearchEngineCSW. Accesses a CSW repository to send searTch 
 * and parse back results.  works closely with the csw client jar.
 * 
 */
public class SearchEngineRest extends ASearchEngine {

    // class variables =============================================================
    /** The Class Logger. */
    private static final Logger LOG = Logger.getLogger(SearchEngineRest.class.getCanonicalName());

    /** The Constant DELIMETER key value pair */
    public final static String DELIMETER_KVP = "\u2715";

    /** The Constant DELIMETER_VALUES between key value*/
    public final static String DELIMETER_KV = "\u2714";

    /** Xsl Param opensearch URL **/
    private static final String XSL_PARAM_OPENSEARCH_URL = "searchQueryUrl";

    /** The Constant JSFBEAN_SEARCH_CONTROLLER. */
    private static final String JSFBEAN_SEARCH_CONTROLLER = "SearchController";

    // instance variables ==========================================================
    /** The search xsl req res. */

    /** The get record by id kvpurl. */
    private String recordByIdKvpurl = null;

    /** The profile id *. */
    private String profileId = "";

    /** The end point url. */
    private String endPointSearchUrl;

    /** The end point metadata url. */
    private String endPointMetadataUrl;

    /** The xsl profile. */
    private SearchGptXslProfiles xslProfiles;

    /** The default search text. */
    private String defaultParamValues;

    /** The replace url values. */
    private String replaceUrlValues;

    // constructor =================================================================

    private SearchEngineRest() {

    }

    /**
     * Instantiates a new search engine rest.
     * 
     * @param context the context
     */
    public SearchEngineRest(RequestContext context) {
        super(context);
    }
    // properties ==================================================================

    /**
     * Sets the default param values.
     * 
     * @param defaultParamValues the new default param values
     */
    public void setDefaultParamValues(String defaultParamValues) {
        this.defaultParamValues = defaultParamValues;
    }

    /**
     * Gets the end point url.
     * 
     * @return the end point url (trimmed, never null)
     */
    public String getEndPointSearchUrl() {
        return Val.chkStr(endPointSearchUrl);
    }

    /**
     * Sets the end point url.
     * 
     * @param endPointUrl the new end point url
     */
    public void setEndPointSearchUrl(String endPointUrl) {
        this.endPointSearchUrl = endPointUrl;
    }

    /**
     * Gets the profile id.
     * 
     * @return the profile id (trimmed, never null)
     */
    public String getProfileId() {
        return Val.chkStr(profileId);
    }

    /**
     * Sets the profile id.
     * 
     * @param profileId the new profile id
     */
    public void setProfileId(String profileId) {
        this.profileId = profileId;
    }

    /**
     * Gets the record by id kvpurl.
     * 
     * @return the record by id kvpurl (trimmed, never null)
     * @throws SearchException 
     */
    public String getRecordByIdKvpurl() throws SearchException {
        if ("".equals(Val.chkStr(recordByIdKvpurl))) {
            return Val.chkStr(this.readXslProfile().getKvp());
        }
        return Val.chkStr(recordByIdKvpurl);
    }

    /**
     * Sets the record by id kvpurl.
     * 
     * @param recordByIdKvpurl the new record by id kvpurl
     */
    public void setRecordByIdKvpurl(String recordByIdKvpurl) {
        this.recordByIdKvpurl = recordByIdKvpurl;
    }

    /**
     * Sets the replace url values.
     * 
     * @param replaceUrlValues the new replace url values
     */
    public void setReplaceUrlValues(String replaceUrlValues) {
        this.replaceUrlValues = replaceUrlValues;
    }

    /**
     * Gets the xsl profiles.
     * 
     * @return the xsl profiles
     * @throws SearchException the search exception
     */
    public SearchGptXslProfiles getXslProfiles() throws SearchException {
        if (xslProfiles == null) {
            xslProfiles = SearchConfig.getConfiguredInstance().getGptXslProfiles();
        }
        return xslProfiles;
    }

    /**
     * Sets the xsl profiles.
     * 
     * @param xslProfiles the new xsl profiles
     */
    public void setXslProfiles(SearchGptXslProfiles xslProfiles) {
        this.xslProfiles = xslProfiles;
    }

    /**
     * Gets the end point metadata url.
     * 
     * @return the end point metadata url (trimmed, never null)
     */
    public String getEndPointMetadataUrl() {
        return Val.chkStr(endPointMetadataUrl);
    }

    /**
     * Sets the end point metadata url.
     * 
     * @param endPointMetadataUrl the new end point metadata url
     */
    public void setEndPointMetadataUrl(String endPointMetadataUrl) {
        this.endPointMetadataUrl = endPointMetadataUrl;
    }

    /**
     * Gets the metadata url for record.
     * 
     * @param uuid the uuid
     * 
     * @return the metadata url
     * 
     * @throws SearchException the search exception
     */
    @Override
    public String getMetadataUrl(String uuid) throws SearchException {
        return this.getRecordByIdKvpurl().replaceFirst("\\{id\\}", uuid);
    }

    /**
     * Get the connection uri
     * 
     * @return uri representing the search
     */
    @Override
    public URI getConnectionUri() throws SearchException {
        Exception ex = null;
        try {
            return new URL(this.getSearchQuery()).toURI();
        } catch (MalformedURLException e) {
            ex = e;
        } catch (SearchException e) {
            ex = e;
        } catch (URISyntaxException e) {
            ex = e;
        }
        throw new SearchException("Could not get the connection uri", ex);

    }

    // methods =====================================================================
    /* 
     * Sets items that may have been handed from the factory (from gpt.xml)
     * <br/>
     * Assigns value of endPointUrl to setEndPointUrl
     * <br/>
     * Assigns profileId to setProfileId
     * 
     * Uses attributes connectionUrl, profileId, metadataUrl attributes for 
     * searchengine in gpt.xml
     * 
     *
     * @see com.esri.gpt.catalog.search.ASearchEngine#init()
     */
    /*@Override
    public void init() throws SearchException {
      super.init();
      this.class.
      Map<String, String> factoryAttributes =  this.getFactoryAttributes();
      if(factoryAttributes == null) {
        return;  
      }
      Object obj = factoryAttributes.get(ATTRIBUTE_CONNECTION_URL);
      if(obj != null) {
        this.setEndPointSearchUrl(obj.toString());
        LOG.finer("Init Connection url: " + obj.toString());
      }
      obj = factoryAttributes.get(ATTRIBUTE_PROFILE_ID);
      if(obj != null) {
        this.setProfileId(obj.toString());
        LOG.finer("Init Profile id: " + obj.toString());
      }
      obj = factoryAttributes.get(ATTRIBUTE_METADATA_URL);
      if(obj != null) {
        this.setEndPointMetadataUrl(obj.toString());
        LOG.finer("Init Metadata Profile id: " + obj.toString());
      }
      obj = factoryAttributes.get(ATTRIBUTE_DEFAULT_PARAMVALS);
      if(obj != null) {
        this.setDefaultParamValues(obj.toString());
        LOG.finer("Init Default Param values: " + obj.toString());
      }
    }*/

    /* 
     * Does a search
     * 
     * @param hitsOnly Only the hits
     * @see com.esri.gpt.catalog.search.ASearchEngine#doSearch()
     */
    @Override
    public void doSearch() throws SearchException {
        Exception ex = null;

        try {
            URI uri = this.getConnectionUri();
            URL url = uri.toURL();
            HttpClientRequest clientRequest = HttpClientRequest.newRequest(HttpClientRequest.MethodName.GET,
                    url.toExternalForm());
            clientRequest.setConnectionTimeMs(getConnectionTimeoutMs());
            clientRequest.setResponseTimeOutMs(getResponseTimeoutMs());

            Map map = (Map) this.getRequestContext().extractFromSession(SEARCH_CREDENTIAL_MAP);
            if (map != null) {
                CredentialProvider credProvider = (CredentialProvider) map.get(this.getKey());
                if (credProvider != null) {
                    clientRequest.setCredentialProvider(credProvider);
                }
            }

            clientRequest.execute();
            String response = clientRequest.readResponseAsCharacters();
            InputStream is = null;
            try {
                SearchXslProfile profile = this.readXslProfile();
                String js = Val.chkStr(profile.getResponsexslt());
                //String js = Val.chkStr(this.getFactoryAttributes().get("searchResponseJsT"));
                String xml = null;
                if (js.toLowerCase().endsWith(".js")) {
                    try {
                        ResourcePath rPath = new ResourcePath();
                        URL fileUrl = rPath.makeUrl(js);
                        is = fileUrl.openStream();
                        String jsTransFile = IOUtils.toString(is, "UTF-8");
                        jsTransFile = "var jsGptInput =" + response + ";" + jsTransFile;
                        HttpServletRequest servletRequest = (HttpServletRequest) this.getRequestContext()
                                .getServletRequest();
                        if (servletRequest != null) {
                            jsTransFile = "var jsGptQueryString = '" + servletRequest.getQueryString() + "';"
                                    + jsTransFile;
                        }
                        jsTransFile = "var jsGptEndpointSearchQuery = '" + url.toExternalForm() + "';"
                                + jsTransFile;
                        ScriptEngineManager manager = new ScriptEngineManager();
                        ScriptEngine engine = manager.getEngineByName("JavaScript");
                        //manager.put("jsGptInput", response);
                        Object obj = engine.eval(jsTransFile);
                        xml = obj.toString();
                        parseResponse(xml);// has to work before the finally. dont move
                    } catch (Exception e) {
                        throw new SearchException(
                                e.getMessage() + ":" + "Error when doing transformation from javascript", e);
                    }
                } else {
                    xml = XmlIoUtil.jsonToXml(response, "gptJsonXml");
                    parseResponse(xml);
                }

                checkPagination();
            } catch (SearchException e) {
                throw e;
            } catch (Exception e) {
                parseResponse(response);
                checkPagination();
            } finally {
                if (is != null) {
                    IOUtils.closeQuietly(is);
                }
            }

        } catch (MalformedURLException e) {
            ex = e;
        } catch (IOException e) {
            ex = e;
        } finally {

        }
        if (ex != null) {
            throw new SearchException(ex.getMessage() + ": Could not perform search", ex);
        }
    }

    /**
     * Parses the response.
     * 
     * @param xml the xml
     * @throws SearchException 
     */
    @SuppressWarnings("unchecked")
    public void parseResponse(String xml) throws SearchException {

        SearchResult searchResult = new SearchResult();
        SearchResultRecords searchResultRecords = this.getRequestDefinition().getResult().getRecords();
        try {
            this.readXslProfile().readGetRecordsResponse(xml, searchResult);
            this.getRequestDefinition().getResult().setMaxQueryHits(searchResult.getMaxQueryHits());
            SearchResultRecords records = searchResult.getRecords();

            HttpServletRequest servletRequest = (HttpServletRequest) this.getRequestContext().getServletRequest();
            if (servletRequest != null) {
                String queryString = Val.chkStr(servletRequest.getQueryString());
                if (records.size() > searchResult.getPageCursor().getRecordsPerPage()) {
                    // must be one of those endpoints that do not have a number

                }
            }
            Iterator iter = records.iterator();
            while (iter.hasNext()) {

                Object obj = iter.next();

                if (obj instanceof SearchResultRecord) {
                    SearchResultRecord searchResultRecord = (SearchResultRecord) obj;
                    searchResultRecord.setExternal(this.readIsExternalSearch());
                    searchResultRecord.setExternalId(this.getKey());
                    searchResultRecords.add(searchResultRecord);
                } else if (obj instanceof SearchXslRecord) {
                    SearchResultRecord searchResultRecord = ((SearchXslRecord) obj).readAsSearchResult(
                            this.getResourceLinkBuilder(), this.readIsExternalSearch(), this.getKey());
                    searchResultRecords.add(searchResultRecord);
                } else {
                    throw new SearchException(
                            "Did not understand the parsed object " + obj.getClass().getCanonicalName());
                }
            }
        } catch (Exception e) {
            throw new SearchException(e.getMessage() + ":" + "Could not parse getRecordsResponse", e);
        }

    }

    private SearchXslRecord getMetadataAsObjectX(String uuid) throws SearchException {
        SearchResultRecord record = new SearchResultRecord();
        String metaUrl = this.getMetadataUrl(uuid);
        SearchXslRecord recordX = null;

        URL url = null;
        Exception ex = null;
        try {
            url = new URL(metaUrl);

            HttpClientRequest clientRequest = HttpClientRequest.newRequest(HttpClientRequest.MethodName.GET,
                    url.toExternalForm());
            clientRequest.setConnectionTimeMs(getConnectionTimeoutMs());
            clientRequest.setResponseTimeOutMs(getResponseTimeoutMs());
            clientRequest.execute();
            String response = clientRequest.readResponseAsCharacters();
            LOG.log(Level.FINER, "Response from get Metadata url = {0}\n response = \n{1}",
                    new Object[] { url.toExternalForm(), response });
            recordX = new SearchXslRecord();
            this.readXslProfile().readGetMetadataByIDResponse(response, recordX);
        } catch (MalformedURLException e) {
            ex = e;
        } catch (IOException e) {
            ex = e;
        } catch (TransformerException e) {
            ex = e;
        }
        if (ex != null) {
            throw new SearchException("Could not get metadata id url = " + url, ex);
        }
        return recordX;
    }

    /**
     * Gets the metadata as object.
     * 
     * @param uuid the uuid
     * @return the metadata as object
     * @throws SearchException the search exception
     * @see com.esri.gpt.catalog.search.ASearchEngine#getMetadataAsSearchResultRecord(java.lang.String)
     */
    @Override
    public SearchResultRecord getMetadataAsSearchResultRecord(String uuid) throws SearchException {
        return this.getMetadataAsObjectX(uuid).readAsSearchResult(this.getResourceLinkBuilder(),
                this.readIsExternalSearch(), this.getKey());
    }

    /**
     * Gets the metadata as text.
     * 
     * @param uuid the uuid
     * @return the metadata as text
     * @throws SearchException the search exception
     * @see com.esri.gpt.catalog.search.ASearchEngine#getMetadataAsText(java.lang.String)
     */
    @Override
    public String getMetadataAsText(String uuid) throws SearchException {
        this.getMetadataAsObjectX(uuid).getFullMetadata();
        return null;
    }

    /**
     * Gets the default param values as map.
     * 
     * @return the default param values (never null)
     */
    public Map<String, String> readDefaultParamValues() {
        return readParamValues(Val.chkStr(this.defaultParamValues));
    }

    /**
     * Read replace param values as map.
     * 
     * @return the map
     */
    public Map<String, String> readReplaceParamValues() {
        return readParamValues(Val.chkStr(this.replaceUrlValues));
    }

    /**
     * Read param values as maps.
     * 
     * @param param the param
     * @return the map
     */
    private Map<String, String> readParamValues(String param) {
        Map<String, String> defVals = new HashMap<String, String>();
        String vals = Val.chkStr(defaultParamValues);
        String kvp[] = vals.split(DELIMETER_KVP);
        for (int i = 0; kvp != null && i < kvp.length; i++) {
            String kv[] = (kvp[i]).split(DELIMETER_KV);
            if (kv == null) {
                continue;
            }
            String key = "";
            String value = "";
            if (kv.length < 1) {
                continue;
            }
            key = Val.chkStr(kv[0]);
            if ("".equals(kv)) {
                continue;
            }
            if (kv.length > 1) {
                value = Val.chkStr(kv[1]);
            }
            defVals.put(key, value);
        }
        return defVals;
    }

    /**
     * Read xsl profile.
     * 
     * @return the search xsl profile
     * @throws SearchException the search exception
     */
    @SuppressWarnings("unchecked")
    public SearchXslProfile readXslProfile() throws SearchException {
        return this.getXslProfiles().getProfileById(this.getProfileId());

    }

    /**
     * Gets the search query.
     * 
     * @return the search query
     * @throws SearchException the search exception
     */
    @SuppressWarnings("unchecked")
    @Override
    public String getSearchQuery() throws SearchException {
        String searchQuery = null;
        Exception ex = null;
        Map<String, String> map = this.getFactoryAttributes();
        map.put(XSL_PARAM_OPENSEARCH_URL, this.getEndPointSearchUrl());

        try {
            searchQuery = this.readXslProfile().generateGetRecordsRequest(this.getRequestDefinition().getCriteria(),
                    map, this.getHitsOnly());
            String searchEndPoint = this.getEndPointSearchUrl();
            // assign values from the xslt
            Document doc = DomUtil.makeDomFromString(searchQuery, false);
            NodeList paramList = doc.getElementsByTagName("parameter");
            for (int i = 0; i < paramList.getLength(); i++) {
                Node paramNode = paramList.item(i);
                String key = paramNode.getAttributes().getNamedItem("key").getNodeValue();
                String value = paramNode.getAttributes().getNamedItem("value").getNodeValue();

                String paramValue = this.getFactoryAttributes().get(key);
                if (paramValue != null) {
                    String paramValues[] = paramValue.split(DELIMETER_KVP);
                    for (int j = 0; j < paramValues.length; j++) {
                        String paramValueValues[] = Val.chkStr(paramValues[j]).split(DELIMETER_KV);
                        if (paramValueValues.length < 2) {
                            continue;
                        }
                        if (Val.chkStr(paramValueValues[0]).equalsIgnoreCase(value)) {
                            value = paramValueValues[1];
                        }
                    }
                }
                key = key.replaceAll("([\\\\*+\\[\\](){}\\$.?\\^|])", "\\\\$1");
                searchEndPoint = searchEndPoint.replaceAll("(?i)" + key, URLEncoder.encode(value, "UTF-8"));
            }

            // assign default values input
            Map<String, String> paramVals = this.readDefaultParamValues();
            Iterator<String> keyIter = paramVals.keySet().iterator();
            while (keyIter.hasNext()) {
                String key = keyIter.next();
                if (searchEndPoint.contains(key + "=&") || searchEndPoint.endsWith(key + "=")) {
                    searchEndPoint = searchEndPoint.replaceAll(key + "=",
                            key + "=" + URLEncoder.encode(paramVals.get(key), "UTF-8"));
                }
            }

            // replace sections of the url
            paramVals = this.readReplaceParamValues();
            keyIter = paramVals.keySet().iterator();
            while (keyIter.hasNext()) {
                String key = Val.chkStr(keyIter.next());
                String value = Val.chkStr(paramVals.get(key));
                searchEndPoint.replaceAll(key, value);
            }

            LOG.log(Level.FINER, "Search Query: {0}", searchEndPoint);
            return searchEndPoint;
        } catch (XPathExpressionException e) {
            ex = e;
        } catch (TransformerException e) {
            ex = e;
        } catch (ParserConfigurationException e) {
            ex = e;
        } catch (SAXException e) {
            ex = e;
        } catch (IOException e) {
            ex = e;
        }
        if (ex == null) {
            throw new SearchException("Error when generating search query", ex);
        }

        return searchQuery;

    }

    /**
     * Read is external search.
     * 
     * @return true, if successful
     * @see com.esri.gpt.catalog.search.ASearchEngine#readIsExternalSearch()
     */
    @Override
    protected boolean readIsExternalSearch() {
        return true;
    }

    /** 
     * 
     * Creates instances of this search engines from the rids
     *@param rids
     *@return mapping between rids and search engines instances
     *@throws SearchException
     */
    @Override
    public Map<String, Object> createInstances(StringSet rids) throws SearchException {

        Map<String, Object> mapRid2Engine = new HashMap<String, Object>();
        for (String rid : rids) {
            SearchEngineRest searchEngineRest = new SearchEngineRest();
            searchEngineRest.setKey(rid);
            mapRid2Engine.put(rid, searchEngineRest);
        }
        return mapRid2Engine;
    }

    /**
     * Gets the abstract associated with the key
     * 
     * @return the abstract
     * @throws SearchException
     */
    @Override
    public String getKeyAbstract() throws SearchException {

        Map<String, String> map = this.getFactoryAttributes();
        String absKey = null;
        if (map != null) {
            absKey = map.get("abstractResourceKey");
        }

        MessageBroker bundle = new MessageBroker();
        bundle.setBundleBaseName(MessageBroker.DEFAULT_BUNDLE_BASE_NAME);
        return bundle.retrieveMessage(absKey);
    }

}