Skip to the content.

Configuration data/properties in FactoryFX

Introduction

In FactoryFX configuration data are treated like dependencies. If a liveobject needs configuration data (for example jdbc url for a database) the data is stored in the corresponding Factory. The basic dependencies injection principle: “Don’t Look For Things” also applies to data. So you don’t ask a property storage for the data instead they are passed directly from the factory.

Example

The example consist of a jetty server with a jersey resource. The jersey resource creates a connection to a database. For the database connection the resource needs a jdbc url and user/password.

public class DatabaseResource {
    private final String url;
    private final String user;
    private final String password;
    public DatabaseResource(String url, String user, String password) {
        this.url = url;
        this.user = user;
        this.password = password;
    }
    @GET
    public String get() {
        //connect with url,user and password and do something with database
        return "";
    }
}

public class DatabaseResourceFactory  extends SimpleFactoryBase<DatabaseResource,Void, RootFactory> {
    public final StringAttribute url = new StringAttribute();
    public final StringAttribute user = new StringAttribute();
    public final StringAttribute password = new StringAttribute();
    @Override
    protected DatabaseResource createImpl() {
        return new DatabaseResource(url.get(),user.get(),password.get());
    }
}

The configuration data are part of the DatabaseResourceFactory and are passed directly to the DatabaseResource.

Initial Configuration

public class Main {
    public static void main(String[] args) {
        FactoryTreeBuilder<RootFactory> builder = new FactoryTreeBuilder<>(RootFactory.class);
        builder.addFactory(RootFactory.class, Scope.SINGLETON, ctx-> new JettyServerBuilder<>(new RootFactory())
                .withHost("localhost").withPort(8005)
                .withResource(ctx.get(DatabaseResourceFactory.class)).build());

        builder.addSingleton(DatabaseResourceFactory.class, ctx->{
            DatabaseResourceFactory databaseResource = new DatabaseResourceFactory();
            databaseResource.url.set("jdbc:postgresql://host/database");
            databaseResource.user.set("user");
            databaseResource.password.set("123");
            return databaseResource;
        });

        Microservice<Void, Server,RootFactory,Void> microservice = builder.microservice().withFilesystemStorage(Paths.get("./")).build();
        microservice.start();
    }
}

The initial configuration is defined with the FactoryTreeBuilder. To store the configuration we use the use the filesystem storage. That means the configuration is stored in the file: ‘currentFactory.json’. If no configuration is present the initial configuration is created by means of the FactoryTreeBuilder provided. Every consequtive start-up is based on the configuration stored in the file.

Please note that the creating of the initial configuration only happens upon the very first application start. Afterwards the configuration changes are carried out with the microservice api which stores the changes persistingly.

Comparison with property files

For the configuration no additional property files are required. The following example serves as an explanation.

A property file containing the configuration data

resource.database.url = "jdbc:postgresql://host/database"
resource.database.user = user
resource.database.password = 123

Reading and using the properties.

Properties property = new Properties();
prop.load(...);
...
builder.addSingleton(WebResourceFactory.class, ctx->{
    String time = new DateTimeFormatterBuilder().appendPattern("dd.MM.yyyy HH:mm:ss.SSS").toFormatter().format(LocalDateTime.now());
    WebResourceFactory webResourceFactory = new WebResourceFactory();
    webResourceFactory.url.set(property.getProperty("resource.database.url"));
    webResourceFactory.user.set(property.getProperty("resource.database.user"));
    webResourceFactory.password.set(property.getProperty("resource.database.password"));
    return webResourceFactory;
});

Problems

There are several problems with this approach.

Specific problems in this example

General problems with property files

But there are only 3 values that can’t be that important

A good analogy is the broken windows theory.

Consider a building with a few broken windows. If the windows are not repaired, the tendency is for vandals to break a few more windows. Eventually, they may even break into the building, and if it’s unoccupied, perhaps become squatters or light fires inside.

If you already have a property file you will likely add more properties to it. For example why not add a property to configure the database schema, a connection timeout, or even a feature switch? Over time you will add more and more properties and they become harder and harder to manage.

Change the configuration data

From inside the same jvm

DataAndNewMetadata<RootFactory> update = microservice.prepareNewFactory();
update.root.getResource(DatabaseResourceFactory.class).url.set("jdbc:postgresql://host/databasenew");
microservice.updateCurrentFactory(update,"user","comment",(p)->true);

Rest

To change the configuration from outside there is a REST interface

Add REST interface to the server:

builder.addFactory(RootFactory.class, Scope.SINGLETON, ctx-> new JettyServerBuilder<>(new RootFactory())
        .withHost("localhost").withPort(8005)
        .withResource(ctx.get(SpecificMicroserviceResource.class))
        .withResource(ctx.get(DatabaseResourceFactory.class)).build());
builder.addSingleton(SpecificMicroserviceResource.class);

Using the REST interface with the MicroserviceRestClient.

MicroserviceRestClient<Void, RootFactory, Void> microserviceRestClient = MicroserviceRestClientBuilder.build("localhost", 8005, "", "", RootFactory.class);
DataAndNewMetadata<RootFactory> update = microserviceRestClient.prepareNewFactory();
update.root.getResource(DatabaseResourceFactory.class).url.set("jdbc:postgresql://host/databasenew");
microservice.updateCurrentFactory(update, "user", "comment", (p) -> true);

Complete code

code

Conclusion

Fixing the configuration data problems with the coherent approach implementd in FactoryFX is one of its major advantages. Thus it makes no sense to combine FactoryFX and property files because FactoryFX is a superior replacement for property files and duplicated configuration data are harmful.