net.sf.lucis.core.impl.ReindexingFSStore.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.lucis.core.impl.ReindexingFSStore.java

Source

/*
 * Copyright (C) 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 net.sf.lucis.core.impl;

import static com.google.common.base.Preconditions.checkArgument;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;

import net.derquinse.common.io.DurableFiles;
import net.sf.lucis.core.Factory;
import net.sf.lucis.core.ReindexingStore;
import net.sf.lucis.core.StoreException;

import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

/**
 * File system-based reindexing store.
 * @author Andres Rodriguez
 */
public class ReindexingFSStore extends AbstractStore implements ReindexingStore {
    /** Name of the control file. */
    private static final String STATUS_FILE = "status.ctl";
    /** Name of the control file. */
    private static final String CHECKPOINT_FILE = "checkpoint.ctl";
    /** Initial state. */
    private static final State STATE = new State(Status.DONE01);
    /** Relative path to first copy. */
    private static final String COPY01 = "copy01";
    /** Relative path to second copy. */
    private static final String COPY02 = "copy02";
    /** Main directory. */
    private final File file;
    /** Status file. */
    private final File statusFile;
    /** Status file. */
    private final File checkpointFile;

    /** Status. */
    enum Status {
        NULL, DONE01, DONE02
    }

    /** First copy. */
    private final Copy copy01;
    /** Second copy. */
    private final Copy copy02;
    /** Version. */
    private final AtomicReference<State> state;

    private static Status readStatus(File file) {
        if (!file.exists()) {
            return Status.NULL;
        }
        try {
            String str = Files.toString(file, Charsets.UTF_8);
            if (str == null || str.length() == 0) {
                return Status.NULL;
            }
            str = str.trim();
            if (str.length() == 0) {
                return Status.NULL;
            }
            try {
                return Enum.valueOf(Status.class, str);
            } catch (IllegalArgumentException e) {
                return Status.NULL;
            }
        } catch (FileNotFoundException fnfe) {
            // Nothing.
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        return Status.NULL;
    }

    private static File file(File base, String name, String description) {
        final File f = new File(base, name);
        checkArgument(!f.isDirectory(), "%s cannot be a directory", description);
        return f;
    }

    public ReindexingFSStore(final String indexDir) {
        try {
            this.file = new File(indexDir);
            checkArgument(file.exists() && file.isDirectory(), "Invalid index directory");
            this.statusFile = file(file, STATUS_FILE, "Status file");
            this.checkpointFile = file(file, CHECKPOINT_FILE, "Checkpoint file");
            this.copy01 = new Copy(file, COPY01);
            this.copy02 = new Copy(file, COPY02);
            final Status status = readStatus(this.statusFile);
            if (Status.NULL.equals(status)) {
                IndexWriter w = new IndexWriter(copy01.directory, Factory.get().writerConfig());
                w.commit();
                w.close();
                state = new AtomicReference<State>(STATE);
                writeStatus();
                writeCheckpoint(null);
            } else {
                state = new AtomicReference<State>(new State(status));
            }
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    Status getStatus() {
        return state.get().status;
    }

    public Directory getDirectory() {
        switch (getStatus()) {
        case NULL:
            return null;
        case DONE01:
            return copy01.directory;
        case DONE02:
            return copy02.directory;
        }
        throw new AssertionError();
    }

    public Directory getDestinationDirectory() {
        switch (getStatus()) {
        case DONE01:
            return copy02.directory;
        default:
            return copy01.directory;
        }
    }

    public String getCheckpoint() throws StoreException {
        if (!checkpointFile.exists()) {
            return null;
        }
        try {
            return Files.toString(checkpointFile, Charsets.UTF_8);
        } catch (IOException e) {
            return null;
        }
    }

    public void reindexed(String checkpoint) {
        try {
            state.set(state.get().increment());
            writeStatus();
            writeCheckpoint(checkpoint);
        } finally {
            changed();
        }
    }

    private void writeStatus() {
        try {
            DurableFiles.write(getStatus().name(), statusFile, Charsets.UTF_8);
        } catch (IOException e) {
        }
    }

    private void writeCheckpoint(String checkpoint) {
        try {
            if (checkpoint == null) {
                if (checkpointFile.exists()) {
                    checkpointFile.delete();
                }
            } else {
                DurableFiles.write(checkpoint, checkpointFile, Charsets.UTF_8);
            }
        } catch (IOException e) {
        }
    }

    public Object getVersion() {
        return state.get().version;
    }

    private static final class Copy {
        private final File file;
        private final Directory directory;

        Copy(File base, String path) throws IOException {
            this.file = new File(base, path);
            checkArgument(!file.exists() || (file.exists() && file.isDirectory()), "Invalid index copy directory");
            if (!file.exists()) {
                file.mkdir();
            }
            this.directory = FSDirectory.open(this.file);
        }
    }

    private static final class State {
        private final Status status;
        private final int version;

        State(Status status, int version) {
            this.status = status;
            this.version = version;
        }

        State(Status status) {
            this(status, 0);
        }

        State increment() {
            Status ns = status == Status.DONE01 ? Status.DONE02 : Status.DONE01;
            return new State(ns, version + 1);
        }
    }

}