Skip to content

Jackson Configuration

Lilly Tempest edited this page Apr 12, 2023 · 4 revisions

Sonatype Nexus (Releases) Sonatype Nexus (Development) Sonatype Nexus (Snapshots)

repositories {
    maven("https://eldonexus.de/repository/maven-public")
}
dependencies {
    implementation("de.eldoria.util", "jackson-configuration", "version")
}
<repository>
    <id>EldoNexus</id>
    <url>https://eldonexus.de/repository/maven-public/</url>
</repository>

<dependency>
    <groupId>de.eldoria.util</groupId>
    <artifactId>jackson-configuration</artifactId>
    <version>version</version>
</dependency>

The jackson-configuration module bundles the jackson-bukkit library and is build on top of the jackson serialization library. This library allows to easily work with bukkit objects like Location in a configuration file.

Sample implementation

For an explanation what we are doing here see the points below! This is purely for quick reference!

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        // Defining the key for the config.yml
        ConfigKey<ConfigFile> defKey = ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new);
        var config = new JacksonConfig<>(this, defKey);
        // Getting the config.yml data
        ConfigFile general = config.main();

        // Defining a second config key for database.yml
        ConfigKey<Database> databaseConfig = ConfigKey.of("Database Config", Path.of("database.yml"), Database.class, Database::new);
        // Loading the file and creating it, if it doesn't exist yet
        Database database = config.secondary(databaseConfig);
        // Retrieving a wrapped instance which will be updated during config reloads
        Wrapper<Database> databaseWrapped = config.secondaryWrapped(databaseConfig);

        // Reloading the config with the key
        config.reload(databaseConfig);
        // Reloading all configuration files
        config.reload();
        
        // Saving a specific file
        config.save(databaseConfig);
        // Saving all files
        config.save();
    }
}

The core of the module is the JacksonConfig class, which you have either to extend to customize it or just to instantiate in order to read files.

In order to create a jackson config we need two things:

  1. A class representing the content of our configuration file.
  2. A ConfigKey associated with the configuration file.

Creating a Class representation

Our configuration file shall contain our database settings and general settings for our plugin. This means we need to create three classes:

  • ConfigFile which represents the root of our configuration
  • General which represents the general section
  • Database which represents the database section

You define the default values by setting a value on the fields. Make sure your class has an empty default constructor if you define a constructor with arguments.

Fields can not be final, but private.

Classes
public class ConfigFile {
    private Database database = new Database();
    private General general = new General();

    public Database database() {
        return database;
    }

    public General general() {
        return general;
    }
}

public class Database {
    private String host = "localhost";
    private int port = 3306;
    private String user = "root";
    private String password = "root";

    public String host() {
        return host;
    }

    public int port() {
        return port;
    }

    public String user() {
        return user;
    }

    public String password() {
        return password;
    }
}

public class General{
    private String language = "en_US";
    private String prefix = "[Plugin]";

    public String language() {
        return language;
    }

    public String prefix() {
        return prefix;
    }
}

Loading a file with the Jackson Configuration

To create our Jackson Configuration we need to create a config key pointing to our config.yml. For that we can use ConfigKey.defaultConfig()`. Additionally we need our plugin instance to know in which plugin directory the file should be located.

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        var config = new JacksonConfig<>(this, ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new));
    }
}

A config key requires usually 4 parameters

  1. A human readable name. This one is purely for you and might be used for debugging
  2. A relative or absolute path to the file. E.g. `Path.of("config.yml") for the default config.yml
  3. The class representing the file. E.g. ConfigFile.class
  4. A supplier creating an instance of the class representing the file.

Now we can easily access the main configuration file via the main() method. On calling this method first the file will be loaded or created and loaded if it doesn't exist yet.

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        var config = new JacksonConfig<>(this, ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new));
        Database database = config.main().database();
        General general = config.main().general();
    }
}

Loading a secondary file

The jackson config also allows to load secondary files beside the default configuration. This can be done by calling the secondary(ConfigKey) method.

Lets assume we don't want our database settings being part of the config.yml, but to be a separate file called datbase.yml. To do this we create a new config key for another file and set the values to our Database class.

ConfigKey.of("Database Config", Path.of("database.yml"), Database.class, Database::new)

Now we load it via the JacksonConfig

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        ConfigKey<ConfigFile> defKey = ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new);
        var config = new JacksonConfig<>(this, defKey);
        General general = config.main().general();

        ConfigKey<Database> databaseConfig = ConfigKey.of("Database Config", Path.of("database.yml"), Database.class, Database::new);
        Database database = config.secondary(databaseConfig);
    }
}

Saving a file

You can either save all files or only a specific file by providing a config key.

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        ConfigKey<ConfigFile> defKey = ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new);
        var config = new JacksonConfig<>(this, defKey);
        General general = config.main().general();

        ConfigKey<Database> databaseConfig = ConfigKey.of("Database Config", Path.of("database.yml"), Database.class, Database::new);
        Database database = config.secondary(databaseConfig);

        // Saves all configurations loaded via this instance
        config.save();
        // Saves the configuration associated with this key
        config.save(defKey);
    }

Reloading files

The same works with reloading files. You can either reload all files loaded via the configuration or a single file

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        ConfigKey<ConfigFile> defKey = ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new);
        var config = new JacksonConfig<>(this, defKey);

        // Reloads all configurations loaded via this instance
        config.reload();
        // Reloads the configuration associated with this key
        config.reload(defKey);
    }

Keeping persistence over configuration reloads

If you wanna store your files during a reload you need to use the wrapped call. This wrapps the configuration into a wrapper, which references the file via the key. This will also update the underlying data of the file even when reloading and stored somewhere else.

class JacksonPlugin extends JavaPlugin {

    @Override
    public void onEnable(){
        ConfigKey<ConfigFile> defKey = ConfigKey.defaultConfig(ConfigFile.class, ConfigFile::new);
        var config = new JacksonConfig<>(this, defKey);
        General general = config.main().general();

        ConfigKey<Database> databaseConfig = ConfigKey.of("Database Config", Path.of("database.yml"), Database.class, Database::new);
        Database database = config.secondary(databaseConfig);
        Wrapper<Database> databaseWrapped = config.secondaryWrapped(databaseConfig);
        
        config.reload(databaseConfig);
        database.host(); // outdated value
        databaseWrapped.config().host(); // correct value
    }
}