/** * Copyright 2016 LinkedIn Corp. All rights reserved. * * 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. */ package com.github.ambry.store; import com.github.ambry.utils.CrcInputStream; import com.github.ambry.utils.CrcOutputStream; import com.github.ambry.utils.Utils; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.UUID; /** * Refers to the Store descriptor of a {@link BlobStore} having some metadata information about each store. * As of now, store descriptor stores the incarnation Id which is a unique identifier for every new * incarnation of the store. */ class StoreDescriptor { private final UUID incarnationId; static final short VERSION_0 = 0; static final int VERSION_SIZE = 2; static final int INCARNATION_ID_LENGTH_SIZE = 4; static final String STORE_DESCRIPTOR_FILENAME = "StoreDescriptor"; /** * Instantiates the {@link StoreDescriptor} for the store. If the respective file is present, reads the bytes * to understand the incarnationId. If not, creates a new one with a random Unique identifier as the new incarnationId * @param dataDir the directory path to locate the Store Descriptor file * @throws IOException when file creation or read or write to file fails */ StoreDescriptor(String dataDir) throws IOException { File storeDescriptorFile = new File(dataDir, STORE_DESCRIPTOR_FILENAME); if (storeDescriptorFile.exists()) { CrcInputStream crcStream = new CrcInputStream(new FileInputStream(storeDescriptorFile)); DataInputStream stream = new DataInputStream(crcStream); short version = stream.readShort(); switch (version) { case VERSION_0: // read incarnationId String incarnationIdStr = Utils.readIntString(stream); incarnationId = UUID.fromString(incarnationIdStr); long crc = crcStream.getValue(); long crcValueInFile = stream.readLong(); if (crc != crcValueInFile) { throw new IllegalStateException( "CRC mismatch for StoreDescriptor. CRC of the stream " + crc + ", CRC from file " + crcValueInFile); } break; default: throw new IllegalArgumentException("Unrecognized version in StoreDescriptor: " + version); } } else { incarnationId = UUID.randomUUID(); File tempFile = new File(dataDir, STORE_DESCRIPTOR_FILENAME + ".tmp"); File actual = new File(dataDir, STORE_DESCRIPTOR_FILENAME); if (tempFile.createNewFile()) { FileOutputStream fileStream = new FileOutputStream(tempFile); CrcOutputStream crc = new CrcOutputStream(fileStream); DataOutputStream writer = new DataOutputStream(crc); writer.write(toBytes()); long crcValue = crc.getValue(); writer.writeLong(crcValue); fileStream.getChannel().force(true); if (!tempFile.renameTo(actual)) { throw new IllegalStateException( "File " + tempFile.getAbsolutePath() + " renaming to " + actual.getAbsolutePath() + " failed "); } } else { throw new IllegalStateException("File " + tempFile.getAbsolutePath() + " creation failed "); } } } /** * @return the incarnationId of the store */ UUID getIncarnationId() { return incarnationId; } /** * Returns the serialized form of this {@link StoreDescriptor} * @return the serialized form of this {@link StoreDescriptor} */ private byte[] toBytes() { byte[] incarnationIdBytes = incarnationId.toString().getBytes(); int size = VERSION_SIZE + INCARNATION_ID_LENGTH_SIZE + incarnationIdBytes.length; byte[] buf = new byte[size]; ByteBuffer bufWrap = ByteBuffer.wrap(buf); // add version bufWrap.putShort(VERSION_0); // add incarnationId bufWrap.putInt(incarnationIdBytes.length); bufWrap.put(incarnationIdBytes); return buf; } }