Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java (revision 1756878) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java (working copy) @@ -76,7 +76,6 @@ import com.google.common.base.Stopwatch; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; -import com.google.common.io.Files; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean; import org.apache.jackrabbit.oak.plugins.blob.ReferenceCollector; @@ -114,6 +113,19 @@ private static final int MB = 1024 * 1024; + /** + * This value can be used as an invalid store version, since the store + * version is defined to be strictly greater than zero. + */ + private static final int INVALID_STORE_VERSION = 0; + + /** + * The store version is an always incrementing number, strictly greater than + * zero, that is changed every time there is a backwards incompatible + * modification to the format of the segment store. + */ + private static final int CURRENT_STORE_VERSION = 1; + private static final Pattern FILE_NAME_PATTERN = Pattern.compile("(data|bulk)((0|[1-9][0-9]*)[0-9]{4})([a-z])?.tar"); @@ -281,17 +293,13 @@ Map> map = collectFiles(directory); - File manifest = new File(directory, MANIFEST_FILE_NAME); + Manifest manifest = Manifest.empty(); if (map.size() > 0) { - if (manifest.exists()) { - log.debug("The store folder is non empty and has a valid manifest file"); - } else { - throw new InvalidFileStoreVersionException(); - } + manifest = checkManifest(openManifest()); } - Files.touch(manifest); + saveManifest(manifest); this.readers = newArrayListWithCapacity(map.size()); Integer[] indices = map.keySet().toArray(new Integer[map.size()]); @@ -391,6 +399,51 @@ return this; } + private File getManifestFile() { + return new File(directory, MANIFEST_FILE_NAME); + } + + private Manifest openManifest() throws IOException { + File file = getManifestFile(); + + if (file.exists()) { + return Manifest.load(file); + } + + return null; + } + + private Manifest checkManifest(Manifest manifest) throws InvalidFileStoreVersionException { + if (manifest == null) { + throw new InvalidFileStoreVersionException("Using oak-segment-tar, but oak-segment should be used"); + } + + int storeVersion = manifest.getStoreVersion(INVALID_STORE_VERSION); + + // A store version less than or equal to the highest invalid value means + // that something or someone is messing up with the manifest. This error + // is not recoverable and is thus represented as an ISE. + + if (storeVersion <= INVALID_STORE_VERSION) { + throw new IllegalStateException("Invalid store version"); + } + + if (storeVersion < CURRENT_STORE_VERSION) { + throw new InvalidFileStoreVersionException("Using a too recent version of oak-segment-tar"); + } + + if (storeVersion > CURRENT_STORE_VERSION) { + throw new InvalidFileStoreVersionException("Using a too old version of oak-segment tar"); + } + + return manifest; + } + + private void saveManifest(Manifest manifest) throws IOException { + manifest.setStoreVersion(CURRENT_STORE_VERSION); + manifest.save(getManifestFile()); + } + @Nonnull private Supplier initialNode() { return new Supplier() { Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/InvalidFileStoreVersionException.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/InvalidFileStoreVersionException.java (revision 1756878) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/InvalidFileStoreVersionException.java (working copy) @@ -24,4 +24,11 @@ */ public class InvalidFileStoreVersionException extends Exception { + public InvalidFileStoreVersionException() { + } + + public InvalidFileStoreVersionException(String message) { + super(message); + } + } Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Manifest.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Manifest.java (revision 0) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Manifest.java (working copy) @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.file; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Properties; + +class Manifest { + + private static final String STORE_VERSION = "store.version"; + + /** + * Loads the manifest from a file. + * + * @param file The file to load the manifest from. + * @return A manifest file. + * @throws IOException If any error occurs when loading the manifest. + */ + static Manifest load(File file) throws IOException { + Properties properties = new Properties(); + properties.load(new FileReader(file)); + return new Manifest(properties); + } + + /** + * Creates an empty manifest file. + * + * @return A manifest file. + */ + static Manifest empty() { + return new Manifest(new Properties()); + } + + private final Properties properties; + + private Manifest(Properties properties) { + this.properties = properties; + } + + /** + * Return the store version saved in this manifest or a user provided value + * if no valid store version is saved in the manifest. + * + * @param otherwise The value that will be returned if no valid store + * version is saved in this manifest. + * @return The store version stored in this manifest or the user provided + * default value. + */ + int getStoreVersion(int otherwise) { + return getIntegerProperty(STORE_VERSION, otherwise); + } + + /** + * Set the store version in this manifest. + * + * @param version The store version. + */ + void setStoreVersion(int version) { + setIntegerProperty(STORE_VERSION, version); + } + + /** + * Save the manifest to the specified file. + * + * @param file The file to save the manifest to. + * @throws IOException if an error occurs while saving the manifest. + */ + void save(File file) throws IOException { + properties.store(new FileWriter(file), null); + } + + private int getIntegerProperty(String name, int otherwise) { + Object value = properties.get(name); + + if (value == null) { + return otherwise; + } + + try { + return Integer.parseInt(value.toString()); + } catch (NumberFormatException e) { + return otherwise; + } + } + + private void setIntegerProperty(String name, int value) { + properties.put(name, Integer.toString(value)); + } + +} Property changes on: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Manifest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ManifestTest.java =================================================================== --- oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ManifestTest.java (revision 0) +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ManifestTest.java (working copy) @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.file; + +import static org.junit.Assert.assertEquals; + +import java.io.File; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ManifestTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(new File("target")); + + @Test + public void defaultStoreVersionShouldBeReturned() throws Exception { + assertEquals(42, Manifest.load(folder.newFile()).getStoreVersion(42)); + } + + @Test + public void storeVersionShouldBeReturned() throws Exception { + File file = folder.newFile(); + + Manifest write = Manifest.empty(); + write.setStoreVersion(42); + write.save(file); + + Manifest read = Manifest.load(file); + assertEquals(42, read.getStoreVersion(0)); + } + +} Property changes on: oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ManifestTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property