001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.search.system;
019:
020: import java.io.IOException;
021: import java.util.Collection;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Set;
026:
027: import javax.transaction.SystemException;
028:
029: import org.apache.lucene.document.Document;
030: import org.apache.lucene.search.Query;
031:
032: import de.finix.contelligent.CallData;
033: import de.finix.contelligent.Component;
034: import de.finix.contelligent.ComponentCreationException;
035: import de.finix.contelligent.ComponentManager;
036: import de.finix.contelligent.ComponentNotFoundException;
037: import de.finix.contelligent.ComponentPath;
038: import de.finix.contelligent.Contelligent;
039: import de.finix.contelligent.Session;
040: import de.finix.contelligent.SessionCreationException;
041: import de.finix.contelligent.core.BasicComponentManager;
042: import de.finix.contelligent.core.ContelligentFactory;
043: import de.finix.contelligent.core.security.ContelligentSecurityManager;
044: import de.finix.contelligent.exception.ComponentPersistenceException;
045: import de.finix.contelligent.logging.LoggingService;
046: import de.finix.contelligent.search.SearchException;
047: import de.finix.contelligent.search.SearchResult;
048: import de.finix.contelligent.search.SearchResultCollector;
049: import de.finix.contelligent.search.engine.LockingLuceneIndexLockPolicy;
050: import de.finix.contelligent.search.engine.LuceneDocumentFactory;
051: import de.finix.contelligent.search.engine.LuceneIndex;
052: import de.finix.contelligent.search.engine.LuceneIndexMutator;
053: import de.finix.contelligent.search.engine.LuceneIndexMutatorAdapter;
054: import de.finix.contelligent.search.engine.RawDocumentFactory;
055: import de.finix.contelligent.search.engine.RealtimeLuceneIndexBuildPolicy;
056: import de.finix.contelligent.xml.elements.AnalyzerElement;
057: import de.finix.contelligent.xml.elements.IndexElement;
058:
059: public class SystemIndex {
060:
061: final static org.apache.log4j.Logger log = LoggingService
062: .getLogger(SystemIndex.class);
063:
064: private static final Set typeNames = new HashSet();
065:
066: static {
067: typeNames.add("contelligent.core.Component");
068: }
069:
070: private ComponentManager cm;
071:
072: private Contelligent contelligent;
073:
074: private IndexElement indexElement;
075:
076: private LuceneIndex index;
077:
078: private LuceneDocumentFactory documentFactory;
079:
080: private ComponentManager parentCM;
081:
082: public SystemIndex(ComponentManager cm) throws IOException {
083:
084: this .cm = cm;
085: this .parentCM = cm.getParent();
086: this .contelligent = ContelligentFactory.getContelligent();
087:
088: indexElement = new IndexElement();
089: indexElement.setName("system." + cm.getName());
090: indexElement.setType(IndexElement.TYPE_RAW);
091: indexElement.setAnalyzerElement(new AnalyzerElement());
092: indexElement.getAnalyzerElement().setType("default");
093:
094: index = new LuceneIndex(
095: indexElement,
096: contelligent,
097: new LockingLuceneIndexLockPolicy(),
098: new RealtimeLuceneIndexBuildPolicy(
099: indexElement,
100: contelligent
101: .getContelligentDir(Contelligent.DIR_INDEX)));
102:
103: documentFactory = new RawDocumentFactory(cm);
104: }
105:
106: public void recreate(final CallData callData)
107: throws SystemException, IOException {
108: final boolean debugEnabled = log.isDebugEnabled();
109:
110: index.apply(new LuceneIndexMutator() {
111: public void perform(LuceneIndexMutatorAdapter adapter)
112: throws Exception {
113: Set subComponents = cm
114: .getComponentsInSubtreeFilteredByType(
115: ComponentPath.ROOT_PATH, typeNames);
116: Iterator iterator = subComponents.iterator();
117: adapter.delete();
118: adapter.prepare();
119:
120: ComponentPath cp;
121: while (iterator.hasNext()) {
122: cp = (ComponentPath) iterator.next();
123: try {
124:
125: Component component = cm.getComponent(cp,
126: callData, false);
127:
128: Collection docs = documentFactory
129: .createDocuments(component);
130: if (debugEnabled) {
131: log.debug(docs.size() + " documents for "
132: + cp);
133: }
134: Iterator documents = docs.iterator();
135:
136: while (documents.hasNext()) {
137: Document document = (Document) documents
138: .next();
139: adapter.add(document);
140: }
141: } catch (Exception e) {
142: log.warn(
143: "Could not add component to system index (path="
144: + cp + "). ", e);
145: }
146: }
147: }
148: });
149: }
150:
151: public void update(final List<ComponentPath> paths)
152: throws SessionCreationException, SystemException,
153: IOException {
154:
155: if (paths == null) {
156: return;
157: }
158:
159: final boolean debugEnabled = log.isDebugEnabled();
160:
161: try {
162: contelligent.getComponentManager(cm.getName());
163: } catch (IllegalArgumentException e) {
164: if (debugEnabled) {
165: log
166: .debug("cm for system index does not exist anymore.");
167: }
168: return;
169: }
170:
171: log.debug(paths.size() + " components in context '"
172: + cm.getName() + "' will be indexed now...");
173:
174: index.apply(new LuceneIndexMutator() {
175: public void perform(LuceneIndexMutatorAdapter adapter)
176: throws Exception {
177:
178: Session session = contelligent.beginSession(
179: ContelligentSecurityManager.getIndexUser(), cm);
180: ComponentPath targetPath = null;
181:
182: try {
183: contelligent.beginTx(3600);
184:
185: // create callData
186: CallData callData = contelligent
187: .createCallData(session);
188:
189: Iterator iterator = paths.iterator();
190:
191: while (iterator.hasNext()) {
192: targetPath = (ComponentPath) iterator.next();
193:
194: if (debugEnabled) {
195: log.debug(SystemIndex.this + " path='"
196: + targetPath + "'");
197: }
198:
199: if (cm.componentExists(targetPath)) {
200: // if component exists, the component has been added or changed
201:
202: Component component = null;
203: try {
204: component = cm.getComponent(targetPath,
205: callData, false); // we don't follow links and don't check, if link target is valid
206: } catch (ComponentCreationException e) {
207: log.debug("Could not get component '"
208: + targetPath + "'. ", e);
209: } catch (ComponentNotFoundException e) {
210: // if targetPath points to a dynamic ComponentReference, getComponent() might throw an exception
211: log.debug("Could not find component '"
212: + targetPath + "'. ", e);
213: }
214:
215: Collection docs = null;
216:
217: if (component != null) {
218:
219: if (debugEnabled) {
220: log
221: .debug(SystemIndex.this
222: + " add/update component '"
223: + targetPath
224: + "' type="
225: + component
226: .getComponentContext()
227: .getType()
228: .getName());
229: }
230:
231: docs = documentFactory
232: .createDocuments(component);
233:
234: } else {
235: if (debugEnabled) {
236: log.debug(SystemIndex.this
237: + " add/update component '"
238: + targetPath + "'.");
239: }
240:
241: docs = documentFactory
242: .createDocuments(targetPath);
243: }
244:
245: if (debugEnabled) {
246: log.debug(docs.size()
247: + " documents for '"
248: + targetPath + "'.");
249: }
250:
251: Iterator documents = docs.iterator();
252:
253: while (documents.hasNext()) {
254: Document document = (Document) documents
255: .next();
256: adapter.add(document);
257: }
258:
259: } else {
260: // if component doesn't exists, the component has been removed
261:
262: if (debugEnabled) {
263: log.debug(SystemIndex.this
264: + " remove component '"
265: + targetPath + "'.");
266: }
267:
268: adapter.remove(targetPath.toPath());
269:
270: if (!cm.isRoot()) {
271: // XXX: is it possible to replace this method by removing all existing subcomponent from the index?
272: Set subComponents = parentCM
273: .getComponentsInSubtreeFilteredByType(
274: targetPath, typeNames);
275: Iterator sub = subComponents.iterator();
276:
277: while (sub.hasNext()) {
278: ComponentPath component = (ComponentPath) sub
279: .next();
280:
281: if (debugEnabled) {
282: log
283: .debug(SystemIndex.this
284: + " remove subcomponent '"
285: + component
286: + "'.");
287: }
288:
289: adapter.remove(component.toPath());
290: }
291: }
292: }
293: }
294: contelligent.commitTx();
295: } catch (IOException e) {
296: contelligent.rollbackTx();
297: log.warn(
298: "Could not add/remove component to/from system index (component="
299: + targetPath + "). ", e);
300: } catch (ComponentPersistenceException e) {
301: contelligent.rollbackTx();
302: log.warn(
303: "Could not get components below component '"
304: + targetPath + "'. ", e);
305: } catch (Exception e) {
306: contelligent.rollbackTx();
307: log.warn(
308: "Could not create documents for system index (component="
309: + targetPath + "). ", e);
310: } finally {
311: if (session != null) {
312: contelligent.invalidateSession(session);
313: }
314: }
315: }
316: });
317: }
318:
319: public ComponentManager getCm() {
320: return cm;
321: }
322:
323: public SearchResult search(String query) throws SearchException {
324: return search(query, null);
325: }
326:
327: public SearchResult search(Query query) throws SearchException {
328: return search(query, null);
329: }
330:
331: public SearchResult search(Query query,
332: SearchResultCollector collector) throws SearchException {
333: if (cm.isRoot()) {
334: return index.search(query, 0, -1, null, collector);
335: } else {
336: return hierarchySearch(query, collector);
337: }
338: }
339:
340: public SearchResult search(String query,
341: SearchResultCollector collector) throws SearchException {
342: if (cm.isRoot()) {
343: return index.search(query, 0, -1, collector);
344: } else {
345: return hierarchySearch(query, collector);
346: }
347: }
348:
349: public void destroy() {
350: index.delete();
351: }
352:
353: public String toString() {
354: return "system." + cm.getName();
355: }
356:
357: public void listDocuments() throws IOException {
358: index.listDocuments();
359: }
360:
361: public void listTerms() throws IOException {
362: index.listTerms();
363: }
364:
365: private SearchResult hierarchySearch(String query,
366: SearchResultCollector collector) throws SearchException {
367: SystemIndex parentIndex = ((BasicComponentManager) cm
368: .getParent()).getSystemIndex();
369: SearchResult parentResult = parentIndex
370: .search(query, collector);
371: SearchResult localResult = index.search(query, 0, -1, null,
372: collector);
373:
374: return mergeResults(parentResult, localResult);
375: }
376:
377: private SearchResult hierarchySearch(Query query,
378: SearchResultCollector collector) throws SearchException {
379: SystemIndex parentIndex = ((BasicComponentManager) cm
380: .getParent()).getSystemIndex();
381: SearchResult parentResult = parentIndex
382: .search(query, collector);
383: SearchResult localResult = index.search(query, 0, -1, null,
384: collector);
385:
386: return mergeResults(parentResult, localResult);
387: }
388:
389: private SearchResult mergeResults(SearchResult parentResult,
390: SearchResult localResult) {
391: SearchResult answer = new SearchResult(parentResult.getLength()
392: + localResult.getLength(), 0);
393: HashSet paths = new HashSet();
394:
395: Iterator iterator = localResult.iterator();
396: while (iterator.hasNext()) {
397: SearchResult.SearchResultItem item = (SearchResult.SearchResultItem) iterator
398: .next();
399:
400: answer.add(item.getDocument(), item.getScore());
401: paths.add(item.getPath());
402: }
403:
404: iterator = parentResult.iterator();
405: while (iterator.hasNext()) {
406: SearchResult.SearchResultItem item = (SearchResult.SearchResultItem) iterator
407: .next();
408:
409: if (paths.contains(item.getPath())) {
410: continue;
411: }
412: ComponentPath componentPath = new ComponentPath(item
413: .getPath());
414:
415: if (parentCM.componentExists(componentPath)
416: && cm.componentExists(componentPath, false)) {
417: continue; // overwritten locally, ignore
418: }
419: if (cm.componentExists(componentPath)) {
420: answer.add(item.getDocument(), item.getScore());
421: paths.add(item.getPath());
422: }
423: }
424: return answer;
425: }
426: }
|