/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.compass.core.lucene.engine.store;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.SimpleFSLockFactory;
import org.compass.core.CompassException;
import org.compass.core.config.CompassConfigurable;
import org.compass.core.config.CompassEnvironment;
import org.compass.core.config.CompassSettings;
import org.compass.core.engine.SearchEngineException;
import org.compass.core.util.FileSystemUtils;
/**
* A directory store implemented using a file system. Uses Lucene {@link org.apache.lucene.store.FSDirectory}.
*
* @author kimchy
*/
public class FSDirectoryStore extends AbstractDirectoryStore implements CompassConfigurable {
private static final Log log = LogFactory.getLog(FSDirectoryStore.class);
public static final String PROTOCOL = "file://";
private String indexPath;
public void configure(CompassSettings settings) throws CompassException {
String connection = settings.getSetting(CompassEnvironment.CONNECTION);
indexPath = findIndexPath(connection);
// Make sure we use the FSDirectory
System.setProperty("org.apache.lucene.FSDirectory.class", getFSDirectoryClass());
FSDirectory directory;
try {
directory = FSDirectory.getDirectory(System.getProperty("java.io.tmpdir"));
directory.close();
} catch (IOException e) {
throw new SearchEngineException("Failed to open the lucene directory", e);
}
if (!directory.getClass().getName().equals(getFSDirectoryClass())) {
throw new SearchEngineException("Setting type of FS directory is a JVM "
+ "level setting, you can not set different values within the same JVM");
}
}
protected String findIndexPath(String connection) {
if (connection.startsWith(PROTOCOL)) {
return connection.substring(PROTOCOL.length());
}
return connection;
}
public Directory open(String subContext, String subIndex) throws SearchEngineException {
try {
String path = buildPath(subContext, subIndex);
Directory dir = FSDirectory.getDirectory(path);
dir.setLockFactory(new SimpleFSLockFactory(path));
return dir;
} catch (IOException e) {
throw new SearchEngineException("Failed to open directory for path [" + subIndex + "]", e);
}
}
@Override
public String[] listSubIndexes(String subContext) throws SearchEngineException, UnsupportedOperationException {
File subContextIndex = new File(indexPath + "/" + subContext);
if (!subContextIndex.exists()) {
return null;
}
File[] subIndexFiles = subContextIndex.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
String[] subIndexes = new String[subIndexFiles.length];
for (int i = 0; i < subIndexFiles.length; i++) {
subIndexes[i] = subIndexFiles[i].getName();
}
return subIndexes;
}
@Override
public void deleteIndex(Directory dir, String subContext, String subIndex) throws SearchEngineException {
File indexPathFile = new File(buildPath(subContext, subIndex));
if (indexPathFile.exists()) {
boolean deleted = false;
// do this retries for windows...
for (int i = 0; i < 10; i++) {
deleted = FileSystemUtils.deleteRecursively(indexPathFile);
if (deleted) {
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// no matter
}
}
if (!deleted) {
throw new SearchEngineException("Failed to delete index directory [" + buildPath(subContext, subIndex) + "]");
}
}
}
@Override
public void cleanIndex(Directory dir, String subContext, String subIndex) throws SearchEngineException {
deleteIndex(dir, subContext, subIndex);
}
@Override
public CopyFromHolder beforeCopyFrom(String subContext, String subIndex, Directory dir) throws SearchEngineException {
// first rename the current index directory
String path = buildPath(subContext, subIndex);
File indexPathFile = new File(path);
int count = 0;
File renameToIndexPathFile;
while (true) {
renameToIndexPathFile = new File(path + "-copy" + count++);
if (!renameToIndexPathFile.exists()) {
break;
}
}
boolean renamed = false;
for (int i = 0; i < 10; i++) {
renamed = indexPathFile.renameTo(renameToIndexPathFile);
if (!renamed) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// do nothing
}
} else {
break;
}
}
if (!renamed) {
throw new SearchEngineException("Failed to rename index [" + path
+ "] to [" + renameToIndexPathFile.getPath() + "]");
}
((FSDirectory) dir).getFile().mkdirs();
CopyFromHolder holder = new CopyFromHolder();
holder.data = renameToIndexPathFile;
return holder;
}
@Override
public void afterSuccessfulCopyFrom(String subContext, String subIndex, CopyFromHolder holder) throws SearchEngineException {
File renameToIndexPathFile = (File) holder.data;
try {
FileSystemUtils.deleteRecursively(renameToIndexPathFile);
} catch (Exception e) {
log.warn("Failed to delete backup directory", e);
}
}
@Override
public void afterFailedCopyFrom(String subContext, String subIndex, CopyFromHolder holder) throws SearchEngineException {
// TODO if it fails, try to rename the original one back
}
protected String buildPath(String subContext, String subIndex) {
return indexPath + "/" + subContext + "/" + subIndex;
}
@Override
public boolean suggestedUseCompoundFile() {
return true;
}
protected String getFSDirectoryClass() {
return "org.apache.lucene.store.FSDirectory";
}
}