001: /*
002: * $Header: /cvsroot/mvnforum/mvnforum/src/com/mvnforum/search/member/MemberIndexer.java,v 1.24 2008/01/15 11:18:01 minhnn Exp $
003: * $Author: minhnn $
004: * $Revision: 1.24 $
005: * $Date: 2008/01/15 11:18:01 $
006: *
007: * ====================================================================
008: *
009: * Copyright (C) 2002-2007 by MyVietnam.net
010: *
011: * All copyright notices regarding mvnForum MUST remain
012: * intact in the scripts and in the outputted HTML.
013: * The "powered by" text/logo with a link back to
014: * http://www.mvnForum.com and http://www.MyVietnam.net in
015: * the footer of the pages MUST remain visible when the pages
016: * are viewed on the internet or intranet.
017: *
018: * This program is free software; you can redistribute it and/or modify
019: * it under the terms of the GNU General Public License as published by
020: * the Free Software Foundation; either version 2 of the License, or
021: * any later version.
022: *
023: * This program is distributed in the hope that it will be useful,
024: * but WITHOUT ANY WARRANTY; without even the implied warranty of
025: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
026: * GNU General Public License for more details.
027: *
028: * You should have received a copy of the GNU General Public License
029: * along with this program; if not, write to the Free Software
030: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
031: *
032: * Support can be obtained from support forums at:
033: * http://www.mvnForum.com/mvnforum/index
034: *
035: * Correspondence and Marketing Questions can be sent to:
036: * info at MyVietnam net
037: *
038: * @author: Minh Nguyen
039: * @author: Dejan Krsmanovic dejan_krsmanovic@yahoo.com
040: */
041: package com.mvnforum.search.member;
042:
043: import java.io.IOException;
044:
045: import net.myvietnam.mvncore.exception.SearchException;
046: import net.myvietnam.mvncore.util.DateUtil;
047: import net.myvietnam.mvncore.util.TimerUtil;
048:
049: import org.apache.commons.logging.Log;
050: import org.apache.commons.logging.LogFactory;
051: import org.apache.lucene.analysis.Analyzer;
052: import org.apache.lucene.analysis.standard.StandardAnalyzer;
053: import org.apache.lucene.document.*;
054: import org.apache.lucene.document.DateTools.Resolution;
055: import org.apache.lucene.index.*;
056: import org.apache.lucene.store.Directory;
057:
058: import com.mvnforum.*;
059: import com.mvnforum.db.MemberBean;
060: import com.mvnforum.service.MvnForumServiceFactory;
061: import com.mvnforum.service.SearchService;
062:
063: public class MemberIndexer {
064:
065: private static Log log = LogFactory.getLog(MemberIndexer.class);
066:
067: //Field names (used for indexing)
068: public static final String FIELD_MEMBER_ID = "MemberID";
069: public static final String FIELD_MEMBER_NAME = "MemberName";
070: public static final String FIELD_CREATION_DATE = "MemberCreationDate";
071: public static final String FIELD_MEMBER_EMAIL = "MemberEmail";
072: public static final String FIELD_MEMBER_LASTNAME = "MemberLastName";
073: public static final String FIELD_MEMBER_FIRSTNAME = "MemberFirstName";
074: public static final String FIELD_MEMBER_COUNTRY = "MemberCountry";
075:
076: //public static final String PROPERTY_SEARCH_PATH = "search.path";
077: //public static final String PROPERTY_SEARCH_AUTOINDEX = "search.autoindex";
078:
079: //Timer is used for scheduling jobs
080: private static Analyzer analyzer;
081:
082: private static long lastOptimizeTime = 0;
083:
084: static {
085: initializeAnalyzer();
086: }
087:
088: public static void scheduleAddMemberTask(MemberBean MemberBean) {
089: AddUpdateMemberIndexTask task = new AddUpdateMemberIndexTask(
090: MemberBean, AddUpdateMemberIndexTask.OPERATION_ADD);
091: TimerUtil.getInstance().schedule(task, 0);
092: }
093:
094: public static void scheduleUpdateMemberTask(MemberBean MemberBean) {
095: AddUpdateMemberIndexTask task = new AddUpdateMemberIndexTask(
096: MemberBean, AddUpdateMemberIndexTask.OPERATION_UPDATE);
097: TimerUtil.getInstance().schedule(task, 0);
098: }
099:
100: public static void scheduleDeleteMemberTask(int objectID) {
101: DeleteMemberIndexTask task = new DeleteMemberIndexTask(objectID);
102: TimerUtil.getInstance().schedule(task, 0);
103: }
104:
105: public static void scheduleRebuildIndexTask() {
106: int maxMemberID = 0;
107: RebuildMemberIndexTask task = new RebuildMemberIndexTask(
108: maxMemberID);
109: TimerUtil.getInstance().schedule(task, 0);
110: }
111:
112: static Analyzer getAnalyzer() {
113: return analyzer;
114: }
115:
116: /**
117: * This class will load analyzer when starting. If specified analyzer class
118: * cannot be loaded then default analyzer will be used.
119: */
120: private static void initializeAnalyzer() {
121: String analyzerClassName = MVNForumFactoryConfig
122: .getLuceneAnalyzerClassName();
123: if ((analyzerClassName == null)
124: || (analyzerClassName.equals(""))) {
125: //create standard analyzer
126: //String[] stopWords = this.loadStopWords();
127: analyzer = new StandardAnalyzer();
128: log.debug("Using StandardAnalyzer for indexing");
129: } else {
130: //try to create specified analyzer
131: try {
132: log.debug("About to load Analyzer ["
133: + analyzerClassName + "] for indexing");
134: analyzer = (Analyzer) Class.forName(analyzerClassName)
135: .newInstance();
136: } catch (Exception e) {
137: log.warn("Cannot load " + analyzerClassName
138: + ". Loading StandardAnalyzer");
139: analyzer = new StandardAnalyzer();
140: }
141: }
142: }
143:
144: /**
145: * This method is used for getting new IndexWriter. It can create new index
146: * or add Member to existing index. Creating new index will delete previous so it
147: * should be used for rebuilding index.
148: * @param create - true if new index should be created.
149: * - false for adding members to existing index
150: * @return IndexWriter object that is used for adding members to index
151: */
152: static IndexWriter getIndexWriter(Directory directory,
153: boolean create) throws SearchException {
154:
155: IndexWriter writer = null;
156:
157: SearchService service = MvnForumServiceFactory
158: .getMvnForumService().getSearchService();
159: //If create = false, we will create IndexWriter with false argument
160: if (create == false) {
161: try {
162: writer = new IndexWriter(directory, analyzer, false);
163: if (service.saveMemberOnDisk()) {
164: writer.setUseCompoundFile(true);
165: }
166: return writer;
167: } catch (IOException e) {
168: log
169: .warn(
170: "Cannot open existed index. New index will be created.",
171: e);
172: //Ignore Exception. We will try to create index with true parameter
173: }
174: }
175: // We are here in two cases: We wanted to create new index or because
176: // index doesn't existed
177: try {
178: //This will create new index and delete existing
179: service.deleteContent(directory);
180: writer = new IndexWriter(directory, analyzer, true);// actually the directory should be 'create' = true
181: if (service.saveMemberOnDisk()) {
182: writer.setUseCompoundFile(true);
183: }
184: return writer;
185: } catch (IOException e) {
186: //@todo : localize me
187: log.error("IOException during get index writer", e);
188: throw new SearchException(
189: "Error while creating index writer");
190: }
191: }
192:
193: /**
194: * This method is used for adding single Member to index
195: * Note: this method does not close the writer
196: * @param MemberBean A Member that should be indexed
197: * @param writer IndexWriter that is used for storing
198: * @throws SearchException
199: */
200: static void doIndexMember(MemberBean memberBean, IndexWriter writer)
201: throws SearchException {
202: if (memberBean == null)
203: return;
204: if ((memberBean.getMemberName() == null)
205: || memberBean.getMemberName().equals("")
206: || (memberBean.getMemberCreationDate() == null)) {
207: return;
208: }
209:
210: //Each Member will be represented as a document
211: Document memberDocument = new Document();
212: //Document has following fields that could be queried on
213: memberDocument.add(new Field(FIELD_MEMBER_ID, Integer
214: .toString(memberBean.getMemberID()), Field.Store.YES,
215: Field.Index.UN_TOKENIZED));
216: memberDocument
217: .add(new Field(FIELD_MEMBER_NAME, memberBean
218: .getMemberName(), Field.Store.NO,
219: Field.Index.TOKENIZED));
220: memberDocument.add(new Field(FIELD_MEMBER_EMAIL, memberBean
221: .getMemberEmail(), Field.Store.NO,
222: Field.Index.TOKENIZED));
223: memberDocument.add(new Field(FIELD_MEMBER_LASTNAME, memberBean
224: .getMemberLastname(), Field.Store.NO,
225: Field.Index.TOKENIZED));
226: memberDocument.add(new Field(FIELD_MEMBER_FIRSTNAME, memberBean
227: .getMemberFirstname(), Field.Store.NO,
228: Field.Index.TOKENIZED));
229: memberDocument.add(new Field(FIELD_MEMBER_COUNTRY, memberBean
230: .getMemberCountry(), Field.Store.NO,
231: Field.Index.TOKENIZED));
232: memberDocument.add(new Field(FIELD_CREATION_DATE, DateTools
233: .dateToString(memberBean.getMemberCreationDate(),
234: Resolution.MILLISECOND), Field.Store.YES,
235: Field.Index.UN_TOKENIZED));
236: //now we have created document with fields so we can store it
237: try {
238: writer.addDocument(memberDocument);
239: } catch (IOException e) {
240: log.error("MemberIndexer.doIndexMember failed", e);
241: //@todo : localize me
242: throw new SearchException(
243: "Error writing new member to index");
244: } catch (Throwable e) {
245: log.error("MemberIndexer.doIndexMember failed", e);
246: //@todo : localize me
247: throw new SearchException(
248: "Error writing new member to index");
249: }
250: }
251:
252: /**
253: * Add single Member to index
254: * @param MemberBean
255: * @throws SearchException
256: */
257: static void addMemberToIndex(MemberBean memberBean)
258: throws SearchException, IOException {
259:
260: Directory directory = null;
261: IndexWriter writer = null;
262: SearchService service = MvnForumServiceFactory
263: .getMvnForumService().getSearchService();
264: try {
265: directory = service.getSearchMemberIndexDir();
266: writer = getIndexWriter(directory, false);
267: if (writer == null) {
268: log.warn("Cannot get the IndexWriter");
269: return;
270: }
271: doIndexMember(memberBean, writer);
272:
273: // now check if we should optimize index (each hour)
274: long now = System.currentTimeMillis();
275: long timeFromLastOptimize = now - lastOptimizeTime;
276: if (service.saveMemberOnDisk()
277: && (timeFromLastOptimize > DateUtil.HOUR)) {
278: log
279: .debug("writer.optimize() called in addMemberToIndex");
280: writer.optimize();
281: lastOptimizeTime = now;
282: }
283: } catch (SearchException ex) {
284: throw ex;
285: } finally {
286: if (writer != null) {
287: try {
288: writer.close();
289: } catch (IOException e) {
290: log.debug("Error closing Lucene IndexWriter", e);
291: }
292: }
293: if (directory != null) {
294: try {
295: directory.close();
296: } catch (IOException e) {
297: log.debug("Cannot close directory.", e);
298: }
299: }
300: }
301: }
302:
303: /**
304: * This method is used for deleting Member from index.
305: * @param MemberID id of the Member that should be deleted
306: * @throws SearchException
307: */
308: static void deleteMemberFromIndex(int memberID)
309: throws SearchException {
310:
311: Directory directory = null;
312: IndexReader reader = null;
313: try {
314: SearchService service = MvnForumServiceFactory
315: .getMvnForumService().getSearchService();
316: directory = service.getSearchMemberIndexDir();
317: reader = IndexReader.open(directory);
318: if (reader == null) {
319: log.warn("Cannot get the IndexReader");
320: return;
321: }
322:
323: Term term = new Term(FIELD_MEMBER_ID, String
324: .valueOf(memberID));
325: int deletedCount = reader.deleteDocuments(term);
326: log.debug("deleteMemberFromIndex: deleted member = "
327: + deletedCount);
328: } catch (IOException e) {
329: //@todo : localize me
330: throw new SearchException(
331: "Error trying to delete Member with memberID = "
332: + memberID);
333: } finally {
334: if (reader != null) {
335: try {
336: reader.close();
337: } catch (IOException e) {
338: log.debug("Error closing Lucene IndexReader", e);
339: }
340: }
341: if (directory != null) {
342: try {
343: directory.close();
344: } catch (IOException e) {
345: log.debug("Cannot close directory.", e);
346: }
347: }
348: }
349: }
350:
351: public static int getNumDocs() {
352:
353: int numDocs = -1;
354: Directory directory = null;
355: IndexReader reader = null;
356: try {
357: SearchService service = MvnForumServiceFactory
358: .getMvnForumService().getSearchService();
359: directory = service.getSearchMemberIndexDir();
360: reader = IndexReader.open(directory);
361: if (reader == null) {
362: log.warn("Cannot get the IndexReader");
363: return -1;
364: }
365: numDocs = reader.numDocs();
366: } catch (IOException ioe) {
367: //ignore
368: ioe.printStackTrace();
369: } finally {
370: if (reader != null) {
371: try {
372: reader.close();
373: } catch (IOException e) {
374: log.debug("Error closing Lucene IndexReader", e);
375: }
376: }
377: if (directory != null) {
378: try {
379: directory.close();
380: } catch (IOException e) {
381: log.debug("Cannot close directory.", e);
382: }
383: }
384: }
385: return numDocs;
386: }
387:
388: }
|