1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2018, Chris Fraire <cfraire@me.com>. 23 */ 24 package org.opengrok.indexer.index; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.util.Collection; 29 import java.util.logging.Level; 30 import java.util.logging.Logger; 31 import org.apache.lucene.index.SegmentInfos; 32 import org.apache.lucene.store.Directory; 33 import org.apache.lucene.store.FSDirectory; 34 import org.apache.lucene.store.LockFactory; 35 import org.apache.lucene.store.NativeFSLockFactory; 36 import org.apache.lucene.util.Version; 37 import org.jetbrains.annotations.NotNull; 38 import org.opengrok.indexer.configuration.Configuration; 39 import org.opengrok.indexer.logger.LoggerFactory; 40 41 /** 42 * Index checker. 43 * 44 * @author Vladimír Kotal 45 */ 46 public class IndexCheck { 47 private static final Logger LOGGER = LoggerFactory.getLogger(IndexCheck.class); 48 49 /** 50 * Exception thrown when index version does not match Lucene version. 51 */ 52 public static class IndexVersionException extends Exception { 53 54 private static final long serialVersionUID = 5693446916108385595L; 55 56 private final int luceneIndexVersion; 57 private final int indexVersion; 58 IndexVersionException(String s, int luceneIndexVersion, int indexVersion)59 public IndexVersionException(String s, int luceneIndexVersion, int indexVersion) { 60 super(s); 61 62 this.indexVersion = indexVersion; 63 this.luceneIndexVersion = luceneIndexVersion; 64 } 65 66 @Override toString()67 public String toString() { 68 return getMessage() + ": " + String.format("Lucene version = %d", luceneIndexVersion) + ", " + 69 String.format("index version = %d", indexVersion); 70 } 71 } 72 IndexCheck()73 private IndexCheck() { 74 // utility class 75 } 76 77 /** 78 * Check if version of index(es) matches major Lucene version. 79 * @param configuration configuration based on which to perform the check 80 * @param subFilesList collection of paths. If non-empty, only projects matching these paths will be checked. 81 * @return true on success, false on failure 82 */ check(@otNull Configuration configuration, Collection<String> subFilesList)83 public static boolean check(@NotNull Configuration configuration, Collection<String> subFilesList) { 84 File indexRoot = new File(configuration.getDataRoot(), IndexDatabase.INDEX_DIR); 85 LOGGER.log(Level.FINE, "Checking for Lucene index version mismatch in {0}", indexRoot); 86 int ret = 0; 87 88 if (!subFilesList.isEmpty()) { 89 // Assumes projects are enabled. 90 for (String projectName : subFilesList) { 91 LOGGER.log(Level.FINER, 92 "Checking Lucene index version in project {0}", 93 projectName); 94 ret |= checkDirNoExceptions(new File(indexRoot, projectName)); 95 } 96 } else { 97 if (configuration.isProjectsEnabled()) { 98 for (String projectName : configuration.getProjects().keySet()) { 99 LOGGER.log(Level.FINER, 100 "Checking Lucene index version in project {0}", 101 projectName); 102 ret |= checkDirNoExceptions(new File(indexRoot, projectName)); 103 } 104 } else { 105 LOGGER.log(Level.FINER, "Checking Lucene index version in {0}", 106 indexRoot); 107 ret |= checkDirNoExceptions(indexRoot); 108 } 109 } 110 111 return ret == 0; 112 } 113 checkDirNoExceptions(File dir)114 private static int checkDirNoExceptions(File dir) { 115 try { 116 checkDir(dir); 117 } catch (Exception e) { 118 LOGGER.log(Level.WARNING, "Index check for directory " + dir + " failed", e); 119 return 1; 120 } 121 122 return 0; 123 } 124 125 /** 126 * Check index in given directory. It assumes that that all commits (if any) 127 * in the Lucene segment file were done with the same version. 128 * 129 * @param dir directory with index 130 * @throws IOException if the directory cannot be opened 131 * @throws IndexVersionException if the version of the index does not match Lucene index version 132 */ checkDir(File dir)133 public static void checkDir(File dir) throws IndexVersionException, IOException { 134 LockFactory lockFactory = NativeFSLockFactory.INSTANCE; 135 int segVersion; 136 137 try (Directory indexDirectory = FSDirectory.open(dir.toPath(), lockFactory)) { 138 SegmentInfos segInfos = SegmentInfos.readLatestCommit(indexDirectory); 139 segVersion = segInfos.getIndexCreatedVersionMajor(); 140 } 141 142 if (segVersion != Version.LATEST.major) { 143 throw new IndexVersionException( 144 String.format("Directory %s has index version discrepancy", dir), 145 Version.LATEST.major, segVersion); 146 } 147 } 148 } 149