com.crankuptheamps.client.PublishStore Maven / Gradle / Ivy
////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2024 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.
* PublishStore helps in keeping track of unacknowledged messages.
* If messages are published at a rate exceeding the storage capacity,
* the buffer is resized to accommodate more messages.
*
* The PublishStore class provides a robust foundation for implementing features
* such as tracking unacknowledged messages and ensuring message persistence.
*
*/
public class PublishStore extends BlockPublishStore
{
// Constants and variables
final static int DEFAULT_INITIAL_BLOCKS = 10 * 1000;
private int _initialCapacity;
private boolean _truncateOnClose = false;
// MMapStoreBuffer class is a nested class for managing memory-mapped
// storage.
static class MMapStoreBuffer extends MemoryStoreBuffer
{
String _path;
RandomAccessFile _file;
FileChannel _channel;
// Constructor for MMapStoreBuffer
public MMapStoreBuffer(String path) throws StoreException
{
_path = path;
try {
_file = new RandomAccessFile(path, "rw");
} catch (IOException e) {
throw new StoreException(e);
}
_channel = _file.getChannel();
}
// Retrieves the current size of the storage buffer
@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();
}
// Sets the size of the storage buffer. This can be used to resize the buffer
// when needed
@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);
}
// Close and release resources associated with the memory-mapped storage
public void close() throws Exception
{
_buffer = null;
_store = null;
if (_channel != null)
_channel.close();
if (_file != null)
_file.close();
_channel = null;
_file = null;
}
// Forces data synchronization to ensure any changes made to the memory-mapped
// storage are flushed to disk
public void sync() throws IOException
{
MappedByteBuffer b = ((MappedByteBuffer) _buffer);
b.force();
}
// Finalize method for cleanup
@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;
// Calculate the count of unpersisted (unsaved) messages
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 persistent storage. This
* method ensures that any unsaved modifications are committed to the
* underlying file.
* @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();
}
}