com.crankuptheamps.client.PublishStore Maven / Gradle / Ivy
////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2020 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties. This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights. This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
package com.crankuptheamps.client;
import com.crankuptheamps.client.exception.*;
import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
* PublishStore is a memory-mapped file-backed store for storing outgoing messages.
* As messages are stored, space is allocated from a pre-created flat buffer on disk.
* As messages are discarded, space in that buffer is marked "free" for future store operations.
* If messages are stored faster than they are published, the buffer is re-sized to create more capacity.
*
* @author dnoor
*
*/
public class PublishStore extends BlockPublishStore
{
final static int DEFAULT_INITIAL_BLOCKS = 10 * 1000;
private int _initialCapacity;
private boolean _truncateOnClose = false;
static class MMapStoreBuffer extends MemoryStoreBuffer
{
String _path;
RandomAccessFile _file;
FileChannel _channel;
public MMapStoreBuffer(String path) throws StoreException
{
_path = path;
try {
_file = new RandomAccessFile(path, "rw");
} catch (IOException e) {
throw new StoreException(e);
}
_channel = _file.getChannel();
}
@Override
public long getSize() throws IOException
{
if(_buffer == null)
{
if(_file.length() == 0)
{
return 0;
}
_buffer = _channel.map(MapMode.READ_WRITE, 0, _file.length());
}
return _buffer.capacity();
}
@Override
public void setSize(long newSize) throws IOException
{
if(_channel == null)
{
throw new IOException("The store is closed.");
}
if(_buffer != null) ((MappedByteBuffer)_buffer).force();
_buffer = _channel.map(MapMode.READ_WRITE, 0, newSize);
}
public void close() throws Exception
{
_buffer = null;
_store = null;
if (_channel != null) _channel.close();
if (_file != null) _file.close();
_channel = null;
_file = null;
}
public void sync() throws IOException
{
MappedByteBuffer b = ((MappedByteBuffer)_buffer);
b.force();
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
}
/**
* Creates a new PublishStore with the given path. Immediately proceeds to recovery if the file exists.
* @param path The path (absolute or relative) of the publish store
* @throws StoreException Thrown when an operation on the store fails.
*/
public PublishStore(String path) throws StoreException
{
this(path, DEFAULT_INITIAL_BLOCKS);
}
/**
* Creates a new PublishStore with the given path. Immediately proceeds to recovery if the file exists.
* @param path The path (absolute or relative) of the publish store
* @param initialCapacity The initial capacity (in 2k blocks) of the store. This size is also used when
* resizing the store: the store is resized by this value each time the
* store grows. A general guideline for initial capacity is to set the
* capacity to messages_published_per_second * int(average_message_size / block_size) + 1.
* @throws StoreException Thrown when an operation on the store fails.
*/
public PublishStore(String path, int initialCapacity) throws StoreException
{
super(new MMapStoreBuffer(path), initialCapacity, true);
_initialCapacity = initialCapacity;
recover();
if (_usedList == null)
{
growFreeListIfEmpty();
}
}
/**
* Tells the PublishStore to truncate the file to its original size if there are no saved Messages when it is closed.
* This feature is not supported on Windows.
* @param truncate If true, file will be truncated when the PublishStore is closed.
*/
public void truncateOnClose(boolean truncate)
{
_truncateOnClose = truncate;
}
/**
* Closes the memory mapped file.
* @throws IOException Thrown when an operation on the file produces an IOException.
*/
public void close() throws Exception
{
if (_buffer == null) return;
long unpersistedCount = unpersistedCount();
_buffer.close();
if (_truncateOnClose && unpersistedCount == 0 &&
!System.getProperty("os.name").startsWith("Windows"))
{
FileChannel channel = new FileOutputStream(((MMapStoreBuffer)_buffer)._path, true).getChannel();
channel.truncate((long)_initialCapacity*BlockPublishStore.Block.SIZE);
}
_buffer = null;
}
/**
* Forces any in memory changes to be written to persistant storage.
* @throws IOException Thrown when an operation on the file produces an IOException.
*/
public void sync() throws IOException
{
((MMapStoreBuffer)_buffer).sync();
}
/**
* Overridden to call close() upon garbage collection.
*/
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
}