/*
* Created on Nov 15, 2012
*
* Copyright 2013 ATPCO Confidential and Proprietary. All Rights Reserved.
*/
package net.atpco.dds.offline.filing.common.dao;
import java.io.File;
import java.io.IOException;
import java.util.List;
import net.atpco.common.exception.ApplicationException;
import net.atpco.common.util.StringUtil;
import net.atpco.dds.common.DDSLogUtil;
import net.atpco.dds.offline.filing.common.exception.ObjectNotFoundException;
import net.atpco.dds.offline.filing.common.exception.TooManyObjectFoundException;
import net.atpco.dds.offline.filing.common.model.Entity;
import net.atpco.dds.offline.filing.common.service.DataWrite;
import net.atpco.dds.offline.filing.common.util.LuceneUtility;
import net.atpco.dds.offline.filing.model.SearchCriteria;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexCommit;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy;
import org.apache.lucene.index.PersistentSnapshotDeletionPolicy;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
/**
*
* This class provides methods to write data into lucene index files. It
* implements the DataWrite interface.
*
* @author Infosys
* @version 1.0
*
*/
@Repository("dataWrite")
@Scope("prototype")
public class LuceneDataWriteDAO extends LuceneDAO implements DataWrite {
/** This LOGGER is used to log information related to LuceneDataWriteDAO. */
private static final Logger LOGGER = DDSLogUtil.getLogger();
/** Variable to instantiate lucene based on memory or document.**/
private transient boolean memoryBase = true;
/**
* A constructor with one parameter.
*
* @param indexFilePath - String
* */
public LuceneDataWriteDAO(final String indexFilePath) {
super(indexFilePath);
createWriter(indexFilePath, true);
}
/**
* A constructor with two parameters.
*
* @param indexFilePath - String
* @param isBackupRequired - boolean
* */
public LuceneDataWriteDAO(final String indexFilePath, final boolean isBackupRequired) {
super(indexFilePath);
createWriter(indexFilePath, isBackupRequired);
}
/**
* A constructor with two parameters.
*
* @param indexFilePath - String
* @param isBackupRequired - boolean
* */
public LuceneDataWriteDAO(final String indexFilePath, final boolean isBackupRequired, final boolean memoryBase) {
super(indexFilePath);
this.memoryBase = memoryBase;
createWriter(indexFilePath, isBackupRequired);
}
/**
* This method is used to write object to lucene index file.
*
* @param objectToStore
* - Entity
* */
@Override
public void writeData(final Entity objectToStore) {
// writer and objectToStore are not null then add document into writer.
if (writer != null && objectToStore != null) {
// populate document matched with objectToStore
final Document document = createDocument(objectToStore);
try {
writer.addDocument(document);
} catch (final CorruptIndexException e) {
throw new ApplicationException(
"CorruptIndexException in method writeData", LOGGER, e);
} catch (final IOException e) {
throw new ApplicationException(
"IOException in method writeData", LOGGER, e);
}
} else if (writer == null) {
throw new ApplicationException(
"writer is null in method writeData.", LOGGER);
} else {
throw new ApplicationException(
"objectToStore is null in method writeData.", LOGGER);
}
}
/**
* This method is used to close reader,searcher, writer and snapshotter.
* */
@Override
public void closeDataWrite() {
try {
// Close searcher if it is not null.
if (searcher != null) {
searcher.close();
}
// Close writer if it is not null.
if(writer != null){
writer.commit();
writer.close();
}
// Close reader if it is not null.
if (reader != null) {
reader.close();
}
// Close the directory handler
if(directory != null){
directory.close();
}
} catch (final IOException e) {
throw new ApplicationException("Some IOException occurs in closeDataWrite.", LOGGER, e);
}
try {
if (snapshotter != null) {
snapshotter.close();
}
// Close the snapShotDir directory handler
if(snapShotDir != null){
snapShotDir.close();
}
} catch (final IOException e) {
throw new ApplicationException(
"Exception occured while closing the snapshotter.", LOGGER,
e);
}
}
/**
* This method is used to update the object in lucene schema. Lucene does
* not support the update option with the 'Query', So have to delete the
* existing data and create the new document.
*
* @param searchCriteria - List<SearchCriteria>
* @param objectToStore - Entity
* @throws TooManyObjectFoundException
* @throws ObjectNotFoundException
*/
@Override
public void updateData(final List<SearchCriteria> searchCriteria, final Entity objectToStore) throws ObjectNotFoundException, TooManyObjectFoundException {
// delete the some specific document as per searchCriteria from index file
deleteData(searchCriteria);
// write the newly objectToStore into index file
writeData(objectToStore);
}
/**
* This method is used to delete the object from lucene schema.
*
* @param searchCriterias - List<SearchCriteria>
* @throws ObjectNotFoundException
* @throws TooManyObjectFoundException
*/
private void deleteData(final List<SearchCriteria> searchCriterias) throws ObjectNotFoundException, TooManyObjectFoundException {
BooleanQuery bQuery = null;
try {
// Check reader and searcher are null or not.
createReaderAndSearcher();
// Form the query
bQuery = getExpressBooleanQuery(searchCriterias);
writer.deleteDocuments(bQuery);
// // Before deletion, check whether record exist for the filter criteria
// final TopDocs topDocs = searcher.search(bQuery, LuceneConstants.LUCENE_SEARCH_MAX);
//
// //If the length of topDocs is zero then throw object not found exception
// if (topDocs.scoreDocs.length == 0) {
// throw new ObjectNotFoundException("No matching record was found in method deleteData");
// }else if(topDocs.scoreDocs.length > 1){
// //If the length of topDocs is greater than one then throw too many object found exception
// throw new TooManyObjectFoundException("More than one record was found in method deleteData");
// }else {
// writer.deleteDocuments(bQuery);
// }
} catch (final IOException e) {
throw new ApplicationException("Some IOException occured in method deleteData.", LOGGER, e);
}
}
/**
* Take a snapshot of current data. The backup data will be uniquely
* identified using the snapshotname
*
* @param backupName
*/
@Override
public void backup(final String backupName) throws ApplicationException {
if (backupName == null || StringUtil.isEmpty(backupName)) {
throw new ApplicationException("Backup name is null or empty", LOGGER);
}
try {
writer.commit();
snapshotter.snapshot(backupName);
} catch (final IOException e) {
throw new ApplicationException(
"IO Exception occurred while taking backup for the snapshot "
+ backupName, LOGGER, e);
} catch (final Exception ex) {
throw new ApplicationException(
"Exception occurred while doing restore for the snapshot ", LOGGER, ex);
} finally {
closeDataWrite();
LuceneUtility.close(snapShotDir);
}
}
/**
* Restore the data from the backup to original index file
*
* @param backupName
*/
@Override
public void restore(final String backupName) throws ApplicationException {
IndexReader reader = null;
if (backupName == null && StringUtil.isEmpty(backupName)) {
throw new ApplicationException("Backup name is null or empty", LOGGER);
}
try {
// Get the commit point reference for the snapshot
final IndexCommit commit = snapshotter.getSnapshot(backupName);
// Read the data
reader = IndexReader.open(commit);
// Delete the current index file from the source schema
writer.deleteAll();
// Restore the data from commit point reference to the original
// index files
writer.addIndexes(reader);
// Commit the data
writer.commit();
} catch (final IOException e) {
throw new ApplicationException(
"IO Exception occurred while doing restore for the snapshot "
+ backupName, LOGGER, e);
} catch (final Exception ex) {
throw new ApplicationException(
"Exception occurred while doing restore for the snapshot "
+ backupName, LOGGER, ex);
} finally {
closeDataWrite();
LuceneUtility.close(snapShotDir);
LuceneUtility.close(directory);
LuceneUtility.close(reader);
}
}
/**
* Check if the backup exists already
* @param snapshotName
*/
@Override
public boolean isBackupExist(final String snapshotName) throws ApplicationException {
boolean hasBackup;
if(snapshotter!=null && snapshotter.isSnapshotted(snapshotName)) {
hasBackup = true;
} else {
hasBackup = false;
}
return hasBackup;
}
private void createWriter(final String indexFilePath, final boolean isBackupRequired) {
if (indexFilePath == null) {
throw new ApplicationException("Index Path shouldn't be null.", LOGGER);
}
try {
this.indexFilePath = indexFilePath;
final File indexFile = new File(indexFilePath);
// Open the indexFile which is used to write data.
directory = FSDirectory.open(indexFile);
// Define the configuration for this writer.
final IndexWriterConfig indexWriterConfig = new IndexWriterConfig(
Version.LUCENE_36, new SimpleAnalyzer(Version.LUCENE_36));
// Set the write mode of writer to CREATE_OR_APPEND.
indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
// Fix GI-1543 Start at 08 Apr, 2014
if(memoryBase){
// Set the RAM Buffer size, the default value is 16MB
indexWriterConfig.setRAMBufferSizeMB(48.0);
LOGGER.debug("Memory based Lucene write");
}else{
// Set the Buffered document
indexWriterConfig.setMaxBufferedDeleteTerms(50);
indexWriterConfig.setMaxBufferedDocs(50);
LOGGER.debug("Term based Lucene write");
}
// Fix GI-1543 End
// Generate the backup path based on the schema path
if (isBackupRequired) {
final String backupFile = LuceneUtility.generateBackupPath(indexFilePath);
if (backupFile != null) {
final File bFile = new File(backupFile);
snapShotDir = FSDirectory.open(bFile);
snapshotter = new PersistentSnapshotDeletionPolicy(
new KeepOnlyLastCommitDeletionPolicy(),
snapShotDir, OpenMode.CREATE_OR_APPEND,
Version.LUCENE_36);
// Set the snapshot policy
indexWriterConfig.setIndexDeletionPolicy(snapshotter);
} else {
LOGGER.warn("User Requested to take backup but the file location did not match");
}
}
// Make an instance of writer with the pre-defiend configuration.
writer = new IndexWriter(directory, indexWriterConfig);
} catch (final Exception e) {
throw new ApplicationException(
"Some IOException occured in method createWriter.", LOGGER,
e);
}
}
}