/* 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.lang.reflect.Constructor; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import com.esri.gpt.catalog.harvest.repository.HrRecord; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.jsf.MessageBroker; import com.esri.gpt.framework.security.credentials.UsernamePasswordCredentials; import com.esri.gpt.framework.util.Val; import com.esri.gpt.framework.collection.StringSet; /** * A factory for creating Search Data Access Objects. Factory caches * the engines in session. * */ public abstract class SearchEngineFactory extends ASearchEngine { // class variables ============================================================= /** class logger **/ private static final Logger LOG = Logger.getLogger(SearchEngineFactory.class.getCanonicalName()); // constructor ================================================================= /** * Instantiates a new search DAO factory. */ protected SearchEngineFactory() { super(); } // methods ===================================================================== /** * Creates a new SearchDAO object. This will return default searchEngine. * * @param criteria the criteria to be used by the search * @param result the result reference where the results will be stored after \ * search. * @param context Used for connection information. Can be null if you intend * to use CSW only. * @param messageBroker the message broker * * @return the search Data Access * * @throws SearchException when URI to search cannot be found */ public static ASearchEngine createSearchEngine( final SearchCriteria criteria, final SearchResult result, final RequestContext context, final MessageBroker messageBroker) throws SearchException { return createSearchEngine( criteria, result, context, SearchEngineLocal.ID, messageBroker, null, null); } /** * Creates a new SearchEngine object. * * @param criteria the criteria (should never be null) * @param result the result (should never be null) * @param context the context (should never be null) * @param key the key to find the corresponding search engine(should never be null) * @param messageBroker the message broker * * @return search engine * * @throws SearchException the search exception * * @see #createSearchEngine(SearchCriteria, SearchResult, RequestContext, String, MessageBroker, String, String) */ public static ASearchEngine createSearchEngine( final SearchCriteria criteria, final SearchResult result, final RequestContext context, String key, MessageBroker messageBroker ) throws SearchException { return createSearchEngine( criteria, result, context, key, messageBroker, null, null); } /** * Creates a new Search * * @param criteria the criteria (should never be null) * @param result the result (should never be null) * @param context the context (should never be null) * @param key the key to find the corresponding search engine(should never be null) (Should be a string to be used with regex on the {@link SearchConfig#getSearchFactoryRepos()} key * @param username the username * @param password the password * * @return the a search engine (never null, default search engine returned if no engine * is found corresponding to key * * * @throws SearchException Thrown if any parameters are null and on other faults */ public static ASearchEngine createSearchEngine( final SearchCriteria criteria, final SearchResult result, final RequestContext context, final String key, final MessageBroker messageBroker, final String username, final String password) throws SearchException { String sKey = Val.chkStr(key); if(result == null) { throw new SearchException ("Result variable give in Search Engine Factory is null"); } if(criteria == null) { throw new SearchException ("Criteria variable give in Search Engine Factory is null"); } if(context == null) { throw new SearchException ("Context variable give in Search Engine Factory is null"); } ASearchEngine sEngine = null; String value = null; try { sEngine = identifyEngine(sKey, context); } catch (Throwable e) { LOG.log(Level.WARNING, "Search Engine not found for key value {0}. Resorting to default search engine : {1}", new Object[]{sKey, e.getMessage()}); } if(sEngine == null) { // For backward compatibility (fail safe) so that searches without // searchFactory methods in gpt.xml work sEngine = new SearchEngineLocal(context); } intializeEngine(sEngine,criteria, result, context, sKey, messageBroker, username, password); return sEngine; } private static void intializeEngine ( final ASearchEngine sEngine, final SearchCriteria criteria, final SearchResult result, final RequestContext context, final String sKey, final MessageBroker messageBroker, final String username, final String password ) throws SearchException { SearchRequestDefinition sDef = new SearchRequestDefinition(criteria, result); sEngine.setRequestDefinition(sDef); sEngine.setRequestContext(context); sEngine.setKey(sKey); Map<String, String> attribs = getAttributesOfKey(sKey, context); sEngine.setFactoryAttributes(attribs); HttpServletRequest hReq = null; context.getServletRequest(); if( context.getServletRequest() instanceof HttpServletRequest) { hReq = (HttpServletRequest) context.getServletRequest(); } sEngine.setMessageBroker(messageBroker); sEngine.setResourceLinkBuilder( ResourceLinkBuilder.newBuilder(context, hReq, messageBroker)); if(username != null && "".equals(username.trim())) { UsernamePasswordCredentials cred = new UsernamePasswordCredentials(); cred.setUsername(username); cred.setPassword(password); sEngine.setCredentials(cred); } sEngine.init(); } private static ASearchEngine identifyEngine(String sKey, RequestContext context) throws SearchException { ASearchEngine sEngine = null; try { Map<String, String> attribs = getAttributesOfKey(sKey, context); String value = attribs.get("CLASS"); sEngine = getSearchEngine(value, sKey, context); } catch (Throwable e) { LOG.log(Level.WARNING, "Search Engine not found for key value {0}. Resorting to default search engine : {1}", new Object[]{sKey, e.getMessage()}); throw new SearchException("Could not find engine"); } return sEngine; } /** * Creates a new SearchEngine object. * * @param endPointUrl the end point url * @param endPointData the end point data * @param options the options * @return the a search engine */ public ASearchEngine createSearchEngine(String endPointUrl, String endPointData, SearchPointOptions options) { Map<String, Map<String, String>> factMap = SearchConfig.getConfiguredInstance().getSearchFactoryRepos(); return null; } /** * Gets the search engine. Sto/res it in the session incase * it is encountered again. * * @param className the class name * @param key the key * @param context the context * * @return the search engine (never null) * * @throws SearchException if class cannot be constructed */ @SuppressWarnings("unchecked") public static ASearchEngine getSearchEngine(String className, String key, RequestContext context) throws SearchException { Object obj = null; ASearchEngine sEngine = null; String value = className; try { Class cls = Class.forName(value); obj = cls.newInstance(); } catch (Throwable e) { try { Class cls = Class.forName(value); Class[] typesList = {RequestContext.class}; Constructor ctr = cls.getConstructor(typesList); Object[] initargs = {context}; obj = ctr.newInstance(initargs); } catch(Throwable f) { throw new SearchException("Could not locate SearchEngine Obj for key = " + key +" : " + e.getMessage() + " : " + f.getMessage(), e); } } if(!(obj instanceof ASearchEngine)) { throw new SearchException("Object from searchfactory " + obj.getClass().getCanonicalName() + " is not an instance of " + ASearchEngine.class.getCanonicalName()); } sEngine = (ASearchEngine) obj; return sEngine; } /** * Gets the value of key. * * @param key the key to be used * @param context the context * * @return attributes of the key (never null) * * @throws SearchException if error or cannot find a value */ private static Map<String, String> getAttributesOfKey(String key, RequestContext context) throws SearchException { Map<String, String> attributes = null; GptRepository repository = new GptRepository(); String error = ""; // use the url from the db feed it to the configuration // use the key itself to feed in to the configuration try { attributes = getAttributes(key, context); } catch (Exception e) { error += ": Did not get key = " + key + " in config file " + e.getMessage(); } if (attributes == null) { try { HrRecord record = repository.readHarvestRecord(key, context); String url = record.getHostUrl(); attributes = getAttributes(url, context); } catch (Exception e) { error = "Did not get key = " + key + " in repository " + e.getMessage(); } } if(attributes == null) { throw new SearchException(error); } return attributes; } /** * Gets the value of key from config (currently sourced from gpt.xml). * * @param key the key * @param context the context * * @return the value of key from config * * @throws SearchException the search exception */ private static Map<String, String> getAttributes(String key, RequestContext context) throws SearchException { Map<String, Map<String, String>> factMap = context.getApplicationConfiguration() .getCatalogConfiguration().getSearchConfig().getSearchFactoryRepos(); Set<String> keys = factMap.keySet(); Iterator<String> iter = keys.iterator(); String rKey = null; Map<String, String> attribs = null; while (iter != null && iter.hasNext()) { try { rKey = iter.next(); if (rKey == null) { continue; } String cKey = rKey; if (cKey.equals("*")) cKey = ".*"; Pattern pattern = Pattern.compile(cKey, Pattern.CASE_INSENSITIVE| Pattern.MULTILINE); Matcher matcher = pattern.matcher(key); if (!matcher.matches()) { continue; } attribs = factMap.get(rKey); break; } catch (Exception e) { LOG.log(Level.WARNING, "Error found while inspecting factory map", e); } } return attribs; } /** * Creates a new SearchEngine object. * * @param criteria the criteria * @param result the result * @param context the context * @param rids the rids * @param messageBroker the message broker * @param username the username * @param password the password * @return the map * @throws SearchException the search exception */ public static Map<String, Object> createSearchEngines ( final SearchCriteria criteria, final SearchResult result, final RequestContext context, final StringSet rids, final MessageBroker messageBroker, final String username, final String password) throws SearchException { Map<String, Object> returnMap = new HashMap<String, Object>(); Map<String,StringSet> mapEngineName2rids = new HashMap<String,StringSet> (); Map<String, ASearchEngine> mapEngineName2Engine = new HashMap<String, ASearchEngine>(); for(String rid: rids) { ASearchEngine engine = identifyEngine(rid, context); if(engine == null) { returnMap.put(rid, messageBroker.getMessage( "catalog.search.distributedSearch.ridEngineNotFound", new Object[]{rid})); continue; } // map rids to canonical engine String engineCanonicalName = engine.getClass().getCanonicalName(); StringSet ridEngineSet = mapEngineName2rids.get(engineCanonicalName); if(ridEngineSet == null) { ridEngineSet = new StringSet(); mapEngineName2rids.put(engineCanonicalName, ridEngineSet); } ridEngineSet.add(rid); // save engine if(!mapEngineName2Engine.containsKey(engineCanonicalName)) { mapEngineName2Engine.put(engineCanonicalName, engine); } } // create instances of search engines using asearchegine.createinstances Set<String> setEngineName = mapEngineName2rids.keySet(); for(String sEngineName: setEngineName) { StringSet setEngineRids = mapEngineName2rids.get(sEngineName); ASearchEngine searchEngine = mapEngineName2Engine.get(sEngineName); try { searchEngine.setRequestContext(context); searchEngine.setMessageBroker(messageBroker); Map<String, Object> searchEngineInstances = searchEngine.createInstances(setEngineRids); returnMap.putAll(searchEngineInstances); } catch(SearchException e) { for(String rid: setEngineRids) { returnMap.put(rid, "Rid = " + rid + " " + e.getMessage()); LOG.log(Level.WARNING, "Rid = " + rid + " error", e); } } } // Intialize engine Set<String> setRid = returnMap.keySet(); for(String rid:setRid) { Object obj = returnMap.get(rid); if(!(obj instanceof ASearchEngine)) { continue; } ASearchEngine engine = (ASearchEngine) obj; try { // TODO: Make this threaded. Get capabilities may be called intializeEngine(engine, criteria, result, context, rid, messageBroker, username, password); } catch (Exception e) { returnMap.put(rid, e.getMessage()); LOG.log(Level.FINE, "Rid += " + rid + " Error while initializing engine" , e); } } return returnMap; } }