/* 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.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; 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.jsf.MessageBroker; import com.esri.gpt.framework.request.IRequest; import com.esri.gpt.framework.request.PageCursor; import com.esri.gpt.framework.security.credentials.UsernamePasswordCredentials; import com.esri.gpt.framework.util.Val; import com.esri.gpt.server.csw.client.CswRecord; import java.util.Date; /** * Defines operations to be carried out by the search * end point. */ public abstract class ASearchEngine implements IRequest<SearchRequestDefinition> { // class variables ============================================================== /** Addtribute for connection timeout*. */ private static final String ATTRIBUTE_TIMEOUT_CONNECTION = "connectionTimeout"; /** Attribute for connection response timeout *. */ private static final String ATTRIBUTE_TIMEOUT_RESPONSE = "responseTimeout"; /** The Constant ATTRIBUTE_ABSTRACT_KEY. */ private static final String ATTRIBUTE_ABSTRACT_KEY = ""; public static final String SEARCH_CREDENTIAL_MAP = "SEARCH_CREDENTIAL_MAP"; /** class logger *. */ private static final Logger LOG = Logger.getLogger(ASearchEngine.class.getCanonicalName()); // instance variables ========================================================== /** The connection URI. */ private URI connectionUri; /** The search time in milliseconds. */ private Long searchTimeInMilis = Long.MIN_VALUE; /** The request definition. */ private SearchRequestDefinition requestDefinition; /** The request context. */ private RequestContext requestContext; /** The resource link builder. */ private ResourceLinkBuilder resourceLinkBuilder; /** key that was used to find the searchengine. */ private String key; /** Username Password *. */ private UsernamePasswordCredentials credentials; /** The factory attributes. */ private Map<String, String> factoryAttributes; /** The message broker. */ private MessageBroker messageBroker; /** The connection timeout. */ private int connectionTimeout; /** The response timeout. */ private int responseTimeout; /** The hits only. */ private boolean hitsOnly; //Constructors ================================================================== /** Default Constructor. SearchFactory can instantiate this constructor.*/ public ASearchEngine(){ } /** * Overloaded Constructor. SearchFactory can instantiate this constructor. * @param context the request context */ public ASearchEngine(RequestContext context){ this.requestContext = context; } // properties ================================================================== /** * Gets the hits only. * * @return the hits only */ public boolean getHitsOnly() { return hitsOnly; } /** * Sets the hits only. * * @param hitsOnly the new hits only */ public void setHitsOnly(boolean hitsOnly) { this.hitsOnly = hitsOnly; } /** * Gets the resource link builder. * * @return the resource link builder (never null) */ public ResourceLinkBuilder getResourceLinkBuilder() { return resourceLinkBuilder; } /** * Sets the resource link builder. * * @param resourceLinkBuilder the new resource link builder */ public void setResourceLinkBuilder(ResourceLinkBuilder resourceLinkBuilder) { this.resourceLinkBuilder = resourceLinkBuilder; } /** * Gets the connection uri. If connection uri is null, the URI in the * criteria will be used. * * @return the connection uri (possibly null) * @throws SearchException if uri from criteria is invalid */ public URI getConnectionUri() throws SearchException { if(connectionUri == null) { return this.getRequestDefinition().getCriteria().getSearchUri(); } return connectionUri; } /** * Sets the connection uri. If connection uri is null, the URI in the * criteria will be used. * * @param connectionUri the new connection uri */ public void setConnectionUri(URI connectionUri) { this.connectionUri = connectionUri; } /** * Gets the request context. * * @return the request context (Could be null) */ public RequestContext getRequestContext() { return requestContext; } /** * Sets the request context. * * @param context the new request context */ public void setRequestContext(RequestContext context) { this.requestContext = context; } /** * Gets the abstract text connected with the key. * * @return the abstract hint * @throws SearchException the search exception */ public String getKeyAbstract() throws SearchException { return ""; } /** * Gets the key associated with the engine. * * @return the hint (trimmed, never null) */ public String getKey() { return Val.chkStr(key); } /** * Sets the key associated with the key. * * @param key the key * * @throws SearchException the search exception (If error) */ public void setKey(String key) throws SearchException{ this.key = key; } /** * Gets the credentials. * * @return the credentials */ public UsernamePasswordCredentials getCredentials() { if(credentials == null) { credentials = new UsernamePasswordCredentials(); Map<String, CredentialProvider> credMap = (Map<String, CredentialProvider>) this.getRequestContext().extractFromSession( ASearchEngine.SEARCH_CREDENTIAL_MAP); if(credMap != null) { CredentialProvider credProvider = (CredentialProvider) credMap.get(this.getKey()); if(credProvider != null) { credentials.setUsername(credProvider.getUsername()); credentials.setPassword(credProvider.getPassword()); } } } return credentials; } /** * Sets the credentials. * * @param credentials the new credentials */ public void setCredentials(UsernamePasswordCredentials credentials) { this.credentials = credentials; } /** * Gets the connection timeout in milliseconds. * * @return the connection timeout */ public int getConnectionTimeoutMs() { return connectionTimeout; } /** * Sets the connection timeout in milliseconds. * * @param connectionTimeout the new connection timeout */ public void setConnectionTimeoutMs(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } /** * Gets the response timeout in milliseconds. * * @return the response timeout */ public int getResponseTimeoutMs() { return responseTimeout; } /** * Sets the response timeout. * * @param responseTimeout the new response timeout */ public void setResponseTimeout(int responseTimeout) { this.responseTimeout = responseTimeout; } // methods ===================================================================== /** * Do search. Should update the requestDefinition.searchResult and * requestDefinition.searchResult.pageCursor * * @throws SearchException Exceptions from performing a search */ public abstract void doSearch() throws SearchException; /** * Gets record. * @param uuid UUID * @return record * @throws SearchException if searching fails */ public abstract ARecord getARecord(String uuid) throws SearchException; /** * Gets the metadata as text. * * @param uuid the uuid of the metadata * * @return the metadata as text * * @throws SearchException Exception from retrieving the document */ public abstract String getMetadataAsText(String uuid) throws SearchException; /** * Gets the metadata as object. * * * @param uuid the uuid of the metadata * * @return the metadata as object * * @throws SearchException the search exception */ public abstract SearchResultRecord getMetadataAsSearchResultRecord(String uuid) throws SearchException; /** * Gets the time in seconds. * * @return the time in seconds */ public double getTimeInSeconds() { return this.getSearchTimeInMillis().doubleValue() / 1000.0; } /** * Gets the search time in millis. * * @return the search time in millis */ public Long getSearchTimeInMillis() { return this.searchTimeInMilis; } /** * Sets the search time in millis. * * @param time the new search time in millis */ protected void setSearchTimeInMillis(Long time) { this.searchTimeInMilis = time; } /** * Gets the search query as a string representation. Should be the * native query translated from the criteria. * * @return the search query * @throws SearchException the search exception */ public abstract String getSearchQuery() throws SearchException; /** * Gets the request definition. * * @return the request definition (possibly null) */ public SearchRequestDefinition getRequestDefinition() { return requestDefinition; } /** * Sets the request definition. * * @param requestDefinition the new request definition */ public void setRequestDefinition(SearchRequestDefinition requestDefinition) { this.requestDefinition = requestDefinition; } /** * Gets the metadata url for record. * * @param uuid the uuid * * @return the metadata url * * @throws SearchException the search exception */ public String getMetadataUrl(String uuid) throws SearchException { return ""; } /** * Inits the Object after you have set the properties. * * Sets the attribute timeout connection and the attribute response timeout * * @throws SearchExcpetion the search excpetion * @throws SearchException the search exception */ @SuppressWarnings("unchecked") public void init() throws SearchException { Class c = this.getClass(); Method methods[] = c.getDeclaredMethods(); for(int i = 0; i < methods.length; i++) { Method method = methods[i]; String setName = methods[i].getName(); if(!setName.startsWith("set")) { continue; } if(setName.length() < 4) { continue; } String attributeName = setName.substring(3); Object obj = factoryAttributes.get(attributeName); if(obj == null) { continue; } Class paramClass[] = method.getParameterTypes(); Exception ex = null; try { if(paramClass[0].isInstance(new String())) { method.invoke(this, new Object[]{(String) obj}); } else if(paramClass[0].isInstance(new Integer(0))) { method.invoke(this, new Object[]{(Integer) obj}); } else { method.invoke(this, new Object[]{obj}); } } catch (IllegalArgumentException e) { ex = e; } catch (IllegalAccessException e) { ex = e; } catch (InvocationTargetException e) { ex = e; } if(ex != null) { throw new SearchException("Engine not intialized well with method " + setName, ex); } } /*Map<String, String> factoryAttributes = this.getFactoryAttributes(); if(factoryAttributes == null) { return; } Object obj = factoryAttributes.get(ATTRIBUTE_TIMEOUT_CONNECTION); if(obj != null) { this.setConnectionTimeout(Val.chkInt(obj.toString(), 0)); LOG.finer("Connection timeout : " + obj.toString()); } obj = factoryAttributes.get(ATTRIBUTE_TIMEOUT_RESPONSE); if(obj != null) { this.setResponseTimeout(Val.chkInt(obj.toString(), 0)); LOG.finer("Connection timeout response : " + obj.toString()); }*/ } /** * Gets the factory attributes. * * @return the factory attributes (possibly null if not set or if set to null) */ public Map<String, String> getFactoryAttributes() { return factoryAttributes; } /** * Sets the factory attributes. * * @param factoryAttributes the factory attributes */ public void setFactoryAttributes(Map<String, String> factoryAttributes) { this.factoryAttributes = factoryAttributes; } /** * Gets the message broker. * * @return the message broker */ public MessageBroker getMessageBroker() { return messageBroker; } /** * Sets the message broker. * * @param messageBroker the new message broker */ public void setMessageBroker(MessageBroker messageBroker) { this.messageBroker = messageBroker; } /** * Can search end point. Interrogates the URL and maybe connects to the enpoint * to identify if this endpoint is searchable. * * @param endPoint the end point * @param endPointData the end point data. If available then endpoint may not * need to be consulted. * @param options the options * @return true, if the endpoint can be searched, false if the endpoint cannot * be searched. Exception thrown when more info needed to make url searchable * @throws SearchPointOptionsException thrown when url can be searchable but * more options are needed to make it searchable * public abstract boolean canSearchEndPoint(String endPoint, String endPointData, SearchPointOptions options ) throws SearchPointOptionsException; */ /** * Read is external search. * * @return true, if successful */ protected abstract boolean readIsExternalSearch(); /** * Creates multiple instances of this search engine. Created for use by * distributed search. Some searchengines consult the DB on every * instance so if the searchengine knew beforehand that multiple searchengines * need information from the database in the same request, * then it is possible for it to generate 1 hit. * * @param rids the rids * @return the map (value can be error string or ASearchEngine Object) * @throws SearchException the search exception */ public Map<String, Object> createInstances(StringSet rids) throws SearchException{ throw new SearchException("This engine does not create multiple instances"); } /** * Check pagination. Recalculates pagination in-case we come to a service * where max results are not returned. Call this after search to straighten * out pagination * * @throws SearchException the search exception */ protected void checkPagination() throws SearchException { if(this.getHitsOnly() == true) { return; } SearchResult result = this.getRequestDefinition().getResult(); SearchCriteria criteria = this.getRequestDefinition().getCriteria(); ISearchFilterPagination criteriaCursor = criteria.getSearchFilterPageCursor(); PageCursor resultsCursor = result.getPageCursor(); resultsCursor.setRecordsPerPage(criteriaCursor.getRecordsPerPage()); resultsCursor.setCurrentPage(criteriaCursor.getCurrentPage()); if(result.getMaxQueryHits() >= result.getRecordSize()) { return; } if(result.getRecordSize() <= 0 && criteria.getSearchFilterPageCursor().getCurrentPage() >= 1) { // TODO: When we are at a wall (records.getSize() == recordsPerPage) and //we send this request, maxQueryHits should be calculated without adding //1. // Lets do a search for the prior page since we have reached the end criteria.getSearchFilterPageCursor().setCurrentPage( criteria.getSearchFilterPageCursor().getCurrentPage() - 1); this.doSearch(); } int maxQueryHits = 0; if(result.getRecordSize() == criteria.getSearchFilterPageCursor().getRecordsPerPage()) { maxQueryHits = 1 + criteria.getSearchFilterPageCursor().getRecordsPerPage() * criteria.getSearchFilterPageCursor().getCurrentPage(); result.setMaxQueryHits(maxQueryHits); criteria.getSearchFilterPageCursor().setTotalRecordCount(maxQueryHits); } else { maxQueryHits = (criteria.getSearchFilterPageCursor().getCurrentPage() - 1) * criteria.getSearchFilterPageCursor().getRecordsPerPage() + result.getRecordSize(); } criteria.getSearchFilterPageCursor().setTotalRecordCount(maxQueryHits); result.getPageCursor().setTotalRecordCount(maxQueryHits); result.getPageCursor().setCurrentPage( criteria.getSearchFilterPageCursor().getCurrentPage()); } /** * Fully reads the characters from the request input stream. * @param is input stream * @param sEncoding character encoding * @return the characters read * @throws IOException if an exception occurs */ protected String readInputCharacters(InputStream is, String sEncoding) throws IOException { StringBuffer sb = new StringBuffer(); InputStreamReader ir = null; BufferedReader br = null; try { //if (request.getContentLength() > 0) { char cbuf[] = new char[2048]; int n = 0; int nLen = cbuf.length; ir = new InputStreamReader(is,sEncoding); br = new BufferedReader(ir); while ((n = br.read(cbuf,0,nLen)) > 0) { sb.append(cbuf,0,n); } //} } finally { try {if (br != null) br.close();} catch (Exception ef) {} try {if (ir != null) ir.close();} catch (Exception ef) {} try {if (is != null) is.close();} catch (Exception ef) {} } return sb.toString(); } /** * ASearchEngine record. */ public static interface ARecord { /** * Gets metadata as text. * @return metadata as text */ String getMetadataAsText(); /** * Gets modified date. * @return modified date */ Date getModifiedDate(); } }