/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/search/trunk/search-impl/impl/src/java/org/sakaiproject/search/component/service/impl/BaseSearchServiceImpl.java $ * $Id: BaseSearchServiceImpl.java 118402 2013-01-16 21:32:11Z jbush@rsmart.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.search.component.service.impl; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpConnectionManager; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermFreqVector; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Filter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.util.Version; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.event.api.EventTrackingService; import org.sakaiproject.event.api.NotificationEdit; import org.sakaiproject.event.api.NotificationService; import org.sakaiproject.search.api.InvalidSearchQueryException; import org.sakaiproject.search.api.SearchIndexBuilder; import org.sakaiproject.search.api.SearchList; import org.sakaiproject.search.api.SearchResult; import org.sakaiproject.search.api.SearchService; import org.sakaiproject.search.api.SearchStatus; import org.sakaiproject.search.api.TermFrequency; import org.sakaiproject.search.component.Messages; import org.sakaiproject.search.filter.SearchItemFilter; import org.sakaiproject.search.index.IndexReloadListener; import org.sakaiproject.search.index.IndexStorage; import org.sakaiproject.search.journal.impl.JournalSettings; import org.sakaiproject.search.model.SearchBuilderItem; import org.sakaiproject.search.util.DidYouMeanParser; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; /** * The search service * * @author ieb */ public abstract class BaseSearchServiceImpl implements SearchService { private static Log log = LogFactory.getLog(BaseSearchServiceImpl.class); /** * The index builder dependency */ private SearchIndexBuilder searchIndexBuilder; /** * dependency */ private NotificationService notificationService; /** * dependency */ private EventTrackingService eventTrackingService; /** * dependency */ private UserDirectoryService userDirectoryService; /** * dependency */ private SessionManager sessionManager; private static final String DIGEST_STORE_FOLDER = "/searchdigest/"; /** * Optional dependencies */ private List<String> triggerFunctions; /** * the notification object */ private NotificationEdit notification = null; protected IndexStorage indexStorage; /** * init completed */ protected boolean initComplete = false; private SearchItemFilter filter; private Map luceneFilters = new HashMap(); private Map luceneSorters = new HashMap(); private String defaultFilter = null; private String defaultSorter = null; private String sharedKey = null; private String searchServerUrl = null; private boolean searchServer = false; private ThreadLocal<String> localSearch = new ThreadLocal<String>(); private HttpClient httpClient; private HttpConnectionManagerParams httpParams = new HttpConnectionManagerParams(); private HttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager(); /** Configuration: to run the ddl on init or not. */ protected boolean autoDdl = false; private boolean diagnostics; private boolean enabled; private JournalSettings journalSettings; public void setJournalSettings(JournalSettings journalSettings) { this.journalSettings = journalSettings; } public abstract String getStatus(); public abstract SearchStatus getSearchStatus(); public abstract boolean removeWorkerLock(); /** * Configuration: to run the ddl on init or not. * * @param value * the auto ddl value. */ public void setAutoDdl(String value) { autoDdl = Boolean.valueOf(value).booleanValue(); } /** */ public String getAutoDdl() { return String.valueOf(autoDdl); } /** * Register a notification action to listen to events and modify the search * index */ public void init() { try { // register a transient notification for resources notification = notificationService.addTransientNotification(); // add all the functions that are registered to trigger search index // modification notification.setFunction(SearchService.EVENT_TRIGGER_SEARCH); if (triggerFunctions != null) { for (Iterator<String> ifn = triggerFunctions.iterator(); ifn.hasNext();) { String function = (String) ifn.next(); notification.addFunction(function); if (log.isDebugEnabled()) { log.debug("Adding Search Register " + function); //$NON-NLS-1$ } } } // set the filter to any site related resource notification.setResourceFilter("/"); //$NON-NLS-1$ // set the action notification.setAction(new SearchNotificationAction(searchIndexBuilder)); // Configure params for the Connection Manager httpParams.setDefaultMaxConnectionsPerHost(20); httpParams.setMaxTotalConnections(30); // This next line may not be necessary since we specified default 2 // lines ago, but here it is anyway httpParams.setMaxConnectionsPerHost(HostConfiguration.ANY_HOST_CONFIGURATION, 20); // Set up the connection manager httpConnectionManager.setParams(httpParams); // Finally set up the static multithreaded HttpClient httpClient = new HttpClient(httpConnectionManager); if (diagnostics) { indexStorage.enableDiagnostics(); } else { indexStorage.disableDiagnostics(); } indexStorage.addReloadListener(new IndexReloadListener() { public void reloaded(long reloadStart, long reloadEnd) { if (diagnostics) { log.info("Index Reloaded containing " + getNDocs() + " active documents and " + getPendingDocs() + " pending documents in " + (reloadEnd - reloadStart) + "ms"); } } }); } catch (Throwable t) { log.error("Failed to start ", t); //$NON-NLS-1$ } } /** * @return Returns the triggerFunctions. */ public List<String> getTriggerFunctions() { return triggerFunctions; } /** * @param triggerFunctions * The triggerFunctions to set. */ public void setTriggerFunctions(List<String> triggerFunctions) { if (initComplete) throw new RuntimeException( " use register function at runtime, setTriggerFucntions is for Spring IoC only"); //$NON-NLS-1$ this.triggerFunctions = triggerFunctions; } /** * {@inheritDoc} */ public void registerFunction(String function) { notification.addFunction(function); if (log.isDebugEnabled()) { log.debug("Adding Function " + function); //$NON-NLS-1$ } } /** * {@inheritDoc} * * @param indexFilter */ public SearchList search(String searchTerms, List<String> contexts, int start, int end) throws InvalidSearchQueryException { return search(searchTerms, contexts, start, end, defaultFilter, defaultSorter); } public SearchList search(String searchTerms, List<String> contexts, int start, int end, String filterName, String sorterName) throws InvalidSearchQueryException { try { BooleanQuery query = new BooleanQuery(); QueryParser qp = new QueryParser(Version.LUCENE_29,SearchService.FIELD_CONTENTS, getAnalyzer()); Query textQuery = qp.parse(searchTerms); // Support cross context searches if (contexts != null && contexts.size() > 0) { BooleanQuery contextQuery = new BooleanQuery(); for (Iterator<String> i = contexts.iterator(); i.hasNext();) { // Setup query so that it will allow results from any // included site, not all included sites. contextQuery.add(new TermQuery(new Term(SearchService.FIELD_SITEID, (String) i.next())), BooleanClause.Occur.SHOULD); // This would require term to be in all sites :-( // contextQuery.add(new TermQuery(new Term( // SearchService.FIELD_SITEID, (String) i.next())), // BooleanClause.Occur.MUST); } query.add(contextQuery, BooleanClause.Occur.MUST); } query.add(textQuery, BooleanClause.Occur.MUST); log.debug("Compiled Query is " + query.toString()); //$NON-NLS-1$ if (localSearch.get() == null && searchServerUrl != null && searchServerUrl.length() > 0) { try { PostMethod post = new PostMethod(searchServerUrl); String userId = sessionManager.getCurrentSessionUserId(); StringBuilder sb = new StringBuilder(); for (Iterator<String> ci = contexts.iterator(); ci.hasNext();) { sb.append(ci.next()).append(";"); //$NON-NLS-1$ } String contextParam = sb.toString(); post.setParameter(REST_CHECKSUM, digestCheck(userId, searchTerms)); post.setParameter(REST_CONTEXTS, contextParam); post.setParameter(REST_END, String.valueOf(end)); post.setParameter(REST_START, String.valueOf(start)); post.setParameter(REST_TERMS, searchTerms); post.setParameter(REST_USERID, userId); int status = httpClient.executeMethod(post); if (status != 200) { throw new RuntimeException( "Failed to perform remote search, http status was " + status); //$NON-NLS-1$ } String response = post.getResponseBodyAsString(); return new SearchListResponseImpl(response, textQuery, start, end, getAnalyzer(), filter, searchIndexBuilder, this); } catch (Exception ex) { log.error("Remote Search Failed ", ex); //$NON-NLS-1$ throw new IOException(ex.getMessage()); } } else { IndexSearcher indexSearcher = getIndexSearcher(false); int MAX_RESULTS = 1000000; if (indexSearcher != null) { TopDocs topDocs = null; Filter indexFilter = (Filter) luceneFilters.get(filterName); Sort indexSorter = (Sort) luceneSorters.get(sorterName); if (log.isDebugEnabled()) { log.debug("Using Filter " + filterName + ":" //$NON-NLS-1$ //$NON-NLS-2$ + indexFilter + " and " + sorterName + ":" //$NON-NLS-1$ //$NON-NLS-2$ + indexSorter); } if (indexFilter != null && indexSorter != null) { topDocs = indexSearcher.search(query, indexFilter, MAX_RESULTS, indexSorter); } else if (indexFilter != null) { topDocs = indexSearcher.search(query, indexFilter, MAX_RESULTS); } else if (indexSorter != null) { topDocs = indexSearcher.search(query, null, MAX_RESULTS, indexSorter); } else { topDocs = indexSearcher.search(query, MAX_RESULTS); } if (log.isDebugEnabled()) { log.debug("Got " + topDocs.totalHits + " hits"); //$NON-NLS-1$ //$NON-NLS-2$ } String context = null; if (contexts != null && contexts.size() > 0) { //seeing events doesn't support multi context use the first context = contexts.get(0); } eventTrackingService.post(eventTrackingService.newEvent(EVENT_SEARCH, EVENT_SEARCH_REF + textQuery.toString(), context, true, NotificationService.PREF_IMMEDIATE)); return new SearchListImpl(topDocs, indexSearcher, textQuery, start, end, getAnalyzer(), filter, searchIndexBuilder, this); } else { throw new RuntimeException( "Failed to start the Lucene Searche Engine"); //$NON-NLS-1$ } } } catch (ParseException e) { throw new InvalidSearchQueryException("Failed to parse Query ", e); //$NON-NLS-1$ } catch (IOException e) { throw new RuntimeException("Failed to run Search ", e); //$NON-NLS-1$ } } public void refreshInstance() { searchIndexBuilder.refreshIndex(); } public void rebuildInstance() { searchIndexBuilder.rebuildIndex(); } public void refreshSite(String currentSiteId) { searchIndexBuilder.refreshIndex(currentSiteId); } public void rebuildSite(String currentSiteId) { searchIndexBuilder.rebuildIndex(currentSiteId); } /** * {@inheritDoc} */ public void reload() { getIndexSearcher(true); } public void forceReload() { indexStorage.forceNextReload(); } /** * The sequence is, peform reload, * * @param reload * @return */ public IndexSearcher getIndexSearcher(boolean reload) { try { return indexStorage.getIndexSearcher(reload); } catch (Exception ex) { log.error("Failed to get an index searcher ", ex); throw new RuntimeException("Failed to get an index searcher ", ex); } } public int getNDocs() { try { return getIndexSearcher(false).getIndexReader().numDocs(); } catch (Exception e) { return -1; } } public int getPendingDocs() { return searchIndexBuilder.getPendingDocuments(); } public List<SearchBuilderItem> getAllSearchItems() { return searchIndexBuilder.getAllSearchItems(); } public List<SearchBuilderItem> getSiteMasterSearchItems() { return searchIndexBuilder.getSiteMasterSearchItems(); } public List<SearchBuilderItem> getGlobalMasterSearchItems() { return searchIndexBuilder.getGlobalMasterSearchItems(); } /** * @return Returns the filter. */ public SearchItemFilter getFilter() { return filter; } /** * @param filter * The filter to set. */ public void setFilter(SearchItemFilter filter) { this.filter = filter; } /** * @return Returns the defaultFilter. */ public String getDefaultFilter() { return defaultFilter; } /** * @param defaultFilter * The defaultFilter to set. */ public void setDefaultFilter(String defaultFilter) { this.defaultFilter = defaultFilter; } /** * @return Returns the defaultSorter. */ public String getDefaultSorter() { return defaultSorter; } /** * @param defaultSorter * The defaultSorter to set. */ public void setDefaultSorter(String defaultSorter) { this.defaultSorter = defaultSorter; } /** * @return Returns the luceneFilters. */ public Map getLuceneFilters() { return luceneFilters; } /** * @param luceneFilters * The luceneFilters to set. */ public void setLuceneFilters(Map luceneFilters) { this.luceneFilters = luceneFilters; } /** * @return Returns the luceneSorters. */ public Map getLuceneSorters() { return luceneSorters; } /** * @param luceneSorters * The luceneSorters to set. */ public void setLuceneSorters(Map luceneSorters) { this.luceneSorters = luceneSorters; } public TermFrequency getTerms(int documentId) throws IOException { final TermFreqVector tf = getIndexSearcher(false).getIndexReader() .getTermFreqVector(documentId, FIELD_CONTENTS); return new TermFrequency() { public String[] getTerms() { if (tf != null) { return tf.getTerms(); } return new String[0]; } public int[] getFrequencies() { if (tf != null) { return tf.getTermFrequencies(); } return new int[0]; } }; } public String searchXML(Map parameterMap) { String userid = null; String searchTerms = null; String checksum = null; String contexts = null; String ss = null; String se = null; try { if (!searchServer) { throw new Exception(Messages.getString("SearchServiceImpl.49")); //$NON-NLS-1$ } String[] useridA = (String[]) parameterMap.get(REST_USERID); String[] searchTermsA = (String[]) parameterMap.get(REST_TERMS); String[] checksumA = (String[]) parameterMap.get(REST_CHECKSUM); String[] contextsA = (String[]) parameterMap.get(REST_CONTEXTS); String[] ssA = (String[]) parameterMap.get(REST_START); String[] seA = (String[]) parameterMap.get(REST_END); StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\"?>"); //$NON-NLS-1$ boolean requestError = false; if (useridA == null || useridA.length != 1) { requestError = true; } else { userid = useridA[0]; } if (searchTermsA == null || searchTermsA.length != 1) { requestError = true; } else { searchTerms = searchTermsA[0]; } if (checksumA == null || checksumA.length != 1) { requestError = true; } else { checksum = checksumA[0]; } if (contextsA == null || contextsA.length != 1) { requestError = true; } else { contexts = contextsA[0]; } if (ssA == null || ssA.length != 1) { requestError = true; } else { ss = ssA[0]; } if (seA == null || seA.length != 1) { requestError = true; } else { se = seA[0]; } if (requestError) { throw new Exception(Messages.getString("SearchServiceImpl.34")); //$NON-NLS-1$ } int searchStart = Integer.parseInt(ss); int searchEnd = Integer.parseInt(se); String[] ctxa = contexts.split(";"); //$NON-NLS-1$ List<String> ctx = new ArrayList<String>(ctxa.length); for (int i = 0; i < ctxa.length; i++) { ctx.add(ctxa[i]); } if (sharedKey != null && sharedKey.length() > 0) { String check = digestCheck(userid, searchTerms); if (!check.equals(checksum)) { throw new Exception(Messages.getString("SearchServiceImpl.53")); //$NON-NLS-1$ } } org.sakaiproject.tool.api.Session s = sessionManager.startSession(); User u = userDirectoryService.getUser("admin"); //$NON-NLS-1$ s.setUserId(u.getId()); sessionManager.setCurrentSession(s); localSearch.set("localsearch"); //$NON-NLS-1$ try { SearchList sl = search(searchTerms, ctx, searchStart, searchEnd); sb.append("<results "); //$NON-NLS-1$ sb.append(" fullsize=\"").append(sl.getFullSize()) //$NON-NLS-1$ .append("\" "); //$NON-NLS-1$ sb.append(" start=\"").append(sl.getStart()).append("\" "); //$NON-NLS-1$ //$NON-NLS-2$ sb.append(" size=\"").append(sl.size()).append("\" "); //$NON-NLS-1$ //$NON-NLS-2$ sb.append(" >"); //$NON-NLS-1$ for (Iterator<SearchResult> si = sl.iterator(); si.hasNext();) { SearchResult sr = (SearchResult) si.next(); sr.toXMLString(sb); } sb.append("</results>"); //$NON-NLS-1$ return sb.toString(); } finally { sessionManager.setCurrentSession(null); localSearch.set(null); } } catch (Exception ex) { log.error("Search Service XML response failed ",ex); StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\"?>"); //$NON-NLS-1$ sb.append("<fault>"); //$NON-NLS-1$ sb.append("<request>"); //$NON-NLS-1$ sb.append("<![CDATA["); //$NON-NLS-1$ sb.append(" userid = ").append(StringEscapeUtils.escapeXml(userid)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb .append(" searchTerms = ").append(StringEscapeUtils.escapeXml(searchTerms)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb .append(" checksum = ").append(StringEscapeUtils.escapeXml(checksum)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb .append(" contexts = ").append(StringEscapeUtils.escapeXml(contexts)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb.append(" ss = ").append(StringEscapeUtils.escapeXml(ss)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb.append(" se = ").append(StringEscapeUtils.escapeXml(se)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb.append("]]>"); //$NON-NLS-1$ sb.append("</request>"); //$NON-NLS-1$ sb.append("<error>"); //$NON-NLS-1$ sb.append("<![CDATA["); //$NON-NLS-1$ try { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); pw.flush(); sb.append(sw.toString()); pw.close(); sw.close(); } catch (Exception ex2) { sb.append("Failed to serialize exception " + ex.getMessage()) //$NON-NLS-1$ .append("\n"); //$NON-NLS-1$ sb.append("Case: " + ex2.getMessage()); //$NON-NLS-1$ } sb.append("]]>"); //$NON-NLS-1$ sb.append("</error>"); //$NON-NLS-1$ sb.append("</fault>"); //$NON-NLS-1$ return sb.toString(); } } private String digestCheck(String userid, String searchTerms) throws GeneralSecurityException, IOException { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); //$NON-NLS-1$ String chstring = sharedKey + userid + searchTerms; return byteArrayToHexStr(sha1.digest(chstring.getBytes("UTF-8"))); //$NON-NLS-1$ } private static String byteArrayToHexStr(byte[] data) { char[] chars = new char[data.length * 2]; for (int i = 0; i < data.length; i++) { byte current = data[i]; int hi = (current & 0xF0) >> 4; int lo = current & 0x0F; chars[2 * i] = (char) (hi < 10 ? ('0' + hi) : ('A' + hi - 10)); chars[2 * i + 1] = (char) (lo < 10 ? ('0' + lo) : ('A' + lo - 10)); } return new String(chars); } /** * @return the sharedKey */ public String getSharedKey() { return sharedKey; } /** * @param sharedKey * the sharedKey to set */ public void setSharedKey(String sharedKey) { this.sharedKey = sharedKey; } /** * @return the searchServerUrl */ public String getSearchServerUrl() { return searchServerUrl; } /** * @param searchServerUrl * the searchServerUrl to set */ public void setSearchServerUrl(String searchServerUrl) { this.searchServerUrl = searchServerUrl; } /** * @return the searchServer */ public boolean isSearchServer() { return searchServer; } /** * @param searchServer * the searchServer to set */ public void setSearchServer(boolean searchServer) { this.searchServer = searchServer; } public boolean getDiagnostics() { return hasDiagnostics(); } public void setDiagnostics(boolean diagnostics) { if (diagnostics) { enableDiagnostics(); } else { disableDiagnostics(); } } /** * @return the eventTrackingService */ public EventTrackingService getEventTrackingService() { return eventTrackingService; } /** * @param eventTrackingService the eventTrackingService to set */ public void setEventTrackingService(EventTrackingService eventTrackingService) { this.eventTrackingService = eventTrackingService; } /** * @return the notificationService */ public NotificationService getNotificationService() { return notificationService; } /** * @param notificationService the notificationService to set */ public void setNotificationService(NotificationService notificationService) { this.notificationService = notificationService; } /** * @return the searchIndexBuilder */ public SearchIndexBuilder getSearchIndexBuilder() { return searchIndexBuilder; } /** * @param searchIndexBuilder the searchIndexBuilder to set */ public void setSearchIndexBuilder(SearchIndexBuilder searchIndexBuilder) { this.searchIndexBuilder = searchIndexBuilder; } /** * @return the sessionManager */ public SessionManager getSessionManager() { return sessionManager; } /** * @param sessionManager the sessionManager to set */ public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } /** * @return the userDirectoryService */ public UserDirectoryService getUserDirectoryService() { return userDirectoryService; } /** * @param userDirectoryService the userDirectoryService to set */ public void setUserDirectoryService(UserDirectoryService userDirectoryService) { this.userDirectoryService = userDirectoryService; } /** * @return Returns the indexStorage. */ public IndexStorage getIndexStorage() { return indexStorage; } /** * @param indexStorage * The indexStorage to set. */ public void setIndexStorage(IndexStorage indexStorage) { this.indexStorage = indexStorage; } protected Analyzer getAnalyzer() { return indexStorage.getAnalyzer(); } /* * (non-Javadoc) * * @see org.sakaiproject.search.api.Diagnosable#disableDiagnostics() */ public void disableDiagnostics() { diagnostics = false; if (indexStorage != null) { indexStorage.disableDiagnostics(); } } /* * (non-Javadoc) * * @see org.sakaiproject.search.api.Diagnosable#enableDiagnostics() */ public void enableDiagnostics() { diagnostics = true; if (indexStorage != null) { indexStorage.enableDiagnostics(); } } /** * @see org.sakaiproject.search.api.Diagnosable#hasDiagnostics() */ public boolean hasDiagnostics() { return diagnostics; } public List getSegmentInfo() { return indexStorage.getSegmentInfoList(); } /* (non-Javadoc) * @see org.sakaiproject.search.api.SearchService#isEnabled() */ public boolean isEnabled() { enabled = ServerConfigurationService.getBoolean("search.enable",false); log.info("Enable = " + ServerConfigurationService.getString("search.enable", "false")); enabled = enabled && ServerConfigurationService.getBoolean("search.indexbuild",true); return enabled; } public String getDigestStoragePath() { String customPath = ServerConfigurationService.getString("search.digestPath"); String storePath = null; if (customPath == null || "".equals(customPath)) { storePath = ServerConfigurationService.getString("bodyPath@org.sakaiproject.content.api.ContentHostingService"); if (storePath == null || "".equals(storePath)) { return null; } } else { storePath = customPath; } return storePath + "/" + DIGEST_STORE_FOLDER; } Directory spellIndexDirectory = null; public String[] getSearchSuggestions(String searchString, String currentSiteId, boolean allMySites) { String suggestion = getSearchSuggestion(searchString); if (suggestion != null && suggestion.length() > 0) { return new String[]{searchString}; } return new String[]{}; } public String getSearchSuggestion(String queryString) { log.info("getSearchSuggestion( " + queryString + ")"); if (!ServerConfigurationService.getBoolean("search.experimental.didyoumean", false)) { log.info("did you mean feature is not enabled"); return null; } if (spellIndexDirectory == null) { spellIndexDirectory = indexStorage.getSpellDirectory(); } //if its still null we we'rent able to create a spellindex if (spellIndexDirectory == null) { log.info("Spell index is not available"); return null; } //the reader to the original index: IndexReader indexReaderOrigional = null; try { indexReaderOrigional = indexStorage.getIndexReader(); } catch (CorruptIndexException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } DidYouMeanParser parser = new DidYouMeanParser(SearchService.FIELD_CONTENTS, spellIndexDirectory, indexReaderOrigional); try { Query query = parser.suggest(queryString); //the service may have no suggestions if (query != null) { log.debug("got suggestion: " + query.toString(SearchService.FIELD_CONTENTS)); return query.toString(SearchService.FIELD_CONTENTS); } } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }