package io.eguan.ibs;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* 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.
* #L%
*/
import java.nio.ByteBuffer;
import java.util.Objects;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.GuardedBy;
/**
* Common code between different implementations of {@link Ibs}.
*
* @author oodrive
* @author llambert
* @author jmcaba
*
*/
abstract class IbsAbstract implements Ibs {
/** Path of the IBS. For {@link #toString()} and for lock purpose (lock out of the caller scope) */
protected final String ibsPath;
/** true when the IBS have been closed */
@GuardedBy(value = "ibsPath")
protected volatile boolean closed;
/** true when the IBS is started */
@GuardedBy(value = "ibsPath")
protected volatile boolean started;
/** true when the destruction of the IBS is in progress */
@GuardedBy(value = "ibsPath")
protected volatile boolean destroying;
protected IbsAbstract(final String ibsPath) {
super();
this.ibsPath = ibsPath;
this.started = false;
this.closed = false;
}
@Override
public final void start() throws IllegalStateException, IbsException {
synchronized (ibsPath) {
if (started || closed || destroying) {
// Already started, closed or destroy in progress
throw new IllegalStateException(toString());
}
final int retval;
if ((retval = doStart()) != 0) {
throw new IbsException(toString(), IbsErrorCode.valueOf(retval));
}
started = true;
}
}
@Override
public final void stop() throws IbsException {
synchronized (ibsPath) {
if (!started || closed) {
// Already started or closed: ignore silently
return;
}
final int retval;
if ((retval = doStop()) != 0) {
throw new IbsException(toString(), IbsErrorCode.valueOf(retval));
}
started = false;
}
}
@Override
public final boolean isStarted() {
// No need to sync here (volatile)
return started;
}
@Override
public final void close() {
synchronized (ibsPath) {
if (!closed) {
// Make sure the IBS is closed
try {
stop();
}
catch (final Throwable t) {
// Force started to false (will be closed soon)
logger.warn("Failed to stop " + this, t);
started = false;
}
// Release native resources
try {
doClose();
}
catch (final Throwable t) {
logger.warn("Failed to close " + this, t);
}
closed = true;
}
}
}
@Override
public final boolean isClosed() {
// No need to sync here (volatile)
return closed;
}
@Override
public final void destroy() throws IbsIOException {
synchronized (ibsPath) {
if (destroying) {
return;
}
// Try to stop the IBS before destroy
try {
stop();
}
catch (final Throwable t) {
// Force started to false (will be destroyed soon)
logger.warn("Failed to stop " + this, t);
started = false;
}
// Forbid new start
destroying = true;
}
// Release native resources, not under lock (long operation)
final int retval;
if ((retval = doDestroy()) != 0) {
throw new IbsIOException(toString(), IbsErrorCode.valueOf(retval));
}
// Closed by doDestroy()
synchronized (ibsPath) {
closed = true;
}
}
@Override
public final ByteBuffer get(@Nonnull final byte[] key, @Nonnegative final int length, final boolean allocateDirect)
throws IbsException, IbsIOException, IbsBufferTooSmallException, NullPointerException {
// Allocate buffer
final ByteBuffer buffer;
if (allocateDirect) {
buffer = ByteBuffer.allocateDirect(length);
}
else {
buffer = ByteBuffer.allocate(length);
}
// Get data
get(key, buffer);
return buffer;
}
@Override
public final void get(@Nonnull final byte[] key, @Nonnull final ByteBuffer data) throws IbsException,
IbsIOException, IbsBufferTooSmallException, NullPointerException {
final int readLength = get(key, data, data.position(), data.remaining());
data.position(data.position() + readLength);
}
/**
* Real start.
*
* @return 0 or an IbsErrorCode
*/
protected abstract int doStart();
/**
* Real stop.
*
* @return 0 or an IbsErrorCode
*/
protected abstract int doStop();
/**
* Real close.
*
* @return 0 or an IbsErrorCode
*/
protected abstract int doClose();
/**
* Real destroy.
*
* @return 0 or an IbsErrorCode
*/
protected abstract int doDestroy();
/**
* Utility method to check parameters.
*
* @param key
* @param data
* @param offset
* @param length
*/
protected final void checkArgs(final byte[] key, final ByteBuffer data, final int offset, final int length) {
// Check references: key is enough, data is accessed
Objects.requireNonNull(key);
if (offset < 0 || length < 1) {
throw new IllegalArgumentException();
}
if (data.capacity() < (offset + length)) {
throw new IndexOutOfBoundsException();
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#finalize()
*/
@Override
protected final void finalize() throws Throwable {
// Try to close the IBS
try {
close();
}
finally {
super.finalize();
}
}
}