Bug 496853 - Improve Lucene indexer locking, committing and recovery
Change-Id: I8fedd94903982f690f02e2bb8efb4d9237138633
Signed-off-by: Bartlomiej Laczkowski <bartlomiej.l@zend.com>
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/META-INF/MANIFEST.MF b/core/plugins/org.eclipse.dltk.core.index.lucene/META-INF/MANIFEST.MF
index 33ff1c8..46a7b79 100644
--- a/core/plugins/org.eclipse.dltk.core.index.lucene/META-INF/MANIFEST.MF
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/META-INF/MANIFEST.MF
@@ -8,7 +8,8 @@
Bundle-Vendor: %Bundle-Vendor
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.resources,
- org.eclipse.dltk.core
+ org.eclipse.dltk.core,
+ org.eclipse.ui.workbench
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .,
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexContainer.java b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexContainer.java
index da667a3..ab9ae3b 100644
--- a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexContainer.java
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexContainer.java
@@ -11,12 +11,9 @@
package org.eclipse.dltk.internal.core.index.lucene;
import java.io.IOException;
-import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
@@ -29,9 +26,11 @@
import org.apache.lucene.search.SearcherFactory;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.SimpleFSLockFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dltk.core.index.lucene.LucenePlugin;
@@ -57,35 +56,32 @@
@Override
protected IStatus run(IProgressMonitor monitor) {
+ doClean();
+ return Status.OK_STATUS;
+ }
+
+ void clean(boolean fork) {
+ if (fork) {
+ schedule();
+ } else {
+ doClean();
+ }
+ }
+
+ private void doClean() {
close();
Path containerPath = Paths.get(fIndexRoot, getId());
try {
- Files.walkFileTree(containerPath,
- new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path file,
- BasicFileAttributes attrs)
- throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir,
- IOException exc) throws IOException {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- }
- });
+ Utils.delete(containerPath);
} catch (IOException e) {
Logger.logException(e);
}
- return Status.OK_STATUS;
}
}
private static final String TIMESTAMPS_DIR = "timestamps"; //$NON-NLS-1$
+ private static final long WRITE_LOCK_TIMEOUT = 3000;
private final String fIndexRoot;
private final String fContainerId;
@@ -113,19 +109,47 @@
new HashMap<Integer, SearcherManager>());
}
- private IndexWriter createIndexWriter(Path path) {
+ private void purgeLocks(Path path) {
+ /*
+ * Checks if any write locks exist (might be not removed if JVM crashed
+ * or was terminated abnormally) and simply deletes them.
+ */
+ Path writeLockPath = path.resolve(IndexWriter.WRITE_LOCK_NAME);
+ if (writeLockPath.toFile().exists()) {
+ try {
+ Files.delete(writeLockPath);
+ } catch (IOException e) {
+ Logger.logException(e);
+ }
+ }
+ }
+
+ private IndexWriter createWriter(Path path) throws IOException {
+ Directory indexDir = new IndexDirectory(path,
+ SimpleFSLockFactory.INSTANCE);
+ purgeLocks(path);
+ IndexWriterConfig config = new IndexWriterConfig(new SimpleAnalyzer());
+ ConcurrentMergeScheduler mergeScheduler = new ConcurrentMergeScheduler();
+ mergeScheduler.setDefaultMaxMergesAndThreads(true);
+ config.setMergeScheduler(mergeScheduler);
+ config.setOpenMode(OpenMode.CREATE_OR_APPEND);
+ config.setWriteLockTimeout(WRITE_LOCK_TIMEOUT);
+ config.setCommitOnClose(false);
+ return new IndexWriter(indexDir, config);
+ }
+
+ private IndexWriter getWriter(Path path) {
IndexWriter indexWriter = null;
try {
- Directory indexDir = new IndexDirectory(path);
- IndexWriterConfig config = new IndexWriterConfig(
- new SimpleAnalyzer());
- ConcurrentMergeScheduler mergeScheduler = new ConcurrentMergeScheduler();
- mergeScheduler.setDefaultMaxMergesAndThreads(true);
- config.setMergeScheduler(mergeScheduler);
- config.setOpenMode(OpenMode.CREATE_OR_APPEND);
- indexWriter = new IndexWriter(indexDir, config);
+ indexWriter = createWriter(path);
} catch (IOException e) {
- Logger.logException(e);
+ // Try to recover possibly corrupted index
+ IndexRecovery.tryRecover(this, path, e);
+ try {
+ indexWriter = createWriter(path);
+ } catch (IOException ex) {
+ Logger.logException(ex);
+ }
}
return indexWriter;
}
@@ -138,7 +162,7 @@
if (fTimestampsWriter == null) {
Path writerPath = Paths.get(fIndexRoot, fContainerId,
TIMESTAMPS_DIR);
- fTimestampsWriter = createIndexWriter(writerPath);
+ fTimestampsWriter = getWriter(writerPath);
}
return fTimestampsWriter;
}
@@ -163,7 +187,7 @@
if (writer == null) {
Path writerPath = Paths.get(fIndexRoot, fContainerId,
dataType.getDirectory(), String.valueOf(elementType));
- writer = createIndexWriter(writerPath);
+ writer = getWriter(writerPath);
fIndexWriters.get(dataType).put(elementType, writer);
}
return writer;
@@ -205,9 +229,9 @@
}
}
- public synchronized void delete() {
+ public synchronized void delete(boolean wait) {
// Delete container entry entirely
- (new IndexCleaner()).schedule();
+ (new IndexCleaner()).clean(!wait);
}
public synchronized void close() {
@@ -238,4 +262,46 @@
}
}
+ synchronized boolean hasUncommittedChanges() {
+ for (Map<Integer, IndexWriter> dataWriters : fIndexWriters.values()) {
+ for (IndexWriter writer : dataWriters.values()) {
+ if (writer != null && writer.hasUncommittedChanges()) {
+ return true;
+ }
+ }
+ if (fTimestampsWriter != null) {
+ return fTimestampsWriter.hasUncommittedChanges();
+ }
+ }
+ return false;
+ }
+
+ synchronized void commit(IProgressMonitor monitor) {
+ int ticks = 1;
+ for (Map<?, ?> dataWriters : fIndexWriters.values()) {
+ ticks += dataWriters.size();
+ }
+ SubMonitor subMonitor = SubMonitor.convert(monitor, ticks);
+ try {
+ for (Map<Integer, IndexWriter> dataWriters : fIndexWriters
+ .values()) {
+ for (IndexWriter writer : dataWriters.values()) {
+ if (writer != null && !subMonitor.isCanceled()) {
+ writer.forceMergeDeletes(true);
+ writer.commit();
+ subMonitor.worked(1);
+ }
+ }
+ }
+ if (fTimestampsWriter != null && !subMonitor.isCanceled()) {
+ fTimestampsWriter.forceMergeDeletes(true);
+ fTimestampsWriter.commit();
+ subMonitor.worked(1);
+ }
+ subMonitor.done();
+ } catch (IOException e) {
+ Logger.logException(e);
+ }
+ }
+
}
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexRecovery.java b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexRecovery.java
new file mode 100644
index 0000000..77d6135
--- /dev/null
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/IndexRecovery.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Zend Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Zend Technologies - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.dltk.internal.core.index.lucene;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.eclipse.dltk.internal.core.search.ProjectIndexerManager;
+
+/**
+ * Lucene index container recovery class.
+ *
+ * @author Bartlomiej Laczkowski
+ */
+@SuppressWarnings("restriction")
+public final class IndexRecovery {
+
+ private static final String RECOVERY_REASON = "Index writer could not be created, index data might be corrupted."; //$NON-NLS-1$
+ private static final String RECOVERY_STARTED = "Recovering index storage: {0}"; //$NON-NLS-1$
+ private static final String RECOVERY_FAILED = "Failed to recover index storage: {0}"; //$NON-NLS-1$
+
+ private IndexRecovery() {
+ // No instance
+ }
+
+ /**
+ * <p>
+ * Tries to recover possibly corrupted Lucene index. Recovery process will
+ * try do do the following:
+ * </p>
+ * <ul>
+ * <li>Remove problematic index directory.</li>
+ * <li>Clean up time stamps data in corresponding index conatiner.</li>
+ * <li>Trigger index rebuilding to fill empty index directory.</li>
+ * </ul>
+ *
+ * @param indexContainer
+ * @param indexPath
+ * @param exception
+ */
+ static void tryRecover(IndexContainer indexContainer, Path indexPath,
+ IOException exception) {
+ Logger.logException(RECOVERY_REASON, exception);
+ Logger.log(Logger.INFO,
+ MessageFormat.format(RECOVERY_STARTED, indexPath.toString()));
+ try {
+ // Try to delete possibly corrupted index container
+ Utils.delete(indexPath);
+ // Clean time stamps to purge index state
+ indexContainer.getTimestampsWriter()
+ .deleteDocuments(new MatchAllDocsQuery());
+ } catch (IOException e) {
+ Logger.logException(
+ MessageFormat.format(RECOVERY_FAILED, indexPath.toString()),
+ e);
+ return;
+ }
+ // Re-triggering indexing will fill purged container indexes.
+ ProjectIndexerManager.startIndexing();
+ }
+
+}
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/LuceneManager.java b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/LuceneManager.java
index 25c053d..5d36bf9 100644
--- a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/LuceneManager.java
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/LuceneManager.java
@@ -14,31 +14,41 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
+import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Semaphore;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.SearcherManager;
+import org.eclipse.core.resources.ISaveContext;
+import org.eclipse.core.resources.ISaveParticipant;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IShutdownListener;
import org.eclipse.dltk.core.index.lucene.LucenePlugin;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.search.DLTKWorkspaceScope;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.PlatformUI;
/**
* <p>
@@ -69,13 +79,132 @@
*/
INSTANCE;
- private static final class ShutdownListener implements IShutdownListener {
+ private final class ShutdownListener
+ implements IShutdownListener, IWorkbenchListener, ISaveParticipant {
+
+ private final class Committer extends Job {
+
+ private List<IndexContainer> fContainers;
+
+ public Committer() {
+ super(Messages.LuceneManager_Committer_saving_indexes);
+ setUser(false);
+ setSystem(false);
+ }
+
+ @Override
+ public IStatus run(IProgressMonitor monitor) {
+ int containersNumber = fContainers.size();
+ monitor.beginTask("", containersNumber); //$NON-NLS-1$
+ SubMonitor subMonitor = SubMonitor.convert(monitor,
+ containersNumber);
+ int counter = 0;
+ try {
+ for (IndexContainer indexContainer : fContainers) {
+ if (!monitor.isCanceled()) {
+ counter++;
+ monitor.subTask(MessageFormat.format(
+ Messages.LuceneManager_Committer_flushing_index_data,
+ counter, containersNumber));
+ // Commit index data to file system storage
+ indexContainer.commit(subMonitor.newChild(1));
+ }
+ }
+ monitor.done();
+ } catch (Exception e) {
+ Logger.logException(e);
+ } finally {
+ // Whatever happens semaphore must be released.
+ fSemaphore.release();
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return family == LucenePlugin.LUCENE_JOB_FAMILY;
+ }
+
+ void committ() {
+ fContainers = getUncommittedContainers();
+ schedule();
+ }
+
+ }
+
+ private final Committer fCommitter = new Committer();
+ private final Semaphore fSemaphore = new Semaphore(0);
+
+ @Override
+ public boolean preShutdown(IWorkbench workbench, boolean forced) {
+ // Check if there is anything to commit first
+ if (getUncommittedContainers().isEmpty())
+ return true;
+ /*
+ * Trigger this hidden job that will use workspace root as
+ * scheduling rule just to show the committer job progress in the
+ * details pane of "Saving Workspace" dialog.
+ */
+ Job job = new Job("") { //$NON-NLS-1$
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ fSemaphore.acquire();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ job.setRule(ResourcesPlugin.getWorkspace().getRoot());
+ job.setSystem(true);
+ job.setUser(false);
+ job.schedule();
+ // Trigger committer job
+ fCommitter.committ();
+ return true;
+ }
+
+ @Override
+ public void saving(ISaveContext context) throws CoreException {
+ joinCommitter();
+ }
@Override
public void shutdown() {
+ joinCommitter();
+ // Shutdown manager and close all the writers and searchers
LuceneManager.INSTANCE.shutdown();
}
+ @Override
+ public void postShutdown(IWorkbench workbench) {
+ // ignore
+ }
+
+ @Override
+ public void doneSaving(ISaveContext context) {
+ // ignore
+ }
+
+ @Override
+ public void prepareToSave(ISaveContext context) throws CoreException {
+ // ignore
+ }
+
+ @Override
+ public void rollback(ISaveContext context) {
+ // ignore
+ }
+
+ private void joinCommitter() {
+ try {
+ fCommitter.join();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
}
private static final String INDEX_DIR = "index"; //$NON-NLS-1$
@@ -90,7 +219,7 @@
private LuceneManager() {
fIndexProperties = new Properties();
fContainerMappings = new Properties();
- fIndexContainers = new HashMap<>();
+ fIndexContainers = new ConcurrentHashMap<>();
fIndexRoot = Platform
.getStateLocation(LucenePlugin.getDefault().getBundle())
.append(INDEX_DIR).toOSString();
@@ -158,7 +287,7 @@
* @param container
*/
public final void delete(final String container) {
- deleteIndexContainer(container);
+ deleteIndexContainer(container, false);
}
/**
@@ -173,6 +302,52 @@
}
}
+ synchronized String getContainerPath(String containerId) {
+ for (Object key : fContainerMappings.keySet()) {
+ String container = (String) key;
+ if (containerId.equals(fContainerMappings.getProperty(container))) {
+ return container;
+ }
+ }
+ return null;
+ }
+
+ synchronized List<IndexContainer> getUncommittedContainers() {
+ List<IndexContainer> uncommittedContainers = new ArrayList<>();
+ for (IndexContainer indexContainer : fIndexContainers.values()) {
+ if (indexContainer.hasUncommittedChanges()) {
+ uncommittedContainers.add(indexContainer);
+ }
+ }
+ return uncommittedContainers;
+ }
+
+ synchronized IndexContainer getIndexContainer(String container) {
+ String containerId = fContainerMappings.getProperty(container);
+ if (containerId == null) {
+ do {
+ // Just to be sure that ID does not already exist
+ containerId = UUID.randomUUID().toString();
+ } while (fContainerMappings.containsValue(containerId));
+ fContainerMappings.put(container, containerId);
+ fIndexContainers.put(containerId,
+ new IndexContainer(fIndexRoot, containerId));
+ // Persist mapping
+ saveMappings();
+ }
+ return fIndexContainers.get(containerId);
+ }
+
+ synchronized void deleteIndexContainer(String container, boolean wait) {
+ String containerId = (String) fContainerMappings.remove(container);
+ if (containerId != null) {
+ IndexContainer containerEntry = fIndexContainers
+ .remove(containerId);
+ saveMappings();
+ containerEntry.delete(wait);
+ }
+ }
+
private synchronized void startup() {
loadProperties();
boolean purgeIndexRoot = false;
@@ -195,9 +370,16 @@
}
loadMappings();
registerIndexContainers();
+ ShutdownListener shutdownListener = new ShutdownListener();
ModelManager.getModelManager().getIndexManager()
- .addShutdownListener(new ShutdownListener());
-
+ .addShutdownListener(shutdownListener);
+ PlatformUI.getWorkbench().addWorkbenchListener(shutdownListener);
+ try {
+ ResourcesPlugin.getWorkspace().addSaveParticipant(LucenePlugin.ID,
+ shutdownListener);
+ } catch (CoreException e) {
+ Logger.logException(e);
+ }
}
private synchronized void shutdown() {
@@ -208,32 +390,6 @@
cleanup();
}
- private synchronized IndexContainer getIndexContainer(String container) {
- String containerId = fContainerMappings.getProperty(container);
- if (containerId == null) {
- do {
- // Just to be sure that ID does not already exist
- containerId = UUID.randomUUID().toString();
- } while (fContainerMappings.containsValue(containerId));
- fContainerMappings.put(container, containerId);
- fIndexContainers.put(containerId,
- new IndexContainer(fIndexRoot, containerId));
- // Persist mapping
- saveMappings();
- }
- return fIndexContainers.get(containerId);
- }
-
- private synchronized void deleteIndexContainer(String container) {
- String containerId = (String) fContainerMappings.remove(container);
- if (containerId != null) {
- IndexContainer containerEntry = fIndexContainers
- .remove(containerId);
- saveMappings();
- containerEntry.delete();
- }
- }
-
private void registerIndexContainers() {
for (String container : fContainerMappings.stringPropertyNames()) {
String containerId = fContainerMappings.getProperty(container);
@@ -302,6 +458,10 @@
containers.add(path.toString());
}
}
+ /*
+ * Some projects/libraries could be deleted outside the workspace, clean
+ * up the related mappings that might left.
+ */
Set<String> toRemove = new HashSet<>();
for (String mappedContainer : fContainerMappings
.stringPropertyNames()) {
@@ -311,31 +471,36 @@
}
if (!toRemove.isEmpty()) {
for (String container : toRemove) {
- deleteIndexContainer(container);
+ deleteIndexContainer(container, true);
}
- // Save cleaned up container mappings
- saveMappings();
+ }
+ /*
+ * Some projects/libraries could be deleted outside the workspace,
+ * delete up the related index directories that might left.
+ */
+ List<Path> toDelete = new ArrayList<>();
+ Path indexRoot = Paths.get(fIndexRoot);
+ for (File containerDir : indexRoot.toFile().listFiles()) {
+ if (containerDir.isDirectory() && !fContainerMappings
+ .containsValue(containerDir.getName())) {
+ toDelete.add(Paths.get(containerDir.getAbsolutePath()));
+ }
+ }
+ if (!toDelete.isEmpty()) {
+ for (Path containerDir : toDelete) {
+ try {
+ Utils.delete(containerDir);
+ } catch (IOException e) {
+ Logger.logException(e);
+ }
+ }
}
}
private void purge() {
Path indexRoot = Paths.get(fIndexRoot);
try {
- Files.walkFileTree(indexRoot, new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path file,
- BasicFileAttributes attrs) throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir,
- IOException exc) throws IOException {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- }
- });
+ Utils.delete(indexRoot);
} catch (IOException e) {
Logger.logException(e);
}
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/Messages.java b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/Messages.java
new file mode 100644
index 0000000..6087d68
--- /dev/null
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/Messages.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Zend Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Zend Technologies - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.dltk.internal.core.index.lucene;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages.
+ *
+ * @author Bartlomiej Laczkowski
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.dltk.internal.core.index.lucene.messages"; //$NON-NLS-1$
+ public static String LuceneManager_Committer_flushing_index_data;
+ public static String LuceneManager_Committer_saving_indexes;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/Utils.java b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/Utils.java
new file mode 100644
index 0000000..64da887
--- /dev/null
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/Utils.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Zend Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Zend Technologies - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.dltk.internal.core.index.lucene;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * Utility class for Lucene indexer.
+ *
+ * @author Bartlomiej Laczkowski
+ */
+public final class Utils {
+
+ private Utils() {
+ // Utility class, should not be instantiated.
+ }
+
+ /**
+ * Deletes directory under given path with all of its contents.
+ *
+ * @param directory
+ * @throws IOException
+ */
+ public static void delete(Path directory) throws IOException {
+ Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file,
+ BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+ throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+}
diff --git a/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/messages.properties b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/messages.properties
new file mode 100644
index 0000000..58d9c81
--- /dev/null
+++ b/core/plugins/org.eclipse.dltk.core.index.lucene/src/org/eclipse/dltk/internal/core/index/lucene/messages.properties
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (c) 2016 Zend Technologies and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Zend Technologies - initial API and implementation
+###############################################################################
+
+LuceneManager_Committer_flushing_index_data=Flushing index data to persistent storage: ({0} of {1})
+LuceneManager_Committer_saving_indexes=Saving indexes...
\ No newline at end of file