Context.java
package com.renomad.minum.state;
import com.renomad.minum.database.Db;
import com.renomad.minum.database.DbData;
import com.renomad.minum.database.DbEngine2;
import com.renomad.minum.logging.ILogger;
import com.renomad.minum.queue.ActionQueueState;
import com.renomad.minum.web.FullSystem;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.ReentrantLock;
/**
* Holds important system-wide data and methods, such as the
* logger, constants, and the {@link FullSystem} instance.
* <p>
* The common situations:
* </p>
* <ul>
* <li>Building a Minum {@link Db} database</li>
* <li>Getting system constants like the database directory</li>
* <li>Getting the system {@link ExecutorService} for starting threads or an {@link com.renomad.minum.queue.ActionQueue}</li>
* <li>Getting a {@link FullSystem} object, which has
* <ul>
* <li>the {@link com.renomad.minum.web.WebFramework}, which registers endpoints</li>
* <li>the {@link com.renomad.minum.security.TheBrig}, which handles bad actors on the internet</li>
* </ul>
* </li>
* </ul>
*/
public final class Context {
private final ILogger logger;
private final ExecutorService executorService;
private final Constants constants;
private FullSystem fullSystem;
private final ActionQueueState actionQueueState;
/**
* A record of which paths are registered for database
* use, to prevent multiple databases unknowingly pointing
* to the same directory.
*/
private final Set<Path> registeredDatabasePaths;
/**
* Used to provide thread-safe access to the {@link #registeredDatabasePaths}
*/
private final ReentrantLock databasePathsLock;
public Context(ExecutorService executorService, Constants constants, ILogger logger) {
this.executorService = executorService;
this.constants = constants;
actionQueueState = new ActionQueueState();
this.logger = logger;
this.registeredDatabasePaths = new HashSet<>();
this.databasePathsLock = new ReentrantLock();
}
public ILogger getLogger() {
return logger;
}
public ExecutorService getExecutorService() {
return executorService;
}
public Constants getConstants() {
return constants;
}
public void setFullSystem(FullSystem fullSystem) {
this.fullSystem = fullSystem;
}
public FullSystem getFullSystem() {
return fullSystem;
}
public ActionQueueState getActionQueueState() {
return actionQueueState;
}
/**
* This is a helper method to instantiate a {@link Db} class,
* avoiding the need for a user to provide the root database
* directory and the context.
* <p>
* Since this is a generic method, a bit of care is required when
* calling. Try to use a pattern like the following pseudocode:
* {@snippet :
* Db<Photograph> photoDb = context.getDb("photos", new Photograph());
* }
* @param name the name of this data. Note that this will be used
* as the directory for the data, so use characters your
* operating system would allow.
* @param instance an instance of the {@link DbData} data. This is used in the
* Db code to deserialize the data when reading.
*/
public <T extends DbData<?>> Db<T> getDb(String name, T instance) {
return new Db<>(Path.of(constants.dbDirectory, name), this, instance);
}
/**
* This is a helper method to instantiate a {@link DbEngine2} class,
* using the engine2 database implementation. It is similar to
* {@link #getDb(String, DbData)} in all other respects.
* <p>
* By switching your old database calls to use this, when it runs
* it will convert the file schema.
* </p>
* <p>
* <b>Please backup your database before conversion</b>
* </p>
*/
public <T extends DbData<?>> DbEngine2<T> getDb2(String name, T instance) {
return new DbEngine2<>(Path.of(constants.dbDirectory, name), this, instance);
}
/* ***********************************************
Database path registrations
This next section includes some methods for managing
the set of registered database paths in use by
the system
*********************************************** */
/**
* Return true if the input path exists in the set
*/
public boolean isDbPathRegistered(Path path) {
return this.registeredDatabasePaths.contains(path);
}
/**
* Add a path to the set of paths we are tracking as
* registered for a database. This is used to prevent
* pointing two databases at the same directory, which
* would lead to data corruption.
*/
public void addToDbPaths(Path path) {
this.databasePathsLock.lock();
try {
logger.logDebug(() -> "Adding registration for database path " + path);
this.registeredDatabasePaths.add(path);
} finally {
this.databasePathsLock.unlock();
}
}
/**
* Remove an item from the set of registered database paths,
* run when a database shuts down.
*/
public void removeFromPaths(Path path) {
this.databasePathsLock.lock();
try {
logger.logDebug(() -> "Removing registration for database path " + path);
this.registeredDatabasePaths.remove(path);
} finally {
this.databasePathsLock.unlock();
}
}
/**
* Remove all registered paths from the set, mostly
* used when testing
*/
public void clearDatabasePaths() {
this.databasePathsLock.lock();
try {
logger.logDebug(() -> "Removing all registered database paths: " + registeredDatabasePaths);
this.registeredDatabasePaths.clear();
} finally {
this.databasePathsLock.unlock();
}
}
}