MetaHeader.java
// 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.doris.persist.meta;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* Header Format:
* - Magic String (4 bytes)
* - Header Length (4 bytes)
* |- Header -----------------------------|
* | |- Json Header ---------------| |
* | | - version | |
* | | - other key/value(undecided)| |
* | |-----------------------------| |
* |--------------------------------------|
*/
public class MetaHeader {
private static final Logger LOG = LogManager.getLogger(MetaHeader.class);
public static final MetaHeader EMPTY_HEADER = new MetaHeader(null, 0);
private static final long HEADER_LENGTH_SIZE = 4L;
// length of Header
private long length;
// format of image
private FeMetaFormat metaFormat;
// json header
public MetaJsonHeader metaJsonHeader;
public static MetaHeader read(File imageFile) throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(imageFile, "r")) {
raf.seek(0);
MetaMagicNumber magicNumber = MetaMagicNumber.read(raf);
if (!Arrays.equals(MetaMagicNumber.MAGIC, magicNumber.getBytes())) {
LOG.warn("Image file {} format mismatch. Expected magic number is {}, actual is {}",
imageFile.getPath(), Arrays.toString(MetaMagicNumber.MAGIC),
Arrays.toString(magicNumber.getBytes()));
return EMPTY_HEADER;
}
MetaJsonHeader metaJsonHeader = MetaJsonHeader.read(raf);
if (!MetaJsonHeader.IMAGE_VERSION.equalsIgnoreCase(metaJsonHeader.imageVersion)) {
String errMsg = "Image file " + imageFile.getPath() + " format version mismatch. "
+ "Expected version is " + MetaJsonHeader.IMAGE_VERSION
+ ", actual is" + metaJsonHeader.imageVersion;
// different versions are incompatible
throw new IOException(errMsg);
}
long length = raf.getFilePointer() - MetaMagicNumber.MAGIC_STR.length() - HEADER_LENGTH_SIZE;
FeMetaFormat metaFormat = FeMetaFormat.valueOf(new String(magicNumber.getBytes()));
LOG.info("Image header length: {}, format: {}.", length, metaFormat);
return new MetaHeader(metaJsonHeader, length, metaFormat);
}
}
public static long write(File imageFile) throws IOException {
if (imageFile.length() != 0) {
throw new IOException("Meta header has to be written to an empty file.");
}
try (RandomAccessFile raf = new RandomAccessFile(imageFile, "rw")) {
raf.seek(0);
MetaMagicNumber.write(raf);
MetaJsonHeader.write(raf);
raf.getChannel().force(true);
return raf.getFilePointer();
}
}
public MetaHeader(MetaJsonHeader metaJsonHeader, long length) {
this(metaJsonHeader, length, FeMetaFormat.COR1);
}
public MetaHeader(MetaJsonHeader metaJsonHeader, long length, FeMetaFormat metaFormat) {
this.metaJsonHeader = metaJsonHeader;
this.length = length;
this.metaFormat = metaFormat;
}
public long getEnd() {
if (length > 0) {
return MetaMagicNumber.MAGIC_STR.length() + HEADER_LENGTH_SIZE + length;
}
return 0;
}
public long getLength() {
return length;
}
public FeMetaFormat getMetaFormat() {
return metaFormat;
}
public MetaJsonHeader getMetaJsonHeader() {
return metaJsonHeader;
}
}