Backend API. Server-side code: this runs in a Codename One backend, not in the app on the device.

public final class Config

  1. Object
  2. Config

Where a server’s settings come from, in one resolution order.

The shape is the one a Spring Boot developer already knows, because the problem is the same one: the SAME code has to run against a SQLite file on a laptop and a managed PostgreSQL in production, and the difference between those two cannot live in the source. It lives here.

# application.properties, committed
cn1.datasource.url=${DATABASE_URL}
cn1.server.port=8080

# application-dev.properties, also committed
cn1.datasource.url=:memory:
CN1_PROFILE=dev ./server                       # SQLite, nothing installed
DATABASE_URL=postgres://user:pw@db/app ./server # production

A key is looked for in this order, and the first layer that has it wins:

  1. a system property of exactly that name (-Dcn1.server.port=9000), which only the local JVM loop can set;

  2. an environment variable of the name in upper case with dots as underscores (CN1_SERVER_PORT), which is how a container sets one;

  3. the environment variable a platform already sets for it, where one exists: PORT and DATABASE_URL are set for you by every PaaS worth the name, and a server that ignored them would need a wrapper script to start at all;

  4. application-<profile>.properties;

  5. application.properties;

  6. the module’s application.properties as the build compiled it into the server, so a binary with no file beside it still has the committed settings, and a file or the environment can still change any of them;

  7. the default the caller passed in.

A test started by @BackendTest adds one layer above all of these: the settings the test itself names. A test that asks for a free port or an in-memory database gets one even in a shell that exports PORT or DATABASE_URL.

A value may reference an environment variable as ${NAME} or ${NAME:fallback}. That resolution happens when the value is READ rather than when the file is loaded, which is what lets a committed application.properties name a variable that only production sets: the dev profile overrides the key, so the unset variable is never looked at. A reference that IS read and cannot be resolved is an error rather than a value with a dollar sign in it – the alternative is a server that tries to open a SQLite file named “${DATABASE_URL}”.

Nothing here is required. A binary with no properties file beside it reads its whole configuration from the environment, which is the normal shape for a container built FROM SCRATCH: there is no file next to the binary because there is nothing next to the binary.

Fields

public static final String PROFILE = "cn1.profile"Which profile is active.
public static final String LOCATION = "cn1.config.location"The directory the properties files are read from.
public static final String SERVER_PORT = "cn1.server.port"The port to listen on.
public static final String SERVER_ADDRESS = "cn1.server.address"The address to listen on: 127.0.0.1 for this machine only.
public static final String SERVER_BACKLOG = "cn1.server.backlog"The listen backlog.
public static final String SERVER_WORKERS = "cn1.server.workers"The size of the request thread pool.
public static final String SERVER_SHUTDOWN_MILLIS = "cn1.server.shutdownTimeoutMillis"How long a stop waits for requests in flight, in milliseconds.
public static final String TLS_CERTIFICATE = "cn1.server.tls.certificate"A PEM certificate chain to terminate TLS with.
public static final String TLS_KEY = "cn1.server.tls.key"The private key for TLS_CERTIFICATE.
public static final String TLS_HTTP2 = "cn1.server.tls.http2"Whether to offer HTTP/2 through ALPN when TLS is terminated here.
public static final String STATIC_ROOT = "cn1.static.root"A directory to serve static files from.
public static final String STATIC_PREFIX = "cn1.static.prefix"The path prefix those files are served under.
public static final String STATIC_INDEX = "cn1.static.index"The file a directory request is answered with.
public static final String STATIC_CACHE_CONTROL = "cn1.static.cacheControl"The Cache-Control header those files carry.
public static final String WEBAPP_ROOT = "cn1.webapp.root"The directory holding the app’s web build, which the server answers with at WEBAPP_PATH after every route and every static file.
public static final String WEBAPP_PATH = "cn1.webapp.path"The path the web build is served under.
public static final String WEBAPP_CACHE_CONTROL = "cn1.webapp.cacheControl"The Cache-Control header the web build’s files carry.
public static final String WEBAPP_ENABLED = "cn1.webapp.enabled"Set to false to keep the server from looking for a web build at all.
public static final String DATASOURCE_URL = "cn1.datasource.url"The database, as a SQLite path or a PostgreSQL or MySQL URL.
public static final String DATASOURCE_POOL_SIZE = "cn1.datasource.pool.size"How many connections the pool holds.
public static final String DATASOURCE_BORROW_MILLIS = "cn1.datasource.pool.borrowTimeoutMillis"How long a borrow waits for a free connection, in milliseconds.
public static final String DATASOURCE_BUSY_MILLIS = "cn1.datasource.busyTimeoutMillis"How long SQLite waits on a locked database, in milliseconds.
public static final String ORM_CREATE_TABLES = "cn1.orm.createTables"Whether the generated daos create their tables at start-up.
public static final String FLYWAY_ENABLED = "cn1.flyway.enabled"Whether pending migrations run at start-up.
public static final String FLYWAY_TABLE = "cn1.flyway.table"The application’s schema history table.
public static final String FLYWAY_BASELINE_ON_MIGRATE = "cn1.flyway.baselineOnMigrate"Whether a database that has tables and no history is adopted at the baseline version instead of refused.
public static final String FLYWAY_BASELINE_VERSION = "cn1.flyway.baselineVersion"The version an adopted database is taken to be at.
public static final String FLYWAY_BASELINE_DESCRIPTION = "cn1.flyway.baselineDescription"The description written on the baseline row.
public static final String FLYWAY_VALIDATE_ON_MIGRATE = "cn1.flyway.validateOnMigrate"Whether applied migrations are checked against the scripts before migrating.
public static final String FLYWAY_OUT_OF_ORDER = "cn1.flyway.outOfOrder"Whether a migration older than the newest applied one is applied instead of refused.
public static final String FLYWAY_CLEAN_DISABLED = "cn1.flyway.cleanDisabled"Whether dropping the whole schema through Migrator.clean() is refused.
public static final String FLYWAY_TARGET = "cn1.flyway.target"The highest version to migrate to; latest, the default, means all of them.
public static final String FLYWAY_IGNORE_FUTURE_MIGRATIONS = "cn1.flyway.ignoreFutureMigrations"Whether a schema migrated by a newer build is tolerated.
public static final String FLYWAY_INSTALLED_BY = "cn1.flyway.installedBy"The name recorded as having applied each migration.
public static final String FLYWAY_LOCK_RETRY_COUNT = "cn1.flyway.lockRetryCount"How many one-second attempts to wait for another process that is migrating.

Methods

public static Config load() throws IOExceptionReads the configuration for this process: the active profile, then the two properties files, from LOCATION or the working directory.
public static Config load(String directory) throws IOExceptionReads the configuration from properties files in directory.
public static Config of(Properties values, String profile)A configuration with no files behind it, holding exactly what it is given.
public String getProfile()The active profile: “default” unless something named another.
public boolean isDevelopmentProfile()Whether the active profile is a development one – dev, development, test or local.
public String get(String key) throws IOExceptionThe value for key, or null when no layer has one.
public String getRoutePath(String key, String fallback) throws IOExceptionA path a handler of the server’s own answers, in the canonical form every request target is compared in: escaped unreserved characters decoded, other escapes upper-cased.
public String getHeaderSecret(String key) throws IOExceptionA secret that clients present in a request header – a bearer token – or null when no layer has one.
public String get(String key, String fallback) throws IOExceptionThe value for key, or fallback when no layer has one.
public int getInt(String key, int fallback) throws IOExceptionThe value for key as a number, or fallback.
public boolean getBoolean(String key, boolean fallback) throws IOExceptionThe value for key as a flag, or fallback.
public String describe()What was read, for a start-up line.
public List keys()Every key the properties files and the compiled-in settings set, sorted, for a development listing.

Inherited methods

Field details

PROFILE

public static final String PROFILE = "cn1.profile"
Which profile is active. Defaults to “default”.

LOCATION

public static final String LOCATION = "cn1.config.location"
The directory the properties files are read from. Defaults to “.”.

SERVER_PORT

public static final String SERVER_PORT = "cn1.server.port"
The port to listen on. Also read from PORT.

SERVER_ADDRESS

public static final String SERVER_ADDRESS = "cn1.server.address"
The address to listen on: 127.0.0.1 for this machine only. Unset, the server listens on every interface.

SERVER_BACKLOG

public static final String SERVER_BACKLOG = "cn1.server.backlog"
The listen backlog.

SERVER_WORKERS

public static final String SERVER_WORKERS = "cn1.server.workers"
The size of the request thread pool.

SERVER_SHUTDOWN_MILLIS

public static final String SERVER_SHUTDOWN_MILLIS = "cn1.server.shutdownTimeoutMillis"
How long a stop waits for requests in flight, in milliseconds.

TLS_CERTIFICATE

public static final String TLS_CERTIFICATE = "cn1.server.tls.certificate"
A PEM certificate chain to terminate TLS with.

TLS_KEY

public static final String TLS_KEY = "cn1.server.tls.key"
The private key for TLS_CERTIFICATE.

TLS_HTTP2

public static final String TLS_HTTP2 = "cn1.server.tls.http2"
Whether to offer HTTP/2 through ALPN when TLS is terminated here.

STATIC_ROOT

public static final String STATIC_ROOT = "cn1.static.root"
A directory to serve static files from.

STATIC_PREFIX

public static final String STATIC_PREFIX = "cn1.static.prefix"
The path prefix those files are served under. Defaults to /static.

STATIC_INDEX

public static final String STATIC_INDEX = "cn1.static.index"
The file a directory request is answered with. Defaults to index.html.

STATIC_CACHE_CONTROL

public static final String STATIC_CACHE_CONTROL = "cn1.static.cacheControl"
The Cache-Control header those files carry.

WEBAPP_ROOT

public static final String WEBAPP_ROOT = "cn1.webapp.root"
The directory holding the app’s web build, which the server answers with at WEBAPP_PATH after every route and every static file. Defaults to webapp in the working directory, and is served only when it holds an index.html; a directory named here that does not is a start-up error.

WEBAPP_PATH

public static final String WEBAPP_PATH = "cn1.webapp.path"
The path the web build is served under. Defaults to /.

WEBAPP_CACHE_CONTROL

public static final String WEBAPP_CACHE_CONTROL = "cn1.webapp.cacheControl"
The Cache-Control header the web build’s files carry. Defaults to no-cache: the files keep their names from one build to the next, so a browser has to ask whether its copy is still the current one.

WEBAPP_ENABLED

public static final String WEBAPP_ENABLED = "cn1.webapp.enabled"
Set to false to keep the server from looking for a web build at all.

DATASOURCE_URL

public static final String DATASOURCE_URL = "cn1.datasource.url"
The database, as a SQLite path or a PostgreSQL or MySQL URL. Also read from DATABASE_URL.

DATASOURCE_POOL_SIZE

public static final String DATASOURCE_POOL_SIZE = "cn1.datasource.pool.size"
How many connections the pool holds.

DATASOURCE_BORROW_MILLIS

public static final String DATASOURCE_BORROW_MILLIS = "cn1.datasource.pool.borrowTimeoutMillis"
How long a borrow waits for a free connection, in milliseconds.

DATASOURCE_BUSY_MILLIS

public static final String DATASOURCE_BUSY_MILLIS = "cn1.datasource.busyTimeoutMillis"
How long SQLite waits on a locked database, in milliseconds.

ORM_CREATE_TABLES

public static final String ORM_CREATE_TABLES = "cn1.orm.createTables"
Whether the generated daos create their tables at start-up. Defaults to true on a development profile and false everywhere else: a laptop wants a schema without being asked, and production wants its migrations run by whatever runs migrations.

FLYWAY_ENABLED

public static final String FLYWAY_ENABLED = "cn1.flyway.enabled"
Whether pending migrations run at start-up. Defaults to true when the build found migrations or a library registered some, which is the only time there is anything to run. See Migrations.

FLYWAY_TABLE

public static final String FLYWAY_TABLE = "cn1.flyway.table"
The application’s schema history table. flyway_schema_history unless set.

FLYWAY_BASELINE_ON_MIGRATE

public static final String FLYWAY_BASELINE_ON_MIGRATE = "cn1.flyway.baselineOnMigrate"
Whether a database that has tables and no history is adopted at the baseline version instead of refused. Off unless set.

FLYWAY_BASELINE_VERSION

public static final String FLYWAY_BASELINE_VERSION = "cn1.flyway.baselineVersion"
The version an adopted database is taken to be at. 1 unless set.

FLYWAY_BASELINE_DESCRIPTION

public static final String FLYWAY_BASELINE_DESCRIPTION = "cn1.flyway.baselineDescription"
The description written on the baseline row.

FLYWAY_VALIDATE_ON_MIGRATE

public static final String FLYWAY_VALIDATE_ON_MIGRATE = "cn1.flyway.validateOnMigrate"
Whether applied migrations are checked against the scripts before migrating. On unless set.

FLYWAY_OUT_OF_ORDER

public static final String FLYWAY_OUT_OF_ORDER = "cn1.flyway.outOfOrder"
Whether a migration older than the newest applied one is applied instead of refused.

FLYWAY_CLEAN_DISABLED

public static final String FLYWAY_CLEAN_DISABLED = "cn1.flyway.cleanDisabled"
Whether dropping the whole schema through Migrator.clean() is refused. On unless set.

FLYWAY_TARGET

public static final String FLYWAY_TARGET = "cn1.flyway.target"
The highest version to migrate to; latest, the default, means all of them.

FLYWAY_IGNORE_FUTURE_MIGRATIONS

public static final String FLYWAY_IGNORE_FUTURE_MIGRATIONS = "cn1.flyway.ignoreFutureMigrations"
Whether a schema migrated by a newer build is tolerated. On unless set, because a rolling deployment runs the old build against the new schema.

FLYWAY_INSTALLED_BY

public static final String FLYWAY_INSTALLED_BY = "cn1.flyway.installedBy"
The name recorded as having applied each migration. The database user unless set.

FLYWAY_LOCK_RETRY_COUNT

public static final String FLYWAY_LOCK_RETRY_COUNT = "cn1.flyway.lockRetryCount"
How many one-second attempts to wait for another process that is migrating. 50 unless set.

Method details

load

public static Config load() throws IOException
Reads the configuration for this process: the active profile, then the two properties files, from LOCATION or the working directory.

load

public static Config load(String directory) throws IOException
Reads the configuration from properties files in directory.

of

public static Config of(Properties values, String profile)
A configuration with no files behind it, holding exactly what it is given. The process environment still wins over it, for the same reason it wins over a file: the deployment has the last word.

getProfile

public String getProfile()
The active profile: “default” unless something named another.

isDevelopmentProfile

public boolean isDevelopmentProfile()

Whether the active profile is a development one – dev, development, test or local.

This decides two defaults and nothing else: an unconfigured database becomes an in-memory SQLite one rather than a refusal, and the ORM creates its tables. Both are wrong in production and right on a laptop, and both are overridable by naming the key.

get

public String get(String key) throws IOException
The value for key, or null when no layer has one.

getRoutePath

public String getRoutePath(String key, String fallback) throws IOException
A path a handler of the server’s own answers, in the canonical form every request target is compared in: escaped unreserved characters decoded, other escapes upper-cased. Compared as configured, /%6dcp was announced and never matched a request, which reaches handlers as /mcp.

getHeaderSecret

public String getHeaderSecret(String key) throws IOException

A secret that clients present in a request header – a bearer token – or null when no layer has one.

Refused when no request could carry it exactly: the request parser rejects a control character in a header value and trims surrounding spaces and tabs, so a token read with a secret file’s trailing newline would answer every request 401 while the server looked healthy. The value stays out of the message; it is a secret.

Throws

IOException
when the value holds a control character, or leading or trailing whitespace

get

public String get(String key, String fallback) throws IOException
The value for key, or fallback when no layer has one.

getInt

public int getInt(String key, int fallback) throws IOException
The value for key as a number, or fallback.

getBoolean

public boolean getBoolean(String key, boolean fallback) throws IOException

The value for key as a flag, or fallback.

“true”, “yes”, “on” and “1” are true; “false”, “no”, “off” and “0” are false; anything else is an error rather than false. A setting the operator spelled “ture” is a setting they believe is on.

describe

public String describe()
What was read, for a start-up line. NEVER any value: the datasource URL holds a password, and a configuration dump is how it reaches a log.

keys

public List keys()
Every key the properties files and the compiled-in settings set, sorted, for a development listing. The environment is not enumerated: it holds far more than this server’s settings, and secrets that are none of its business.