Domain

This page explains common terms used in smartics-properties

Property

A property is a name value pair. The name is always a string, while the value may by of any type that supports conversion to and from a string representation.

Note: A property name is sometimes called property key. The two terms are synonyms.

@startditaa diagrams/tutorial/property.png +--------------+ | property | +------+-------+ | name | value | +------+-------+ @endditaa
A property has a name and a value.

Property Set

A property set is a collection of properties with a common prefix. A property set allows to arrange property set in groups to refer to.

For instance, in an application there is a property set to configure the mail server and a set to configure display options.

mail.server.address
mail.server.timeout
mail.server.user
mail.server.password
...

display.tableRows
display.fontSize
display.fontColor
display.backgroundColor
...
@startditaa diagrams/tutorial/property-set.png +------------------+ | | | property set | | | | +------------+ | | | property | | | +------------+ | | | | +------------+ | | | property | | | +------------+ | | | | +------------+ | | | property | | | +------------+ | | | | {d}| +------------------+ @endditaa
A property set is a collection of properties.

Declaring a Property

A property is always associated with a property set. To add a property you have to declare it by declaring a property set and providing a property name.

A property set is declared by creating a Java interface with a PropertySet annotation:

@PropertySet("mail.server")
public interface MailServer {
}
@PropertySet("display")
public interface Display {
}

Declaring properties is adding methods to an interface (aka property set - see below for some explanations).

@PropertySet("mail.server")
public interface MailServerProperties {
  String address();
  int timeout();
  String user();
  String password();
}
@PropertySet("display")
public interface DisplayProperties {
  int tableRows();
  int fontSize();
  String fontColor();
  String backgroundColor();
}

The declaration does not only specify the name of the property (the name of the method) and the type of the property (the return value of the method), but may also add further constraints. Here are some examples:

Use bean validation constraints to specify that a font size is required to be larger than zero:

@PropertySet("display")
public interface DisplayProperties {
  ...
  @Min(1)
  int fontSize();
  ...
}

To encrypt the password property, add the PropertyValueSecured annotation:

@PropertySet("mail.server")
public interface MailServerProperties {
  ...
  @PropertyValueSecured
  String password();
}

Please note that you have to configure the security module to get real encryption. Otherwise the system simply encodes the value with a default encoding (e.g. Base64).

To learn more about annotations, please refer to the annotations tutorial.

Another note: Strictly speaking a property set is a virtual collection of properties. Usually every Java interface is annotated with one @PropertySet annotation. But this is not necessarily so. A method within an interface may have its individual property set annotation. But we do not bother with this distinction in this tutorial and assume that an interface has exactly on property set annotation and the property set annotations of different interfaces are also different. This is a best practice to avoid headaches.

Defining a Property

Properties are defined by attaching a value to a name. The attached value is always a string. By accessing the value smartics-properties ensures to return an instance of the property's type by

  1. resolving place holders in the string
  2. converting the string to its property type
  3. validating that the property value meets its constraints

the property string value.

Please note that in the case of encryption, the encrypted value is decrypted prior to resolving.

Accessing a Property

If you want to access a property you usually inject a property set into a CDI managed bean.

@PropertySet("mail.server")
public class MailService {
  ...
  @Inject
  private MailServerProperties mailServerProperties;

  public void send(final Message message) throws MailException {
    final MailServer server = new MailServer(mailServerProperties);
    server.send(message);
  }
}

public class MailServer {
  private final String address;
  ...

  public MailServer(final MailServerProperties properties) {
    this.address = properties.address();
    ...
  }
}

This is just an example to show that the injection point and the point of accessing the property may not be in the same class. In a future version we will support injecting property values instead of property sets. Meanwhile you may either use the way shown in the example above or use a @PostConstruct method.

Configuration

While a property set is environment-agnostic, that is the procedure of accessing a property value is independent of the environment the code is executed, the configuration is environment-specific. A configuration is a collection of all available property sets in a runtime environment. It may provide property definitions for any or none of the properties.

A configuration is selected by a unique configuration key.

@startditaa diagrams/tutorial/configuration.png +-------------------+ | configuration | | | | +---------------+ | | | property set | | | +---------------+ | | | | +---------------+ | | | property set | | | +---------------+ | | | | +---------------+ | | | property set | | | +---------------+ | | {d}| +-------------------+ @endditaa
A configuration provides values for properties.

Configuration Key

A configuration key uniquely identifies a configuration. The key is implementation dependent and transparent to the user of the key.

smartics-properties provides implementations for configuration keys. One implementation has the format

{user}/{tenant}/{env}:{node}/{app-group}:{app-id}:{version} 

The user and tenant part is a dynamic part of the key. It is called dynamic since the selection of the configuration depends on runtime information: the user logged in and using a service and the tenant who is providing the service. The static part is independent on runtime information. A system is deployed in an environment on a given node and identifies itself by its coordinates: group, id, and version.

Here are some examples with static keys (i.e. keys that have only static parts):

sample key description
//test:/:: The configuration provides information for all nodes and all applications, if no specific information is provided by other configurations.
//test:/com.mycorp:mailapp: The configuration provides information the mail application (independent of its version) on any node in the test environment.
//:/:: The default configuration to provide information if no specific information is provided by other configurations.

Here are some examples with dynamic keys (i.e. keys that have at least on dynamic part):

sample key description
jane//:/:: This key selects the configuration for a specific user.
/super-corporation/:/:: This key selects the configuration for a specific tenant.

It depends on the implementation of the configuration key which configuration keys are more specific than others. Please consult the documentation for the selected key.

But how is the selection process? The application runs with a given fully qualified key, e.g.

jane/super-corporation/test:mail/com.mycorp:best-mail:2.2

Explanation: User jane is running super.corporation's version of the com.mycorp:best-mail application in version 2.2 that is deployed in the test environment on node mail.

For this example we assume that the following configurations have been defined and are processed in the given order:

  1. jane/super-corporation/test:mail/com.mycorp:best-mail:2.2
  2. jane//:/::
  3. /super-corporation/:/::
  4. //test:mail/::
  5. //test:/::
  6. //:/com.mycorp:best-mail:2.2
  7. //:/com.mycorp:best-mail:
  8. //:/com.mycorp::
  9. //:/::

The system returns the first value that matches one of the given keys, starting with the first. If no key matches, the value is not defined. Unless there are specific constraints on the value, the null value is returned in this case.

Property Store

A property store allows to add, remove and access property definitions. Any implementation of a property store has to provide a property value by specifying a configuration key and a property name.

A property name alone is not enough to uniquely identify a property in a property store, due to dynamic keys. Implementations that simply provides key/value storage have to either provide a physical store for each configuration of create a compound key of configuration key and property name.

@startditaa diagrams/tutorial/property-store.png +--------------------+ | property store | | | | +----------------+ | | | configuration | | | +----------------+ | | | | +----------------+ | | | configuration | | | +----------------+ | | | | +----------------+ | | | configuration | | | +----------------+ | | | | ... | | {s}| +--------------------+ @endditaa
A property store contains properties for different configurations.