/*
* Copyright (C) 2000 - 2013 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://openbd.org/license/README.txt
*
* http://openbd.org/
*
* $Id: Collection.java 2523 2015-02-22 16:23:11Z alan $
*/
package com.bluedragon.search.collection;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.FileSystems;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Constants;
import com.bluedragon.search.AnalyzerFactory;
import com.bluedragon.search.DocumentWrap;
import com.bluedragon.search.index.DocumentWriter;
import com.nary.io.FileUtils;
public class Collection extends Object {
private String name;
private long created;
private boolean bStoreBody;
private String language, collectionpath;
// Lucene specific variables
private Directory directory = null;
private IndexSearcher indexsearcher = null;
private IndexWriter indexwriter = null;
private int totalDocs = -1;
private DocumentWriter documentwriter = null;
private long lastUsed = System.currentTimeMillis();
public String toString(){
return "[Collection name=" + name + "; path=" + collectionpath + "]";
}
public long getTimeSinceLastUsed(){
return System.currentTimeMillis() - lastUsed;
}
public void setName(String string) throws Exception {
this.name = string;
if ( this.name == null || this.name.length() == 0 )
throw new Exception("invalid collection name");
}
public void setLanguage(String string) {
this.language = string;
}
public String getLanguage(){
return this.language;
}
public void setStoreBody(boolean boolean1) {
bStoreBody = boolean1;
}
public boolean bStoreBody(){
return bStoreBody;
}
public String getName() {
return name;
}
public long getLastModified(){
return lastUsed;
}
public long getCreated(){
return created;
}
public String getPath(){
return collectionpath;
}
public void setDirectory(String path) throws Exception {
File filePath;
if ( !path.endsWith(name) )
filePath = new File( path, name );
else
filePath = new File( path );
if ( !filePath.exists() || !filePath.isDirectory() )
throw new Exception( "invalid collection path: " + path );
collectionpath = filePath.getCanonicalPath();
}
public void close(){
closeReader();
if ( indexwriter != null ){
try {
indexwriter.close();
} catch (IOException e){}
indexwriter = null;
}
}
public void closeReader(){
if ( indexsearcher != null ){
try {
indexsearcher.getIndexReader().close();
} catch ( IOException ignoreThisException ) {}
indexsearcher = null;
}
}
/**
* Retrieves the IndexSearcher object. This object is cached against this collection and is intended
* to be used amongst several threads. This is the recommended approach.
*
* If new content is added to this collection then it won't be available for searching until a new
* indexsearcher is created
*
* @return
* @throws CorruptIndexException
* @throws IOException
*/
public synchronized IndexSearcher getIndexSearcher() throws CorruptIndexException, IOException{
lastUsed = System.currentTimeMillis();
if ( indexsearcher != null )
return indexsearcher;
setDirectory();
indexsearcher = new IndexSearcher( DirectoryReader.open(directory) );
totalDocs = indexsearcher.getIndexReader().numDocs();
return indexsearcher;
}
public int getTotalDocs(){
return totalDocs;
}
public int size(){
int size = 0;
try {
setDirectory();
String[] files = directory.listAll();
for ( int i = 0; i < files.length; i++ ){
try {
size += directory.fileLength( files[i] );
} catch (IOException e) {}
}
} catch (IOException e1) {}
return size;
}
private void setDirectory() throws IOException {
if ( directory != null )
return;
if (Constants.WINDOWS) {
directory = new SimpleFSDirectory( FileSystems.getDefault().getPath(collectionpath) );
} else {
directory = new NIOFSDirectory( FileSystems.getDefault().getPath(collectionpath) );
}
File touchFile = new File( collectionpath, "openbd.created" );
if ( touchFile.exists() )
created = touchFile.lastModified();
else
created = System.currentTimeMillis();
}
/**
* Creates an empty collection to get it up and running
*/
public synchronized void create() throws IOException {
setDirectory();
if ( directory.listAll().length > 2 )
throw new IOException( "directory not empty; possible collection already present" );
IndexWriterConfig iwc = new IndexWriterConfig( AnalyzerFactory.get(language) );
iwc.setOpenMode( OpenMode.CREATE );
indexwriter = new IndexWriter(directory, iwc);
indexwriter.commit();
indexwriter.close();
indexwriter = null;
// throw an openbd.create file in there so we know when it was created
created = System.currentTimeMillis();
File touchFile = new File( collectionpath, "openbd.created" );
Writer fw = new FileWriter( touchFile );
fw.close();
}
/**
* Deletes this collection, removing all files
* @throws IOException
*/
public synchronized void delete() throws IOException {
close();
// delete the file
File f = new File(collectionpath);
if (f.exists() && f.isDirectory())
FileUtils.recursiveDelete(f, true);
}
public synchronized DocumentWriter getDocumentWriter() throws CorruptIndexException, LockObtainFailedException, IOException {
if ( documentwriter != null )
return documentwriter;
documentwriter = new DocumentWriter( this );
return documentwriter;
}
private void setIndexWriter() throws IOException{
if ( indexwriter != null )
return;
setDirectory();
IndexWriterConfig iwc = new IndexWriterConfig( AnalyzerFactory.get(language) );
iwc.setOpenMode( OpenMode.CREATE_OR_APPEND );
indexwriter = new IndexWriter(directory, iwc);
}
public synchronized void addDocuments(java.util.Collection<Document> pageCollection) throws CorruptIndexException, IOException {
setIndexWriter();
indexwriter.addDocuments(pageCollection);
closeWriter();
close();
}
public synchronized void deleteDocument(DocumentWrap docwrap, boolean closeOptimize ) throws IOException {
setIndexWriter();
indexwriter.deleteDocuments( new Term(DocumentWrap.ID, docwrap.getId()) );
if ( closeOptimize ){
closeWriter();
}
}
public synchronized void closeWriter() throws CorruptIndexException, IOException{
indexwriter.commit();
indexwriter.close();
indexwriter = null;
}
public synchronized void deleteAll() throws IOException {
setIndexWriter();
indexwriter.deleteAll();
closeWriter();
close();
}
}