001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.search;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.messaging.DestinationNames;
020    import com.liferay.portal.kernel.security.pacl.permission.PortalRuntimePermission;
021    import com.liferay.portal.kernel.util.ClassUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.PropsKeys;
024    import com.liferay.portal.kernel.util.PropsUtil;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.security.permission.PermissionThreadLocal;
027    import com.liferay.registry.Registry;
028    import com.liferay.registry.RegistryUtil;
029    import com.liferay.registry.ServiceReference;
030    import com.liferay.registry.ServiceTracker;
031    import com.liferay.registry.ServiceTrackerCustomizer;
032    
033    import java.util.Collection;
034    import java.util.HashSet;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.Locale;
038    import java.util.Map;
039    import java.util.Set;
040    import java.util.concurrent.ConcurrentHashMap;
041    
042    /**
043     * @author Bruno Farache
044     * @author Raymond Aug??
045     * @author Michael C. Han
046     */
047    public class SearchEngineUtil {
048    
049            public static final String GENERIC_ENGINE_ID = "GENERIC_ENGINE";
050    
051            public static final String SYSTEM_ENGINE_ID = "SYSTEM_ENGINE";
052    
053            /**
054             * @deprecated As of 7.0.0, replaced by {@link #addDocument(String, long,
055             *             Document, boolean)}
056             */
057            @Deprecated
058            public static void addDocument(
059                            String searchEngineId, long companyId, Document document)
060                    throws SearchException {
061    
062                    addDocument(searchEngineId, companyId, document, false);
063            }
064    
065            public static void addDocument(
066                            String searchEngineId, long companyId, Document document,
067                            boolean commitImmediately)
068                    throws SearchException {
069    
070                    if (isIndexReadOnly()) {
071                            return;
072                    }
073    
074                    if (_log.isDebugEnabled()) {
075                            _log.debug("Add document " + document.toString());
076                    }
077    
078                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
079    
080                    IndexWriter indexWriter = searchEngine.getIndexWriter();
081    
082                    _searchPermissionChecker.addPermissionFields(companyId, document);
083    
084                    SearchContext searchContext = new SearchContext();
085    
086                    searchContext.setCommitImmediately(commitImmediately);
087                    searchContext.setCompanyId(companyId);
088                    searchContext.setSearchEngineId(searchEngineId);
089    
090                    indexWriter.addDocument(searchContext, document);
091            }
092    
093            /**
094             * @deprecated As of 7.0.0, replaced by {@link #addDocuments(String, long,
095             *             Collection, boolean)}
096             */
097            @Deprecated
098            public static void addDocuments(
099                            String searchEngineId, long companyId,
100                            Collection<Document> documents)
101                    throws SearchException {
102    
103                    addDocuments(searchEngineId, companyId, documents, false);
104            }
105    
106            public static void addDocuments(
107                            String searchEngineId, long companyId,
108                            Collection<Document> documents, boolean commitImmediately)
109                    throws SearchException {
110    
111                    if (isIndexReadOnly() || (documents == null) || documents.isEmpty()) {
112                            return;
113                    }
114    
115                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
116    
117                    IndexWriter indexWriter = searchEngine.getIndexWriter();
118    
119                    for (Document document : documents) {
120                            if (_log.isDebugEnabled()) {
121                                    _log.debug("Add document " + document.toString());
122                            }
123    
124                            _searchPermissionChecker.addPermissionFields(companyId, document);
125                    }
126    
127                    SearchContext searchContext = new SearchContext();
128    
129                    searchContext.setCommitImmediately(commitImmediately);
130                    searchContext.setCompanyId(companyId);
131                    searchContext.setSearchEngineId(searchEngineId);
132    
133                    indexWriter.addDocuments(searchContext, documents);
134            }
135    
136            public synchronized static void backup(long companyId, String backupName)
137                    throws SearchException {
138    
139                    for (SearchEngine searchEngine : _searchEngines.values()) {
140                            searchEngine.backup(companyId, backupName);
141                    }
142            }
143    
144            public synchronized static String backup(
145                            long companyId, String searchEngineId, String backupName)
146                    throws SearchException {
147    
148                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
149    
150                    return searchEngine.backup(companyId, backupName);
151            }
152    
153            public synchronized static void backup(String backupName)
154                    throws SearchException {
155    
156                    for (SearchEngine searchEngine : _searchEngines.values()) {
157                            for (long companyId : _companyIds) {
158                                    searchEngine.backup(companyId, backupName);
159                            }
160                    }
161            }
162    
163            /**
164             * @deprecated As of 7.0.0, replaced by {@link #deleteDocument(String, long,
165             *             String, boolean)}
166             */
167            @Deprecated
168            public static void deleteDocument(
169                            String searchEngineId, long companyId, String uid)
170                    throws SearchException {
171    
172                    deleteDocument(searchEngineId, companyId, uid, false);
173            }
174    
175            public static void deleteDocument(
176                            String searchEngineId, long companyId, String uid,
177                            boolean commitImmediately)
178                    throws SearchException {
179    
180                    if (isIndexReadOnly()) {
181                            return;
182                    }
183    
184                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
185    
186                    IndexWriter indexWriter = searchEngine.getIndexWriter();
187    
188                    SearchContext searchContext = new SearchContext();
189    
190                    searchContext.setCommitImmediately(commitImmediately);
191                    searchContext.setCompanyId(companyId);
192                    searchContext.setSearchEngineId(searchEngineId);
193    
194                    indexWriter.deleteDocument(searchContext, uid);
195            }
196    
197            /**
198             * @deprecated As of 7.0.0, replaced by {@link #deleteDocuments(String,
199             *             long, Collection, boolean)}
200             */
201            @Deprecated
202            public static void deleteDocuments(
203                            String searchEngineId, long companyId, Collection<String> uids)
204                    throws SearchException {
205    
206                    deleteDocuments(searchEngineId, companyId, uids, false);
207            }
208    
209            public static void deleteDocuments(
210                            String searchEngineId, long companyId, Collection<String> uids,
211                            boolean commitImmediately)
212                    throws SearchException {
213    
214                    if (isIndexReadOnly() || (uids == null) || uids.isEmpty()) {
215                            return;
216                    }
217    
218                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
219    
220                    IndexWriter indexWriter = searchEngine.getIndexWriter();
221    
222                    SearchContext searchContext = new SearchContext();
223    
224                    searchContext.setCommitImmediately(commitImmediately);
225                    searchContext.setCompanyId(companyId);
226                    searchContext.setSearchEngineId(searchEngineId);
227    
228                    indexWriter.deleteDocuments(searchContext, uids);
229            }
230    
231            public static void deleteEntityDocuments(
232                            String searchEngineId, long companyId, String className,
233                            boolean commitImmediately)
234                    throws SearchException {
235    
236                    if (isIndexReadOnly()) {
237                            return;
238                    }
239    
240                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
241    
242                    if (searchEngine == null) {
243                            return;
244                    }
245    
246                    IndexWriter indexWriter = searchEngine.getIndexWriter();
247    
248                    SearchContext searchContext = new SearchContext();
249    
250                    searchContext.setCommitImmediately(commitImmediately);
251                    searchContext.setCompanyId(companyId);
252                    searchContext.setSearchEngineId(searchEngineId);
253    
254                    indexWriter.deleteEntityDocuments(searchContext, className);
255            }
256    
257            /**
258             * @deprecated As of 7.0.0, replaced by {@link
259             *             #deleteEntityDocuments(String, long, String, boolean)}
260             */
261            @Deprecated
262            public static void deletePortletDocuments(
263                            String searchEngineId, long companyId, String portletId)
264                    throws SearchException {
265    
266                    deleteEntityDocuments(searchEngineId, companyId, portletId, false);
267            }
268    
269            public static String getDefaultSearchEngineId() {
270                    if (_defaultSearchEngineId == null) {
271                            return SYSTEM_ENGINE_ID;
272                    }
273    
274                    return _defaultSearchEngineId;
275            }
276    
277            public static String[] getEntryClassNames() {
278                    Set<String> assetEntryClassNames = new HashSet<>();
279    
280                    for (Indexer indexer : IndexerRegistryUtil.getIndexers()) {
281                            for (String className : indexer.getSearchClassNames()) {
282                                    if (!_excludedEntryClassNames.contains(className)) {
283                                            assetEntryClassNames.add(className);
284                                    }
285                            }
286                    }
287    
288                    return assetEntryClassNames.toArray(
289                            new String[assetEntryClassNames.size()]);
290            }
291    
292            public static String getQueryString(
293                    SearchContext searchContext, Query query) {
294    
295                    SearchEngine searchEngine = getSearchEngine(
296                            searchContext.getSearchEngineId());
297    
298                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
299    
300                    try {
301                            return indexSearcher.getQueryString(searchContext, query);
302                    }
303                    catch (ParseException pe) {
304                            if (_log.isDebugEnabled()) {
305                                    _log.debug("Unable to parse query " + query, pe);
306                            }
307                    }
308    
309                    return StringPool.BLANK;
310            }
311    
312            public static SearchEngine getSearchEngine(String searchEngineId) {
313                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
314    
315                    SearchEngine searchEngine = _searchEngines.get(searchEngineId);
316    
317                    if (searchEngine == null) {
318                            if (SYSTEM_ENGINE_ID.equals(searchEngineId)) {
319                                    waitForSystemSearchEngine();
320    
321                                    searchEngine = _searchEngines.get(SYSTEM_ENGINE_ID);
322    
323                                    if (searchEngine == null) {
324                                            throw new IllegalStateException(
325                                                    "Unable to find search engine " + SYSTEM_ENGINE_ID);
326                                    }
327    
328                                    return searchEngine;
329                            }
330    
331                            if (getDefaultSearchEngineId().equals(searchEngineId)) {
332                                    throw new IllegalStateException(
333                                            "There is no default search engine configured with ID " +
334                                                    getDefaultSearchEngineId());
335                            }
336    
337                            if (_log.isWarnEnabled()) {
338                                    _log.warn(
339                                            "There is no search engine configured with ID " +
340                                                    searchEngineId);
341                            }
342                    }
343    
344                    return searchEngine;
345            }
346    
347            public static String getSearchEngineId(Collection<Document> documents) {
348                    if (!documents.isEmpty()) {
349                            Iterator<Document> iterator = documents.iterator();
350    
351                            Document document = iterator.next();
352    
353                            return getSearchEngineId(document);
354                    }
355    
356                    return getDefaultSearchEngineId();
357            }
358    
359            public static String getSearchEngineId(Document document) {
360                    String entryClassName = document.get("entryClassName");
361    
362                    Indexer indexer = IndexerRegistryUtil.getIndexer(entryClassName);
363    
364                    String searchEngineId = indexer.getSearchEngineId();
365    
366                    if (_log.isDebugEnabled()) {
367                            _log.debug(
368                                    "Search engine ID " + searchEngineId + " is associated with " +
369                                            ClassUtil.getClassName(indexer));
370                    }
371    
372                    return searchEngineId;
373            }
374    
375            public static Set<String> getSearchEngineIds() {
376                    PortalRuntimePermission.checkGetBeanProperty(
377                            SearchEngineUtil.class, "searchEngineIds");
378    
379                    return _searchEngines.keySet();
380            }
381    
382            public static SearchEngine getSearchEngineSilent(String searchEngineId) {
383                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
384    
385                    return _searchEngines.get(searchEngineId);
386            }
387    
388            public static SearchPermissionChecker getSearchPermissionChecker() {
389                    PortalRuntimePermission.checkGetBeanProperty(
390                            SearchEngineUtil.class, "searchPermissionChecker");
391    
392                    return _searchPermissionChecker;
393            }
394    
395            public static String getSearchReaderDestinationName(String searchEngineId) {
396                    return DestinationNames.SEARCH_READER.concat(StringPool.SLASH).concat(
397                            searchEngineId);
398            }
399    
400            public static String getSearchWriterDestinationName(String searchEngineId) {
401                    return DestinationNames.SEARCH_WRITER.concat(StringPool.SLASH).concat(
402                            searchEngineId);
403            }
404    
405            public static void indexKeyword(
406                            long companyId, String querySuggestion, float weight,
407                            String keywordType, Locale locale)
408                    throws SearchException {
409    
410                    String searchEngineId = getDefaultSearchEngineId();
411    
412                    indexKeyword(
413                            searchEngineId, companyId, querySuggestion, weight, keywordType,
414                            locale);
415            }
416    
417            public static void indexKeyword(
418                            String searchEngineId, long companyId, String querySuggestion,
419                            float weight, String keywordType, Locale locale)
420                    throws SearchException {
421    
422                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
423    
424                    IndexWriter indexWriter = searchEngine.getIndexWriter();
425    
426                    SearchContext searchContext = new SearchContext();
427    
428                    searchContext.setCompanyId(companyId);
429                    searchContext.setSearchEngineId(searchEngineId);
430                    searchContext.setKeywords(querySuggestion);
431                    searchContext.setLocale(locale);
432    
433                    indexWriter.indexKeyword(searchContext, weight, keywordType);
434            }
435    
436            public static void indexQuerySuggestionDictionaries(long companyId)
437                    throws SearchException {
438    
439                    Set<String> searchEngineIds = getSearchEngineIds();
440    
441                    for (String searchEngineId : searchEngineIds) {
442                            indexQuerySuggestionDictionaries(searchEngineId, companyId);
443                    }
444            }
445    
446            public static void indexQuerySuggestionDictionaries(
447                            String searchEngineId, long companyId)
448                    throws SearchException {
449    
450                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
451    
452                    IndexWriter indexWriter = searchEngine.getIndexWriter();
453    
454                    SearchContext searchContext = new SearchContext();
455    
456                    searchContext.setCompanyId(companyId);
457                    searchContext.setSearchEngineId(searchEngineId);
458    
459                    indexWriter.indexQuerySuggestionDictionaries(searchContext);
460            }
461    
462            public static void indexQuerySuggestionDictionary(
463                            long companyId, Locale locale)
464                    throws SearchException {
465    
466                    String searchEngineId = getDefaultSearchEngineId();
467    
468                    indexQuerySuggestionDictionary(searchEngineId, companyId, locale);
469            }
470    
471            public static void indexQuerySuggestionDictionary(
472                            String searchEngineId, long companyId, Locale locale)
473                    throws SearchException {
474    
475                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
476    
477                    IndexWriter indexWriter = searchEngine.getIndexWriter();
478    
479                    SearchContext searchContext = new SearchContext();
480    
481                    searchContext.setCompanyId(companyId);
482                    searchContext.setSearchEngineId(searchEngineId);
483                    searchContext.setLocale(locale);
484    
485                    indexWriter.indexQuerySuggestionDictionary(searchContext);
486            }
487    
488            public static void indexSpellCheckerDictionaries(long companyId)
489                    throws SearchException {
490    
491                    String searchEngineId = getDefaultSearchEngineId();
492    
493                    indexSpellCheckerDictionaries(searchEngineId, companyId);
494            }
495    
496            public static void indexSpellCheckerDictionaries(
497                            String searchEngineId, long companyId)
498                    throws SearchException {
499    
500                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
501    
502                    IndexWriter indexWriter = searchEngine.getIndexWriter();
503    
504                    SearchContext searchContext = new SearchContext();
505    
506                    searchContext.setCompanyId(companyId);
507                    searchContext.setSearchEngineId(searchEngineId);
508    
509                    indexWriter.indexSpellCheckerDictionaries(searchContext);
510            }
511    
512            public static void indexSpellCheckerDictionary(
513                            long companyId, Locale locale)
514                    throws SearchException {
515    
516                    String searchEngineId = getDefaultSearchEngineId();
517    
518                    indexSpellCheckerDictionary(searchEngineId, companyId, locale);
519            }
520    
521            public static void indexSpellCheckerDictionary(
522                            String searchEngineId, long companyId, Locale locale)
523                    throws SearchException {
524    
525                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
526    
527                    IndexWriter indexWriter = searchEngine.getIndexWriter();
528    
529                    SearchContext searchContext = new SearchContext();
530    
531                    searchContext.setCompanyId(companyId);
532                    searchContext.setSearchEngineId(searchEngineId);
533                    searchContext.setLocale(locale);
534    
535                    indexWriter.indexSpellCheckerDictionary(searchContext);
536            }
537    
538            public synchronized static void initialize(long companyId) {
539                    if (_companyIds.contains(companyId)) {
540                            return;
541                    }
542    
543                    waitForSystemSearchEngine();
544    
545                    _companyIds.add(companyId);
546    
547                    for (SearchEngine searchEngine : _searchEngines.values()) {
548                            searchEngine.initialize(companyId);
549                    }
550            }
551    
552            public static boolean isIndexReadOnly() {
553                    PortalRuntimePermission.checkGetBeanProperty(
554                            SearchEngineUtil.class, "indexReadOnly");
555    
556                    return _indexReadOnly;
557            }
558    
559            public static void partiallyUpdateDocument(
560                            String searchEngineId, long companyId, Document document,
561                            boolean commitImmediately)
562                    throws SearchException {
563    
564                    if (isIndexReadOnly()) {
565                            return;
566                    }
567    
568                    if (_log.isDebugEnabled()) {
569                            _log.debug("Document " + document.toString());
570                    }
571    
572                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
573    
574                    IndexWriter indexWriter = searchEngine.getIndexWriter();
575    
576                    _searchPermissionChecker.addPermissionFields(companyId, document);
577    
578                    SearchContext searchContext = new SearchContext();
579    
580                    searchContext.setCommitImmediately(commitImmediately);
581                    searchContext.setCompanyId(companyId);
582                    searchContext.setSearchEngineId(searchEngineId);
583    
584                    indexWriter.partiallyUpdateDocument(searchContext, document);
585            }
586    
587            public static void partiallyUpdateDocuments(
588                            String searchEngineId, long companyId,
589                            Collection<Document> documents, boolean commitImmediately)
590                    throws SearchException {
591    
592                    if (isIndexReadOnly() || (documents == null) || documents.isEmpty()) {
593                            return;
594                    }
595    
596                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
597    
598                    IndexWriter indexWriter = searchEngine.getIndexWriter();
599    
600                    for (Document document : documents) {
601                            if (_log.isDebugEnabled()) {
602                                    _log.debug("Document " + document.toString());
603                            }
604    
605                            _searchPermissionChecker.addPermissionFields(companyId, document);
606                    }
607    
608                    SearchContext searchContext = new SearchContext();
609    
610                    searchContext.setCommitImmediately(commitImmediately);
611                    searchContext.setCompanyId(companyId);
612                    searchContext.setSearchEngineId(searchEngineId);
613    
614                    indexWriter.partiallyUpdateDocuments(searchContext, documents);
615            }
616    
617            public synchronized static void removeBackup(
618                            long companyId, String backupName)
619                    throws SearchException {
620    
621                    for (SearchEngine searchEngine : _searchEngines.values()) {
622                            searchEngine.removeBackup(companyId, backupName);
623                    }
624            }
625    
626            public synchronized static void removeBackup(String backupName)
627                    throws SearchException {
628    
629                    for (SearchEngine searchEngine : _searchEngines.values()) {
630                            for (long companyId : _companyIds) {
631                                    searchEngine.removeBackup(companyId, backupName);
632                            }
633                    }
634            }
635    
636            public synchronized static void removeCompany(long companyId) {
637                    if (!_companyIds.contains(companyId)) {
638                            return;
639                    }
640    
641                    for (SearchEngine searchEngine : _searchEngines.values()) {
642                            searchEngine.removeCompany(companyId);
643                    }
644    
645                    _companyIds.remove(companyId);
646            }
647    
648            public static SearchEngine removeSearchEngine(String searchEngineId) {
649                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
650    
651                    return _searchEngines.remove(searchEngineId);
652            }
653    
654            public synchronized static void restore(long companyId, String backupName)
655                    throws SearchException {
656    
657                    for (SearchEngine searchEngine : _searchEngines.values()) {
658                            searchEngine.restore(companyId, backupName);
659                    }
660            }
661    
662            public synchronized static void restore(String backupName)
663                    throws SearchException {
664    
665                    for (SearchEngine searchEngine : _searchEngines.values()) {
666                            for (long companyId : _companyIds) {
667                                    searchEngine.restore(companyId, backupName);
668                            }
669                    }
670            }
671    
672            public static Hits search(SearchContext searchContext, Query query)
673                    throws SearchException {
674    
675                    if (_log.isDebugEnabled()) {
676                            _log.debug("Search query " + getQueryString(searchContext, query));
677                    }
678    
679                    SearchEngine searchEngine = getSearchEngine(
680                            searchContext.getSearchEngineId());
681    
682                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
683    
684                    return indexSearcher.search(searchContext, query);
685            }
686    
687            /**
688             * @deprecated As of 7.0.0, replaced by {@link #search(SearchContext,
689             *             Query)}
690             */
691            @Deprecated
692            public static Hits search(
693                            String searchEngineId, long companyId, Query query, int start,
694                            int end)
695                    throws SearchException {
696    
697                    return search(
698                            searchEngineId, companyId, query, SortFactoryUtil.getDefaultSorts(),
699                            start, end);
700            }
701    
702            /**
703             * @deprecated As of 7.0.0, replaced by {@link #search(SearchContext,
704             *             Query)}
705             */
706            @Deprecated
707            public static Hits search(
708                            String searchEngineId, long companyId, Query query, Sort sort,
709                            int start, int end)
710                    throws SearchException {
711    
712                    return search(
713                            searchEngineId, companyId, query, new Sort[] {sort}, start, end);
714            }
715    
716            /**
717             * @deprecated As of 7.0.0, replaced by {@link #search(SearchContext,
718             *             Query)}
719             */
720            @Deprecated
721            public static Hits search(
722                            String searchEngineId, long companyId, Query query, Sort[] sorts,
723                            int start, int end)
724                    throws SearchException {
725    
726                    SearchContext searchContext = new SearchContext();
727    
728                    searchContext.setCompanyId(companyId);
729                    searchContext.setEnd(end);
730                    searchContext.setSearchEngineId(searchEngineId);
731                    searchContext.setSorts(sorts);
732                    searchContext.setStart(start);
733    
734                    return search(searchContext, query);
735            }
736    
737            public static long searchCount(SearchContext searchContext, Query query)
738                    throws SearchException {
739    
740                    if (_log.isDebugEnabled()) {
741                            _log.debug("Search query " + getQueryString(searchContext, query));
742                    }
743    
744                    SearchEngine searchEngine = getSearchEngine(
745                            searchContext.getSearchEngineId());
746    
747                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
748    
749                    return indexSearcher.searchCount(searchContext, query);
750            }
751    
752            public static void setDefaultSearchEngineId(String defaultSearchEngineId) {
753                    PortalRuntimePermission.checkSetBeanProperty(
754                            SearchEngineUtil.class, "defaultSearchEngineId");
755    
756                    _defaultSearchEngineId = defaultSearchEngineId;
757            }
758    
759            public static void setIndexReadOnly(boolean indexReadOnly) {
760                    PortalRuntimePermission.checkSetBeanProperty(
761                            SearchEngineUtil.class, "indexReadOnly");
762    
763                    _indexReadOnly = indexReadOnly;
764            }
765    
766            public static void setSearchEngine(
767                    String searchEngineId, SearchEngine searchEngine) {
768    
769                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
770    
771                    _searchEngines.put(searchEngineId, searchEngine);
772    
773                    for (Long companyId : _companyIds) {
774                            searchEngine.initialize(companyId);
775                    }
776            }
777    
778            public static String spellCheckKeywords(SearchContext searchContext)
779                    throws SearchException {
780    
781                    if (_log.isDebugEnabled()) {
782                            _log.debug("Spell checking " + searchContext.getKeywords());
783                    }
784    
785                    SearchEngine searchEngine = getSearchEngine(
786                            searchContext.getSearchEngineId());
787    
788                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
789    
790                    return indexSearcher.spellCheckKeywords(searchContext);
791            }
792    
793            public static Map<String, List<String>> spellCheckKeywords(
794                            SearchContext searchContext, int max)
795                    throws SearchException {
796    
797                    if (_log.isDebugEnabled()) {
798                            _log.debug("Spell checking " + searchContext.getKeywords());
799                    }
800    
801                    SearchEngine searchEngine = getSearchEngine(
802                            searchContext.getSearchEngineId());
803    
804                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
805    
806                    return indexSearcher.spellCheckKeywords(searchContext, max);
807            }
808    
809            public static String[] suggestKeywordQueries(
810                            SearchContext searchContext, int max)
811                    throws SearchException {
812    
813                    if (_log.isDebugEnabled()) {
814                            _log.debug(
815                                    "Suggesting keyword queries" + searchContext.getKeywords());
816                    }
817    
818                    SearchEngine searchEngine = getSearchEngine(
819                            searchContext.getSearchEngineId());
820    
821                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
822    
823                    return indexSearcher.suggestKeywordQueries(searchContext, max);
824            }
825    
826            /**
827             * @deprecated As of 7.0.0, replaced by {@link #updateDocument(String, long,
828             *             Document, boolean)}
829             */
830            @Deprecated
831            public static void updateDocument(
832                            String searchEngineId, long companyId, Document document)
833                    throws SearchException {
834    
835                    updateDocument(searchEngineId, companyId, document, false);
836            }
837    
838            public static void updateDocument(
839                            String searchEngineId, long companyId, Document document,
840                            boolean commitImmediately)
841                    throws SearchException {
842    
843                    if (isIndexReadOnly()) {
844                            return;
845                    }
846    
847                    if (_log.isDebugEnabled()) {
848                            _log.debug("Document " + document.toString());
849                    }
850    
851                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
852    
853                    IndexWriter indexWriter = searchEngine.getIndexWriter();
854    
855                    _searchPermissionChecker.addPermissionFields(companyId, document);
856    
857                    SearchContext searchContext = new SearchContext();
858    
859                    searchContext.setCommitImmediately(commitImmediately);
860                    searchContext.setCompanyId(companyId);
861                    searchContext.setSearchEngineId(searchEngineId);
862    
863                    indexWriter.updateDocument(searchContext, document);
864            }
865    
866            /**
867             * @deprecated As of 7.0.0, replaced by {@link #updateDocuments(String,
868             *             long, Collection, boolean)}
869             */
870            @Deprecated
871            public static void updateDocuments(
872                            String searchEngineId, long companyId,
873                            Collection<Document> documents)
874                    throws SearchException {
875    
876                    updateDocuments(searchEngineId, companyId, documents, false);
877            }
878    
879            public static void updateDocuments(
880                            String searchEngineId, long companyId,
881                            Collection<Document> documents, boolean commitImmediately)
882                    throws SearchException {
883    
884                    if (isIndexReadOnly() || (documents == null) || documents.isEmpty()) {
885                            return;
886                    }
887    
888                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
889    
890                    IndexWriter indexWriter = searchEngine.getIndexWriter();
891    
892                    for (Document document : documents) {
893                            if (_log.isDebugEnabled()) {
894                                    _log.debug("Document " + document.toString());
895                            }
896    
897                            _searchPermissionChecker.addPermissionFields(companyId, document);
898                    }
899    
900                    SearchContext searchContext = new SearchContext();
901    
902                    searchContext.setCommitImmediately(commitImmediately);
903                    searchContext.setCompanyId(companyId);
904                    searchContext.setSearchEngineId(searchEngineId);
905    
906                    indexWriter.updateDocuments(searchContext, documents);
907            }
908    
909            public static void updatePermissionFields(String name, String primKey) {
910                    if (isIndexReadOnly()) {
911                            return;
912                    }
913    
914                    if (PermissionThreadLocal.isFlushResourcePermissionEnabled(
915                                    name, primKey)) {
916    
917                            _searchPermissionChecker.updatePermissionFields(name, primKey);
918                    }
919            }
920    
921            public void setExcludedEntryClassNames(
922                    List<String> excludedEntryClassNames) {
923    
924                    PortalRuntimePermission.checkSetBeanProperty(
925                            getClass(), "excludedEntryClassNames");
926    
927                    _excludedEntryClassNames.addAll(excludedEntryClassNames);
928            }
929    
930            public void setSearchPermissionChecker(
931                    SearchPermissionChecker searchPermissionChecker) {
932    
933                    PortalRuntimePermission.checkSetBeanProperty(
934                            getClass(), "searchPermissionChecker");
935    
936                    _searchPermissionChecker = searchPermissionChecker;
937            }
938    
939            private static void waitForSystemSearchEngine() {
940                    try {
941                            int count = 1000;
942    
943                            while (!_searchEngines.containsKey(SYSTEM_ENGINE_ID) &&
944                                       (--count > 0)) {
945    
946                                    if (_log.isDebugEnabled()) {
947                                            _log.debug("Waiting for search engine " + SYSTEM_ENGINE_ID);
948                                    }
949    
950                                    Thread.sleep(500);
951                            }
952                    }
953                    catch (InterruptedException ie) {
954                            _log.error(ie, ie);
955                    }
956            }
957    
958            private SearchEngineUtil() {
959                    Registry registry = RegistryUtil.getRegistry();
960    
961                    _serviceTracker = registry.trackServices(
962                            SearchEngineConfigurator.class,
963                            new SearchEngineConfiguratorServiceTrackerCustomizer());
964    
965                    _serviceTracker.open();
966            }
967    
968            private static final Log _log = LogFactoryUtil.getLog(
969                    SearchEngineUtil.class);
970    
971            private static final Set<Long> _companyIds = new HashSet<>();
972            private static String _defaultSearchEngineId;
973            private static final Set<String> _excludedEntryClassNames = new HashSet<>();
974            private static boolean _indexReadOnly = GetterUtil.getBoolean(
975                    PropsUtil.get(PropsKeys.INDEX_READ_ONLY));
976            private static final Map<String, SearchEngine> _searchEngines =
977                    new ConcurrentHashMap<>();
978            private static SearchPermissionChecker _searchPermissionChecker;
979    
980            private final ServiceTracker
981                    <SearchEngineConfigurator, SearchEngineConfigurator> _serviceTracker;
982    
983            private class SearchEngineConfiguratorServiceTrackerCustomizer
984                    implements ServiceTrackerCustomizer
985                            <SearchEngineConfigurator, SearchEngineConfigurator> {
986    
987                    @Override
988                    public SearchEngineConfigurator addingService(
989                            ServiceReference<SearchEngineConfigurator> serviceReference) {
990    
991                            Registry registry = RegistryUtil.getRegistry();
992    
993                            SearchEngineConfigurator searchEngineConfigurator =
994                                    registry.getService(serviceReference);
995    
996                            searchEngineConfigurator.afterPropertiesSet();
997    
998                            return searchEngineConfigurator;
999                    }
1000    
1001                    @Override
1002                    public void modifiedService(
1003                            ServiceReference<SearchEngineConfigurator> serviceReference,
1004                            SearchEngineConfigurator searchEngineConfigurator) {
1005                    }
1006    
1007                    @Override
1008                    public void removedService(
1009                            ServiceReference<SearchEngineConfigurator> serviceReference,
1010                            SearchEngineConfigurator searchEngineConfigurator) {
1011    
1012                            Registry registry = RegistryUtil.getRegistry();
1013    
1014                            registry.ungetService(serviceReference);
1015    
1016                            searchEngineConfigurator.destroy();
1017                    }
1018    
1019            }
1020    
1021    }