Config, Generator Singleton rewrite
Started setting up main class, started documentation
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package space.b00tload.services.snowflake;
|
||||
|
||||
import space.b00tload.utils.configuration.ConfigValues;
|
||||
|
||||
public enum ConfigurationValues implements ConfigValues {
|
||||
MACHINE_ID_BITS("machinebits", "M", "MACHINE_ID_BITS", "bitcount.machine", "10"),
|
||||
SEQUENCE_BITS("sequencebits", "S", "SEQUENCE_BITS", "bitcount.sequence", "12"),
|
||||
EPOCH("epoch", "E", "EPOCH", "epoch", "1704067200000"),
|
||||
MACHINE_ID("machineid", "I", "MACHINE_ID", "machineid", "-1"),
|
||||
ORCHESTRATOR_IP("orchestrator", "O", "ORCHESTRATOR_IP", "orchestrator.ip", "localhost"),
|
||||
;
|
||||
|
||||
private final String cliFlag;
|
||||
private final String cliAlias;
|
||||
private final String envVariable;
|
||||
private final String tomlPath;
|
||||
private final String defaultValue;
|
||||
|
||||
ConfigurationValues(String cliFlag, String cliAlias, String envVariable, String tomlPath, String defaultValue) {
|
||||
this.cliFlag = cliFlag;
|
||||
this.cliAlias = cliAlias;
|
||||
this.envVariable = envVariable;
|
||||
this.tomlPath = tomlPath;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command line flag for this config value.
|
||||
*
|
||||
* @return the command line flag
|
||||
* @example --discord-token
|
||||
*/
|
||||
@Override
|
||||
public String getFlag() {
|
||||
return cliFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command line alias for this config value.
|
||||
*
|
||||
* @return the command line alias
|
||||
* @example -d
|
||||
*/
|
||||
@Override
|
||||
public String getFlagAlias() {
|
||||
return cliAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* The environment variable name for this config value.
|
||||
*
|
||||
* @return the environment variable name
|
||||
* @example DISCORD_APP_TOKEN
|
||||
*/
|
||||
@Override
|
||||
public String getEnvironmentVariable() {
|
||||
return envVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to the config value in a toml file.
|
||||
*
|
||||
* @return the toml path.
|
||||
* @example discord.token
|
||||
*/
|
||||
@Override
|
||||
public String getTomlPath() {
|
||||
return tomlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default value used if not set via other configuration means.
|
||||
*
|
||||
* @return the default value.
|
||||
*/
|
||||
@Override
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfigurationValues{" +
|
||||
"defaultValue='" + defaultValue + '\'' +
|
||||
", tomlPath='" + tomlPath + '\'' +
|
||||
", envVariable='" + envVariable + '\'' +
|
||||
", cliAlias='" + cliAlias + '\'' +
|
||||
", cliFlag='" + cliFlag + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,107 @@
|
||||
package space.b00tload.services.snowflake;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import space.b00tload.utils.configuration.Configuration;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
|
||||
/**
|
||||
* Generates the SnowflakeIDs. Must be initialized with a machine ID between 0 and 1023.
|
||||
* Can generate up to 4096 IDs per millisecond. If this limit is hit, generation will be halted until the next millisecond.
|
||||
*
|
||||
* @author Alix von Schirp
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class SnowflakeIDGenerator {
|
||||
|
||||
//Singleton instance
|
||||
private static SnowflakeIDGenerator INSTANCE;
|
||||
|
||||
// Constants
|
||||
private static final long EPOCH = 1704067200000L; // January 1, 2024, 00:00:00 UTC
|
||||
private static final long MACHINE_ID_BITS = 10L;
|
||||
private static final long SEQUENCE_BITS = 12L;
|
||||
/**
|
||||
* Epoch for this generator.
|
||||
* Default is {@code January 1, 2024, 00:00:00 UTC}.
|
||||
*/
|
||||
private final long EPOCH;
|
||||
/**
|
||||
* Amount of bits reserved for machine ID.
|
||||
* Default is 10.
|
||||
*/
|
||||
private final long MACHINE_ID_BITS;
|
||||
/**
|
||||
* Amount of bits reserved for sequence counter.
|
||||
* Default is 12.
|
||||
*/
|
||||
private final long SEQUENCE_BITS;
|
||||
|
||||
private static final long MAX_MACHINE_ID = (1L << MACHINE_ID_BITS) - 1;
|
||||
private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;
|
||||
/**
|
||||
* The calculated maximum machine ID.
|
||||
* Defaults to 1023.
|
||||
*/
|
||||
private final long MAX_MACHINE_ID;
|
||||
/**
|
||||
* The calculated maximum sequence counter value.
|
||||
* Defaults to 4095.
|
||||
*/
|
||||
private final long MAX_SEQUENCE;
|
||||
|
||||
private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
|
||||
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
|
||||
/**
|
||||
* How far to shift the machine ID (timestamp + machine + <b><i>sequence</i></b>)
|
||||
*/
|
||||
private final long MACHINE_ID_SHIFT;
|
||||
/**
|
||||
* How far to shift the timestamp (timestamp + <b><i>machine + sequence</i></b>
|
||||
*/
|
||||
private final long TIMESTAMP_SHIFT;
|
||||
|
||||
// State
|
||||
/**
|
||||
* The ID used by the machine.
|
||||
*/
|
||||
private final long machineId;
|
||||
/**
|
||||
* The sequence counter.
|
||||
*/
|
||||
private long sequence = 0L;
|
||||
/**
|
||||
* The unix timestamp at which the last ID was generated.
|
||||
*/
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
// Constructor
|
||||
public SnowflakeIDGenerator(long machineId) {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param machineId The machineID used for generation
|
||||
* @param epoch The snowflake epoch used for generation
|
||||
* @param machineIdBits The amount of bits used for the machineID
|
||||
* @param sequenceBits The amount of bits used for sequence counter
|
||||
*/
|
||||
private SnowflakeIDGenerator(long machineId, @Nullable Long epoch, @Nullable Long machineIdBits, @Nullable Long sequenceBits) {
|
||||
//init constants
|
||||
MACHINE_ID_BITS = Objects.requireNonNullElse(machineIdBits,
|
||||
Long.parseLong(Configuration.getInstance().get(ConfigurationValues.MACHINE_ID_BITS)));
|
||||
MAX_MACHINE_ID = (1L << MACHINE_ID_BITS) - 1;
|
||||
|
||||
SEQUENCE_BITS = Objects.requireNonNullElse(sequenceBits,
|
||||
Long.parseLong(Configuration.getInstance().get(ConfigurationValues.SEQUENCE_BITS)));
|
||||
MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;
|
||||
|
||||
EPOCH = Objects.requireNonNullElse(epoch,
|
||||
Long.parseLong(Configuration.getInstance().get(ConfigurationValues.EPOCH)));
|
||||
|
||||
MACHINE_ID_SHIFT = SEQUENCE_BITS;
|
||||
TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
|
||||
|
||||
if(EPOCH > currentTimeMillis()){
|
||||
throw new IllegalArgumentException("Epoch may not be in the future.");
|
||||
}
|
||||
|
||||
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
|
||||
throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID);
|
||||
}
|
||||
@@ -55,10 +135,7 @@ public class SnowflakeIDGenerator {
|
||||
| sequence;
|
||||
}
|
||||
|
||||
// Get the current timestamp in milliseconds
|
||||
private long currentTimeMillis() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
// Wait for the next timestamp
|
||||
private long waitForNextTimestamp(long currentTimestamp) {
|
||||
@@ -68,6 +145,51 @@ public class SnowflakeIDGenerator {
|
||||
}
|
||||
return nextTimestamp;
|
||||
}
|
||||
|
||||
public static void init(long machineId, long epoch, long machineIdBits, long sequenceBits){
|
||||
INSTANCE = new SnowflakeIDGenerator(machineId, epoch, machineIdBits, sequenceBits);
|
||||
}
|
||||
|
||||
public static void init(long machineId){
|
||||
INSTANCE = new SnowflakeIDGenerator(machineId, null, null, null);
|
||||
}
|
||||
|
||||
public static SnowflakeIDGenerator getInstance() {
|
||||
if(Objects.isNull(INSTANCE)) throw new UnsupportedOperationException("SnowflakeIDGenerator is not initialized.");
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public long getEPOCH() {
|
||||
return EPOCH;
|
||||
}
|
||||
|
||||
public long getMACHINE_ID_BITS() {
|
||||
return MACHINE_ID_BITS;
|
||||
}
|
||||
|
||||
public long getSEQUENCE_BITS() {
|
||||
return SEQUENCE_BITS;
|
||||
}
|
||||
|
||||
public long getMAX_MACHINE_ID() {
|
||||
return MAX_MACHINE_ID;
|
||||
}
|
||||
|
||||
public long getMAX_SEQUENCE() {
|
||||
return MAX_SEQUENCE;
|
||||
}
|
||||
|
||||
public long getMACHINE_ID_SHIFT() {
|
||||
return MACHINE_ID_SHIFT;
|
||||
}
|
||||
|
||||
public long getTIMESTAMP_SHIFT() {
|
||||
return TIMESTAMP_SHIFT;
|
||||
}
|
||||
|
||||
public long getMachineId() {
|
||||
return machineId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,62 @@
|
||||
package space.b00tload.services.snowflake;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.joran.JoranConfigurator;
|
||||
import ch.qos.logback.core.joran.spi.JoranException;
|
||||
import ch.qos.logback.core.util.StatusPrinter2;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import space.b00tload.utils.configuration.Configuration;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SnowflakeService {
|
||||
|
||||
private static String SOFTWARE_VERSION;
|
||||
private static String APPLICATION_BASE;
|
||||
private static String USER_AGENT;
|
||||
private static Logger LOGGER;
|
||||
|
||||
public static void main(String[] args) {
|
||||
//Set up constants
|
||||
SOFTWARE_VERSION = Objects.requireNonNullElse(SnowflakeService.class.getPackage().getImplementationVersion(), "0.0.1-indev");
|
||||
USER_AGENT = "SnowflakeService " + SOFTWARE_VERSION + "(" + System.getProperty("os.name") + "; " + System.getProperty("os.arch") + ") Java/" + System.getProperty("java.version");
|
||||
APPLICATION_BASE = List.of(args).contains("--docker") ? Paths.get("data", "b00tload-tools", "snowflake").toString() : Paths.get(System.getProperty("user.home"), ".b00tload-tools", "snowflake").toString();
|
||||
|
||||
//Set up logger
|
||||
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
try{
|
||||
JoranConfigurator configurator = new JoranConfigurator();
|
||||
configurator.setContext(loggerContext);
|
||||
loggerContext.reset();
|
||||
if(List.of(args).contains("--docker")) {
|
||||
configurator.doConfigure(Objects.requireNonNull(SnowflakeService.class.getResource("/config/logback/logback-docker.xml")));
|
||||
} else {
|
||||
configurator.doConfigure(Objects.requireNonNull(SnowflakeService.class.getResource("/config/logback/logback-bare.xml")));
|
||||
}
|
||||
} catch (JoranException ignored){}
|
||||
(new StatusPrinter2()).printInCaseOfErrorsOrWarnings(loggerContext);
|
||||
|
||||
//Set up logger
|
||||
LOGGER = LoggerFactory.getLogger(SnowflakeService.class);
|
||||
|
||||
//Init config
|
||||
Configuration.init(args, SOFTWARE_VERSION, APPLICATION_BASE, ConfigurationValues.values());
|
||||
|
||||
long machineID;
|
||||
long sequenceBits;
|
||||
long machineBits;
|
||||
long epoch;
|
||||
|
||||
if(!Configuration.getInstance().get(ConfigurationValues.ORCHESTRATOR_IP).equals("localhost")){
|
||||
|
||||
} else if((Long.parseLong(Configuration.getInstance().get(ConfigurationValues.MACHINE_ID))) != -1L) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user