/** * This software is licensed to you under the Apache License, Version 2.0 (the * "Apache License"). * * LinkedIn's contributions are made under the Apache License. If you contribute * to the Software, the contributions will be deemed to have been made under the * Apache License, unless you expressly indicate otherwise. Please do not make any * contributions that would be inconsistent with the Apache License. * * You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, this software * distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache * License for the specific language governing permissions and limitations for the * software governed under the Apache License. * * © 2012 LinkedIn Corp. All Rights Reserved. */ package com.senseidb.search.plugin; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.apache.lucene.index.IndexReader; import org.json.JSONException; import org.json.JSONObject; import proj.zoie.api.Zoie; import proj.zoie.api.ZoieIndexReader; import proj.zoie.api.ZoieMultiReader; import proj.zoie.api.ZoieSegmentReader; import proj.zoie.hourglass.impl.HourglassListener; import com.browseengine.bobo.facets.FacetHandler; import com.senseidb.conf.SenseiSchema; import com.senseidb.indexing.ShardingStrategy; import com.senseidb.indexing.activity.CompositeActivityManager; import com.senseidb.indexing.activity.deletion.DeletionListener; import com.senseidb.plugin.SenseiPluginRegistry; import com.senseidb.search.node.SenseiCore; public class PluggableSearchEngineManager implements DeletionListener, HourglassListener<IndexReader, IndexReader> { private final static Logger logger = Logger.getLogger(PluggableSearchEngineManager.class); private ShardingStrategy shardingStrategy; private SenseiCore senseiCore; private String version; private Comparator<String> versionComparator; private SenseiSchema senseiSchema; private SenseiPluginRegistry pluginRegistry; private int nodeId; private List<PluggableSearchEngine> pluggableEngines = new ArrayList<PluggableSearchEngine>(); private int maxPartition; private boolean acceptEventsForAllPartitions; public PluggableSearchEngineManager() { } public String getOldestVersion() { //as for now lets take into account only zoie persistent version return null; } public boolean acceptEventsForAllPartitions() { return acceptEventsForAllPartitions; } public final void init(String indexDirectory, int nodeId, SenseiSchema senseiSchema, Comparator<String> versionComparator, SenseiPluginRegistry pluginRegistry, ShardingStrategy shardingStrategy) { this.nodeId = nodeId; this.senseiSchema = senseiSchema; this.versionComparator = versionComparator; this.pluginRegistry = pluginRegistry; this.shardingStrategy = shardingStrategy; maxPartition = pluginRegistry.getConfiguration().getInt("sensei.index.manager.default.maxpartition.id", 0) + 1; pluggableEngines = new ArrayList<PluggableSearchEngine>(pluginRegistry.resolveBeansByListKey("sensei.search.pluggableEngines", PluggableSearchEngine.class)); if (CompositeActivityManager.activitiesPresent(senseiSchema)) { pluggableEngines.add(new CompositeActivityManager()); } acceptEventsForAllPartitions = false; for (PluggableSearchEngine engine : pluggableEngines) { engine.init(indexDirectory, nodeId, senseiSchema, versionComparator, pluginRegistry, shardingStrategy); if (engine.acceptEventsForAllPartitions()) { acceptEventsForAllPartitions = true; } } initVersion(versionComparator); } public void initVersion(Comparator<String> versionComparator) { List<String> versions = new ArrayList<String>(); for (PluggableSearchEngine engine : pluggableEngines) { if (engine.getVersion() != null && !"".equals(engine.getVersion())) { versions.add(engine.getVersion()); } } if (versions.size() > 0) { String version = versions.get(0); for (String ver : versions) { if (versionComparator.compare(version, ver) > 0) { this.version = ver; } } } } /** * Updates all the corresponding activity columns found in the document * @param event * @param version * @return */ public JSONObject update(JSONObject event, String version) { if (this.version != null && versionComparator.compare(this.version, version) > 0) { return event; } else { this.version = version; } boolean validForCurrentNode; try { validForCurrentNode = Arrays.binarySearch(senseiCore.getPartitions(), shardingStrategy.caculateShard(maxPartition, event)) >= 0; } catch (JSONException e) { throw new RuntimeException(e); } for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { if (pluggableSearchEngine.acceptEventsForAllPartitions() || validForCurrentNode) { try { event = pluggableSearchEngine.acceptEvent(event, version); } catch (Exception ex) { logger.error(ex.getMessage(), ex); } } } return event; } public void close() { for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { pluggableSearchEngine.stop(); } } /* (non-Javadoc) * @see com.senseidb.indexing.activity.deletion.DeletionListener#onDelete(org.apache.lucene.index.IndexReader, long[]) */ @Override public void onDelete(IndexReader indexReader, long... uids) { for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { pluggableSearchEngine.onDelete(indexReader, uids); } } @Override public void onNewZoie(Zoie<IndexReader, IndexReader> zoie) { } @Override public void onRetiredZoie(Zoie<IndexReader, IndexReader> zoie) { } @Override public void onIndexReaderCleanUp(ZoieIndexReader<IndexReader> indexReader) { if (indexReader instanceof ZoieMultiReader) { ZoieSegmentReader[] segments = (ZoieSegmentReader[]) ((ZoieMultiReader) indexReader).getSequentialSubReaders(); for (ZoieSegmentReader segmentReader : segments) { handleSegment(segmentReader); } } else if (indexReader instanceof ZoieSegmentReader) { handleSegment((ZoieSegmentReader) indexReader); } else { throw new UnsupportedOperationException("Only segment and multisegment readers can be handled"); } } private void handleSegment(ZoieSegmentReader segmentReader) { onDelete(segmentReader, segmentReader.getUIDArray()); } public void start(SenseiCore senseiCore) { this.senseiCore = senseiCore; for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { pluggableSearchEngine.start(senseiCore); } } public Set<String> getFieldNames() { Set<String> ret = new HashSet<String>(); for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { ret.addAll(pluggableSearchEngine.getFieldNames()); } return ret; } public List<FacetHandler<?>> createFacetHandlers() { List<FacetHandler<?>> ret = new ArrayList<FacetHandler<?>>(); for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { ret.addAll(pluggableSearchEngine.createFacetHandlers()); } return ret; } public Set<String> getFacetNames() { Set<String> ret = new HashSet<String>(); for (PluggableSearchEngine pluggableSearchEngine : pluggableEngines) { ret.addAll(pluggableSearchEngine.getFacetNames()); } return ret; } public List<PluggableSearchEngine> getPluggableEngines() { return pluggableEngines; } }