Core utilities and common classes for Elasticsearch, providing fundamental building blocks like resource management, IO utilities, time handling, and reference counting.
—
Resource management in Elasticsearch Core provides consistent lifecycle patterns for objects that require explicit cleanup. The system is built around reference counting and releasable interfaces, ensuring proper resource cleanup and preventing memory leaks in long-running applications.
Reference counting interface for managing object lifecycles. Objects start with a reference count and are cleaned up when the count reaches zero.
/**
* Interface for objects that support reference counting for lifecycle management
*/
public interface RefCounted {
/** Increment the reference count */
void incRef();
/** Try to increment reference count, returns false if already closed */
boolean tryIncRef();
/** Decrement reference count, closes object when count reaches zero */
boolean decRef();
/** Check if object still has active references */
boolean hasReferences();
/** Assert increment reference count (throws if closed) */
default void mustIncRef();
/** No-op RefCounted instance that is always referenced */
RefCounted ALWAYS_REFERENCED = new RefCounted() {
// Implementation details omitted for brevity
};
}Usage Examples:
import org.elasticsearch.core.RefCounted;
import org.elasticsearch.core.AbstractRefCounted;
// Create a ref-counted resource
RefCounted resource = AbstractRefCounted.of(() -> {
System.out.println("Resource cleaned up");
});
// Increment reference before sharing
resource.incRef();
shareResource(resource);
// Always decrement when done
resource.decRef(); // Will close when count reaches 0Simplified closeable interface that only throws RuntimeException, making it easier to use in functional contexts.
/**
* A Closeable that can only throw a RuntimeException
*/
public interface Releasable extends Closeable {
/** Close the resource, only throws RuntimeException */
void close();
}Base implementation of RefCounted that provides thread-safe reference counting with customizable cleanup logic.
/**
* Base RefCounted implementation with reference count starting at 1
*/
public abstract class AbstractRefCounted implements RefCounted {
/** Protected constructor for subclasses */
protected AbstractRefCounted();
/** Increment reference count */
public final void incRef();
/** Must increment reference count (throws if closed) */
public final void mustIncRef();
/** Try to increment reference count */
public final boolean tryIncRef();
/** Decrement reference count */
public final boolean decRef();
/** Check if references exist */
public final boolean hasReferences();
/** Get current reference count */
public final int refCount();
/** Abstract method for cleanup implementation */
protected abstract void closeInternal();
/** Factory method for simple cleanup actions */
public static AbstractRefCounted of(Runnable onClose);
/** Override for debugging access patterns */
protected void touch();
/** Handle already closed state */
protected void alreadyClosed();
/** Error message constant */
public static final String ALREADY_CLOSED_MESSAGE = "RefCounted is already closed";
/** Error message constant */
public static final String INVALID_DECREF_MESSAGE = "RefCounted decRef without incRef";
}Usage Examples:
// Custom RefCounted implementation
public class DatabaseConnection extends AbstractRefCounted {
private final Connection connection;
public DatabaseConnection(Connection connection) {
this.connection = connection;
}
@Override
protected void closeInternal() {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public ResultSet query(String sql) throws SQLException {
if (!hasReferences()) {
throw new IllegalStateException("Connection is closed");
}
return connection.prepareStatement(sql).executeQuery();
}
}
// Using the factory method
AbstractRefCounted fileResource = AbstractRefCounted.of(() -> {
try {
Files.deleteIfExists(tempFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
});RefCounted implementation with no-op close behavior. It is the responsibility of the caller to run whatever release logic should be executed when decRef() returns true.
/**
* RefCounted implementation with no-op close behavior
*/
public class SimpleRefCounted extends AbstractRefCounted {
/** No-op close implementation */
protected void closeInternal();
}Iterator that implements Releasable for proper resource cleanup during iteration.
/**
* An iterator that implements Releasable for resource cleanup
*/
public interface ReleasableIterator<T> extends Releasable, Iterator<T> {
/** Create iterator with single element */
static <T extends Releasable> ReleasableIterator<T> single(T element);
/** Create empty iterator */
static <T extends Releasable> ReleasableIterator<T> empty();
}Utility methods for managing collections of Releasable objects with proper exception handling.
/**
* Utility methods for managing Releasable objects
*/
public enum Releasables {
; // Utility enum with static methods only
/** Close multiple releasables, collecting exceptions */
public static void close(Iterable<? extends Releasable> releasables);
/** Close single releasable safely */
public static void close(@Nullable Releasable releasable);
/** Close multiple releasables safely */
public static void close(Releasable... releasables);
/** Close expecting no exceptions (throws AssertionError if exceptions occur) */
public static void closeExpectNoException(Releasable... releasables);
/** Close single releasable expecting no exceptions */
public static void closeExpectNoException(Releasable releasable);
/** Close while handling other exceptions */
public static void closeWhileHandlingException(Releasable... releasables);
/** Wrap multiple releasables into single releasable */
public static Releasable wrap(Iterable<? extends Releasable> releasables);
/** Wrap iterator of releasables */
public static Releasable wrap(Iterator<Releasable> releasables);
/** Wrap array of releasables */
public static Releasable wrap(Releasable... releasables);
/** Ensure releasable is only closed once */
public static Releasable releaseOnce(Releasable releasable);
/** Add assertion checking to releasable */
public static Releasable assertOnce(Releasable delegate);
}Usage Examples:
import org.elasticsearch.core.Releasables;
import org.elasticsearch.core.Releasable;
import java.util.List;
// Close multiple resources safely
List<Releasable> resources = Arrays.asList(resource1, resource2, resource3);
Releasables.close(resources);
// Wrap multiple resources for batch cleanup
Releasable batchResource = Releasables.wrap(resource1, resource2, resource3);
// Later: batchResource.close(); // Closes all wrapped resources
// Ensure single-close semantics
Releasable onceOnly = Releasables.releaseOnce(resource);
onceOnly.close(); // Works
onceOnly.close(); // Safe no-opInstall with Tessl CLI
npx tessl i tessl/maven-org-elasticsearch--elasticsearch-core