/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.bigdata.rwstore.sector;
import java.io.File;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.UUID;
import com.bigdata.counters.CounterSet;
import com.bigdata.counters.OneShotInstrument;
import com.bigdata.io.DirectBufferPool;
import com.bigdata.journal.AbstractBufferStrategy;
import com.bigdata.mdi.IResourceMetadata;
import com.bigdata.rawstore.AbstractRawStore;
import com.bigdata.rawstore.IAddressManager;
import com.bigdata.rawstore.IAllocationContext;
import com.bigdata.rawstore.IAllocationManagerStore;
import com.bigdata.rawstore.IPSOutputStream;
import com.bigdata.rawstore.IRawStore;
import com.bigdata.rawstore.TransientResourceMetadata;
/**
* An {@link IRawStore} backed by an {@link IMemoryManager}.
*
* @author thompsonbry
*/
public class MemStore extends AbstractRawStore implements IAllocationManagerStore {
// private static final transient Logger log = Logger
// .getLogger(MemStore.class);
/**
* The backing store implementation.
*/
private final MemStrategy m_strategy;
/**
* The {@link UUID} for the store.
*/
private final UUID m_uuid;
/**
* Create a new instance.
*
* @param pool
* The pool from which the backing direct {@link ByteBuffer}s
* will be allocated.
*/
public MemStore(final DirectBufferPool pool) {
this(pool, Integer.MAX_VALUE);
}
/**
* Create a new instance.
*
* @param pool
* The pool from which the backing direct {@link ByteBuffer}s
* will be allocated.
* @param bufferCapacity
* The maximum #of buffers which may be allocated by this
* {@link MemStore} from that pool. This may be
* {@link Integer#MAX_VALUE} for an effectively unlimited
* capacity.
*/
public MemStore(final DirectBufferPool pool, final int bufferCapacity) {
if (pool == null)
throw new IllegalArgumentException();
if (bufferCapacity <= 0)
throw new IllegalArgumentException();
m_strategy = new MemStrategy(new MemoryManager(pool, bufferCapacity));
m_uuid = UUID.randomUUID();
}
/**
* Wrap an existing {@link IMemoryManager}.
*
* @param mmgr
* The {@link IMemoryManager}.
*/
public MemStore(final IMemoryManager mmgr) {
this(mmgr, UUID.randomUUID());
}
/**
* Private constructor used to return a {@link MemStore} backed by a new
* {@link IMemoryManager} allocation context.
*
* @param mmgr
* The {@link IMemoryManager}.
* @param storeId
* The {@link UUID} of the store.
*/
private MemStore(final IMemoryManager mmgr, final UUID storeId) {
if (mmgr == null)
throw new IllegalArgumentException();
m_strategy = new MemStrategy(mmgr);
m_uuid = storeId;
}
public MemStrategy getStrategy() {
return m_strategy;
}
/**
* Return the backing {@link IMemoryManager}.
*/
public IMemoryManager getMemoryManager() {
return m_strategy.getStore();
}
/**
* Return a new view of the {@link MemStore} backed by a child
* {@link IMemoryManager}. Allocations against the {@link MemStore} may be
* released in bulk by {@link #close()}. The life cycle of the returned
* {@link MemStore} is bounded by the life cycle of the {@link MemStore}
* from which it was allocated.
*
* @see IMemoryManager#createAllocationContext()
*/
public MemStore createAllocationContext() {
return new MemStore(m_strategy.getStore().createAllocationContext(), m_uuid);
}
public ByteBuffer read(final long addr) {
return m_strategy.read(addr);
}
public long write(final ByteBuffer data) {
return m_strategy.write(data);
}
@Override
public long write(ByteBuffer data, IAllocationContext context) {
return m_strategy.write(data, context);
}
public void delete(final long addr) {
m_strategy.delete(addr);
}
@Override
public void delete(long addr, IAllocationContext context) {
m_strategy.delete(addr, context);
}
public CounterSet getCounters() {
final CounterSet root = new CounterSet();
root.addCounter("UUID", new OneShotInstrument<String>(getUUID()
.toString()));
root.attach(m_strategy.getStore().getCounters());
return root;
}
private volatile boolean open = true;
private void assertOpen() {
if (!isOpen())
throw new IllegalStateException(AbstractBufferStrategy.ERR_NOT_OPEN);
}
public boolean isOpen() {
return open;
}
public void close() {
// throw exception if open per the API.
assertOpen();
open = false;
m_strategy.close();
}
public void deleteResources() {
if (open)
throw new IllegalStateException(AbstractBufferStrategy.ERR_OPEN);
m_strategy.deleteResources();
}
public void destroy() {
// close w/o exception throw.
open = false;
// delete the backing store.
deleteResources();
}
/**
* NOP since the {@link MemStore} is not backed by stable media.
* <p>
* {@inheritDoc}
*/
public void force(final boolean metadata) {
// NOP
}
/**
* This method always returns <code>null</code> since there is no backing
* file.
* <p>
* {@inheritDoc}
*/
public File getFile() {
return null;
}
public IResourceMetadata getResourceMetadata() {
return new TransientResourceMetadata(m_uuid);
}
public UUID getUUID() {
return m_uuid;
}
public boolean isFullyBuffered() {
return true;
}
public boolean isReadOnly() {
return false;
}
public boolean isStable() {
return false;
}
/**
* {@inheritDoc}
* <p>
* Overridden to return the #of bytes in the allocation slots rather than
* the user bytes. This is because the {@link MemoryManager}, like the
* {@link RWStore}, does not know the #of bytes of user data in each
* allocation slot. Therefore it is not able to keep accurate track of the
* user bytes as allocation slots are cycled.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/212 (Records must
* store the as-written length for HA failover reads to be successful.)
*/
@Override
public long size() {
return m_strategy.size();
}
/*
* IAddressManager
*/
public IAddressManager getAddressManager() {
return m_strategy.getAddressManager();
}
public int getByteCount(final long addr) {
return getAddressManager().getByteCount(addr);
}
public long getOffset(final long addr) {
return getAddressManager().getOffset(addr);
}
public long getPhysicalAddress(long addr) {
return getAddressManager().getPhysicalAddress(addr);
}
public long toAddr(final int nbytes, final long offset) {
return getAddressManager().toAddr(nbytes, offset);
}
public String toString(final long addr) {
return getAddressManager().toString(addr);
}
/*
* Delegate stream handling to MemoryManager
*/
public IPSOutputStream getOutputStream() {
return m_strategy.getOutputStream();
}
/**
* Return an output stream which can be used to write on the backing store
* within the given allocation context. You can recover the address used to
* read back the data from the {@link IPSOutputStream}.
*
* @param context
* The context within which any allocations are made by the
* returned {@link IPSOutputStream}.
*
* @return an output stream to stream data to and to retrieve an address to
* later stream the data back.
*/
public IPSOutputStream getOutputStream(final IAllocationContext context) {
return m_strategy.getOutputStream(context);
}
/**
* Return an input stream from which a previously written stream may be read
* back.
*
* @param addr
* The address at which the stream was written.
*
* @return an input stream for the data for provided address
*/
public InputStream getInputStream(long addr) {
return m_strategy.getInputStream(addr);
}
}