blob: d95e5c357b9b2cf446786d911e322645750a960a [file] [log] [blame]
/*
* 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.jena.dboe.transaction.txn;
import java.nio.ByteBuffer;
import org.apache.jena.atlas.lib.Closeable;
import org.apache.jena.atlas.lib.Sync;
import org.apache.jena.dboe.base.file.BufferChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Helper class for the manage the persistent state of a transactional.
* The persistent state is assumed to be a fixed size, or at least
* of known maximum size.
* May not be suitable for all transactional component implementations.
*/
public abstract class StateMgrBase implements Sync, Closeable {
/* Compare to TransBlob which is a similar idea but where it is a
* component in the transaction component group. That can lead to
* the wrong control, or at least unclear, of the writing during
* the transaction commit cycle.
*/
private static Logger log = LoggerFactory.getLogger(StateMgrBase.class);
private final BufferChannel storage;
// One ByteBuffer that is reused where possible.
// This is short-term usage
// * get disk state-> deserialize
// * serialize->set disk state
// on a single thread (the transaction writer).
private ByteBuffer bb;
// Is the internal state out of sync with the disk state?
private boolean dirty = false;
protected StateMgrBase(BufferChannel storage, int sizeBytes) {
bb = ByteBuffer.allocate(sizeBytes);
this.storage = storage;
}
/**
* After the default initial state is known, call this, for example, at the
* end of the constructor. If no on-disk state is found, a clean copy is written.
*/
protected void init() {
if ( ! storage.isEmpty() )
readState();
else
writeState();
}
/* Serialize the necessary state into a ByteBuffer.
* The argument ByteBuffer can be used or a new one returned
* if it is the wrong size (e.g. too small). The returned one will become the
* recycled ByteBuffer. The returned ByteBuffer should have posn/limit
* delimiting the necessary space to write.
*/
protected abstract ByteBuffer serialize(ByteBuffer bytes);
/*
* Deserialize the persistent state from the ByteBuffer (delimited by posn/limit).
* The byte buffer will be the recycled one from last time.
* Most
*/
protected abstract void deserialize(ByteBuffer bytes);
/** Note that the in-memory state is not known to be the same
* as the on-disk state.
*/
protected void setDirtyFlag() {
dirty = true;
}
/**
* Event call for state writing. Called after successful
* writing of the state.
*/
protected abstract void writeStateEvent();
/**
* Event call for state reading. Called after successful
* deserializing of the state.
*/
protected abstract void readStateEvent();
/** Note that the in-memory state is the same
* as the on-disk state, or at least the on-disk state is
* acceptable for restart at any time.
*/
protected void clearDirtyFlag() {
dirty = false;
}
/** Low level control - for example, used for cloning setup.
* Use with care.
*/
public BufferChannel getBufferChannel() {
return storage;
}
/** Return the serialized state using the internal ByteBuffer
* Typically called by "prepare" for the bytes to write to the journal.
* Calls {@link #serialize}.
* This method does not perform an external I/O.
*/
public ByteBuffer getState() {
bb.rewind();
serialize(bb);
return bb;
}
/** Set the in-memory state from a ByteBuffer, for example, from journal recovery.
* This method does not perform an external I/O.
* Call "writeState" to put the n-memory state as the disk state.
*/
public void setState(ByteBuffer buff) {
buff.rewind();
deserialize(buff);
dirty = true;
}
//public BufferChannel getChannel() { return storage; }
/** The write process : serialize, write, sync,
* After this, the bytes definitely are on disk, not in some OS cache
*/
public void writeState() {
bb.rewind();
ByteBuffer bb1 = serialize(bb);
if ( bb1 != null )
bb = bb1;
bb.rewind();
int len = storage.write(bb, 0);
storage.sync();
dirty = false;
writeStateEvent();
}
/** The read process : get all bytes on disk, deserialize */
public void readState() {
bb.rewind();
int len = storage.read(bb, 0);
bb.rewind();
deserialize(bb);
readStateEvent();
}
@Override
public void sync() {
if ( dirty )
writeState();
}
@Override
public void close() {
storage.close();
}
}