All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.code.fqueue.log.LogEntity Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
/*
 *  Copyright 2011 sunli [[email protected]][weibo.com@sunli1223]
 *
 *  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 com.google.code.fqueue.log;

import com.google.code.fqueue.exception.FileEOFException;
import com.google.code.fqueue.exception.FileFormatException;
import me.aifaq.commons.lang.SunUtil;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *@author sunli
 *@date 2011-5-18
 *@version $Id: LogEntity.java 2 2011-07-31 12:25:36Z [email protected] $
 */
public class LogEntity {
	private static final Logger logger = Logger.getLogger(LogEntity.class.getName());
	public static final byte WRITESUCCESS = 1;
	public static final byte WRITEFAILURE = 2;
	public static final byte WRITEFULL = 3;
	public static final String MAGIC = "FQueuefs";
	public static int messageStartPosition = 20;
	private final Executor executor = Executors.newSingleThreadExecutor();
	private File file;
	private RandomAccessFile raFile;
	private FileChannel fc;
	public MappedByteBuffer mappedByteBuffer;

	private final long fileLimitLength;

	private LogIndex db = null;
	/**
	 * 文件操作位置信息
	 */
	private String magicString = null;
	private int version = -1;
	private int readerPosition = -1;
	private int writerPosition = -1;
	private int nextFile = -1;
	private int endPosition = -1;
	private int currentFileNumber = -1;
	
	static final String FILENAME_FORMAT = "%s%s.idb";

	public LogEntity(String baseDir, LogIndex db, int fileNumber,
			long fileLimitLength) throws IOException, FileFormatException {
		this.currentFileNumber = fileNumber;
		this.fileLimitLength = fileLimitLength;
		this.db = db;
		
		final String path = String.format(FILENAME_FORMAT, baseDir, fileNumber);
		
		file = new File(path);
		// 文件不存在,创建文件
		if (file.exists() == false) {
			createLogEntity();
			// 如果写的够快,会和异步执行的create冲突,导致 mappedByteBuffer 还未赋值就执行write了
//			FileRunner.addCreateFile(Integer.toString(fileNumber + 1));
			// modified by lucifer // 异步改成同步 
			FileRunner.create(String.format(FILENAME_FORMAT, baseDir, fileNumber+1), this.fileLimitLength);
		} else {
			raFile = new RandomAccessFile(file, "rwd");
			if (raFile.length() < LogEntity.messageStartPosition) {
				throw new FileFormatException("file format error, please check file:" + path);
			}
			fc = raFile.getChannel();
			mappedByteBuffer = fc.map(MapMode.READ_WRITE, 0,
					this.fileLimitLength);
			// magicString
			byte[] b = new byte[8];
			mappedByteBuffer.get(b);
			magicString = new String(b);
			if (magicString.equals(MAGIC) == false) {
				throw new FileFormatException("file format error, please check file:" + path);
			}
			// version
			version = mappedByteBuffer.getInt();
			// nextfile
			nextFile = mappedByteBuffer.getInt();
			endPosition = mappedByteBuffer.getInt();
			// 未写满
			if (endPosition == -1) {
				this.writerPosition = db.getWriterPosition();
			} else if (endPosition == -2) {// 预分配的文件
				this.writerPosition = LogEntity.messageStartPosition;
				db.putWriterPosition(this.writerPosition);
				mappedByteBuffer.position(16);
				mappedByteBuffer.putInt(-1);
				this.endPosition = -1;

			} else {
				this.writerPosition = endPosition;
			}
			if (db.getReaderIndex() == this.currentFileNumber) {
				this.readerPosition = db.getReaderPosition();
			} else {
				this.readerPosition = LogEntity.messageStartPosition;
			}
		}
		executor.execute(new Sync());

	}

	public class Sync implements Runnable {
		@Override
		public void run() {
			while (true) {
				if (mappedByteBuffer != null) {
					try {
						mappedByteBuffer.force();
					} catch (Exception e) {
						break;
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						break;
					}
				} else {
					break;
				}
			}

		}

	}

	public int getCurrentFileNumber() {
		return this.currentFileNumber;
	}

	public int getNextFile() {
		return this.nextFile;
	}

	private boolean createLogEntity() throws IOException {
		if (file.createNewFile() == false) {
			return false;
		}
		try {
			raFile = new RandomAccessFile(file, "rwd");
			fc = raFile.getChannel();
			mappedByteBuffer = fc.map(MapMode.READ_WRITE, 0, this.fileLimitLength);
			mappedByteBuffer.put(MAGIC.getBytes());
			mappedByteBuffer.putInt(version);// 8 version
			mappedByteBuffer.putInt(nextFile);// 12next fileindex
			mappedByteBuffer.putInt(endPosition);// 16
			mappedByteBuffer.force();
			this.magicString = MAGIC;
			this.writerPosition = LogEntity.messageStartPosition;
			this.readerPosition = LogEntity.messageStartPosition;
			db.putWriterPosition(this.writerPosition);
			
			if (logger.isLoggable(Level.INFO)) {
				logger.info("success to create " + file.getAbsolutePath());
			}
			return true;
		} catch (Exception e) {
			throw new IOException("Error to create " + file.getAbsolutePath(), e);
		}
	}

	/**
	 * 记录写位置
	 * 
	 * @param pos
	 */
	private void putWriterPosition(int pos) {
		db.putWriterPosition(pos);
	}

	private void putReaderPosition(int pos) {
		db.putReaderPosition(pos);
	}

	/**
	 * write next File number id.
	 * 
	 * @param number
	 */
	public void putNextFile(int number) {
		mappedByteBuffer.position(12);
		mappedByteBuffer.putInt(number);
		this.nextFile = number;
	}

	public boolean isFull(int increment) {
		// confirm if the file is full
		if (this.fileLimitLength < this.writerPosition + increment) {
			return true;
		}
		return false;
	}

	public byte write(byte[] log) {
		int increment = log.length + 4;
		if (isFull(increment)) {
			mappedByteBuffer.position(16);
			mappedByteBuffer.putInt(this.writerPosition);
			this.endPosition = this.writerPosition;
			return WRITEFULL;
		}
		mappedByteBuffer.position(this.writerPosition);
		mappedByteBuffer.putInt(log.length);
		mappedByteBuffer.put(log);
		this.writerPosition += increment;
		putWriterPosition(this.writerPosition);
		return WRITESUCCESS;
	}

	public byte[] readNextAndRemove() throws FileEOFException {
		if (this.endPosition != -1 && this.readerPosition >= this.endPosition) {
			throw new FileEOFException("file eof");
		}
		// readerPosition must be less than writerPosition
		if (this.readerPosition >= this.writerPosition) {
			return null;
		}
		mappedByteBuffer.position(this.readerPosition);
		int length = mappedByteBuffer.getInt();
		byte[] b = new byte[length];
		this.readerPosition += length + 4;
		mappedByteBuffer.get(b);
		putReaderPosition(this.readerPosition);
		return b;
	}

	public void close() {
		try {
		    if(mappedByteBuffer==null){
		        return;
		    }
			mappedByteBuffer.force();
			SunUtil.cleanPrivileged(mappedByteBuffer, "cleaner");
			mappedByteBuffer = null;
			fc.close();
			raFile.close();
		} catch (IOException e) {
			logger.log(Level.SEVERE, "error to close logentity file:" + file.getAbsolutePath(), e);
		}
	}

	public String headerInfo() {
		StringBuilder sb = new StringBuilder();
		sb.append(" magicString:");
		sb.append(magicString);
		sb.append(" version:");
		sb.append(version);
		sb.append(" readerPosition:");
		sb.append(readerPosition);
		sb.append(" writerPosition:");
		sb.append(writerPosition);
		sb.append(" nextFile:");
		sb.append(nextFile);
		sb.append(" endPosition:");
		sb.append(endPosition);
		sb.append(" currentFileNumber:");
		sb.append(currentFileNumber);
		return sb.toString();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy