@Param

Bind constructor parameters to configuration properties

Purpose: Binds a constructor parameter to a configuration property.

Pixie will automatically inject values from a properties file or the builder API.

Example

public final class User {
    private final String username;
    private final int age;

    public User(@Param("username") final String username,
                @Param("age") final int age) {
        this.username = username;
        this.age = age;
    }
}

Mapping to Properties

The @Param value is the property key suffix. In the properties file, each parameter is set as componentName.paramName = value:

user = new://org.example.User
user.username = alice
user.age = 30

Here user.username maps to @Param("username") and user.age maps to @Param("age").

Case Insensitivity

Property keys and @Param names are matched case insensitively. All of the following are equivalent:

user.username = alice
user.Username = alice
user.USERNAME = alice

This applies to both the component name prefix and the param name suffix — User.UserName, user.username, and USER.USERNAME all resolve the same way.

Type Conversion

All @Param values originate as strings — from properties files or the Builder API. Pixie uses the Converter from tomitribe-util to convert these strings into Java types. This is the same conversion system used by Crest for CLI argument parsing.

Conversion Chain

The converter tries the following strategies in order, using the first one that succeeds:

  1. Registered PropertyEditor — if a java.beans.PropertyEditor is registered for the type
  2. EnumEnum.valueOf() with case-insensitive fallback (exact match, then uppercase, then lowercase)
  3. Constructor(String) — any public constructor taking a single String parameter
  4. Constructor(CharSequence) — any public constructor taking CharSequence
  5. Public static factory method — any public static method taking String and returning the target type. The method name does not matter — valueOf, of, parse, from, or any other name will work.

Built-in Types

The following types work out of the box with no additional configuration:

CategoryTypes
Primitives & wrappersbyte, short, int, long, float, double, boolean, char and their boxed equivalents
StringsString, CharSequence
EnumsAny enum type (case-insensitive matching)
Files & pathsjava.io.File
Networkjava.net.URI, java.net.URL
Timejava.util.concurrent.TimeUnit
tomitribe-utilorg.tomitribe.util.Duration (e.g., "30 seconds", "5m"), org.tomitribe.util.Size (e.g., "10mb", "2.5 gb")

Example with Multiple Types

public class DataTypes {

    public DataTypes(@Param("aString") final String aString,
                     @Param("abyte") final byte abyte,
                     @Param("aint") final int aint,
                     @Param("along") final long along,
                     @Param("afloat") final float afloat,
                     @Param("adouble") final double adouble,
                     @Param("aboolean") final boolean aboolean,
                     @Param("achar") final char achar,
                     @Param("aTimeUnit") final TimeUnit aTimeUnit,
                     @Param("aURI") final URI aURI,
                     @Param("aFile") final File aFile) {
        // ...
    }
}
joe = new://org.example.DataTypes
joe.aString = Hello
joe.abyte = 123
joe.aint = 1234567890
joe.along = 1234567890123456789
joe.afloat = 1.234
joe.adouble = 0.1234567890
joe.aboolean = true
joe.achar = z
joe.aTimeUnit = SECONDS
joe.aURI = https://example.com
joe.aFile = /tmp/data.txt

Enums

Enums are matched case-insensitively. All of the following are equivalent:

server.state = RUNNING
server.state = running
server.state = Running

Duration and Size

Pixie includes tomitribe-util which provides human-readable Duration and Size types:

public class CacheConfig {
    public CacheConfig(@Param("ttl") final Duration ttl,
                       @Param("maxSize") final Size maxSize) {
        // ...
    }
}
cache = new://org.example.CacheConfig
cache.ttl = 30 seconds
cache.maxSize = 64 mb

Duration accepts formats like 10s, 30 seconds, 1 day and 5 hours, 500ms.

Size accepts formats like 10kb, 2.5 mb, 1 gigabyte, 512 bytes.

Custom Types

Any class you write that has a public Constructor(String) automatically works as a @Param type:

public class EmailAddress {
    private final String value;

    public EmailAddress(final String value) {
        if (!value.contains("@")) {
            throw new IllegalArgumentException("Invalid email: " + value);
        }
        this.value = value;
    }

    public String get() { return value; }
}
public class NotificationService {
    public NotificationService(@Param("admin") final EmailAddress admin) {
        // ...
    }
}
notifications = new://org.example.NotificationService
notifications.admin = admin@example.com

Classes with any public static method that takes a String and returns an instance of the class also work automatically. Common conventions include valueOf(String), of(String), parse(String), and from(String), but the method name is not restricted — any name will be discovered.

Custom PropertyEditor

For full control over conversion, register a java.beans.PropertyEditor:

public class HostPortEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(final String text) {
        final String[] parts = text.split(":");
        setValue(new HostPort(parts[0], Integer.parseInt(parts[1])));
    }
}

Register it with Java’s PropertyEditorManager:

PropertyEditorManager.registerEditor(HostPort.class, HostPortEditor.class);

The PropertyEditor strategy has the highest priority in the conversion chain, so it will take precedence over constructors and static factory methods.