Low-level Storage API

Uberfire provides a hierarchical preferences management API.

Introduction

First of all, let's define some concepts:

  • Preference: a unique key (String) and a value (any Object);
  • Scope: It's represented by a PreferenceScope. It is composed by a unique type (String) and a key unique for each type (String). It can also have child scopes, to support a more complex hierarchy;
  • Scope Resolution Strategy: Defines the order in which the scopes will be searched to find a preference, as well the default scope where a preference should be persisted (if none is provided). It's represented by a PreferenceScopeResolutionStrategy;
  • Preference Scoped Value: a preference value (any Object) and its scope. It's represented by a PreferenceScopedValue;

You can check the "How it works" session (below) to see some usage examples of these concepts.

Hierarchy

The preferences module hierarchy is defined by Scopes. A preference can be defined in one or multiple scopes. Every time you have to define a preference, you must specify the scope which it will belong to. When you fetch a preference (or several), you need to provide one of the following:

  • A scope: the preference will be searched only in that scope;
  • A scope resolution strategy: The preference returned will be the one in the first scope of the order that has that preference defined.
  • Nothing: the default scope resolution order will be used. The examples in the "How it works" session (below) show how that works.

Storage

The preferences are stored inside a preferences.git repository. A preference path has the structure <REPOSITORY_ROOT>/<SCOPE_PATH>/<PREFERENCE_KEY>.preferences.

The <SCOPE_PATH> can be defined by:

  • A scope without a child scope. It has a path with the structure: <SCOPE_TYPE>/<SCOPE_KEY>;
  • A scope with a child scope. Then the scope path structure will be: <SCOPE_TYPE>/<SCOPE_KEY>/<CHILD_SCOPE_TYPE>/<CHILD_SCOPE_KEY>, and so on, since the child can have another child.

API

The API is very direct. It provides the following operations:

  • put: Inserts a new preference in an specific scope;
  • putIfAbsent: Inserts a new preference in an specific scope, but only if it is not defined there;
  • get: Returns a preference;
  • getScoped: Returns a preference scoped value;
  • search: Searches for preferences by its keys;
  • searchScoped: Searches for preferences scoped values by its keys;
  • all: Returns all preferences defined in all scopes (of that context);
  • allScoped: Returns all preferences scoped values defined in all scopes (of that context);
  • remove: Removes preferences;

You can find more details about these methods and their parameters in the PreferenceStore class javadoc.

How it works

"Global" manner

This is a simple example on how to write and read a preference, using this module just as a global map of properties.

Server-side

import org.uberfire.ext.preferences.shared.PreferenceStore;

@Inject
private PreferenceStore preferenceStore;

public void writingAndReadingAPreference() {
    // Defines a preference.
    preferenceStore.put( "my.preference.key", "my-value" );

    // Reads a preference, if defined.
    // If not, the value returned will be null.
    String value = preferenceStore.get( "my.preference.key" );
}

Client-side

import org.uberfire.ext.preferences.client.ioc.store.PreferenceStore;

@Inject
private PreferenceStore preferenceStore;

public void writingAndReadingAPreference() {
    // Defines a preference.
    preferenceStore.put( "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Reads a preference, if defined.
    // If not, the value returned will be null.
    preferenceStore.get( "my.preference.key", preference -> {
        System.out.println( "Preference read: " + preference );
    } );
}

"Per user" manner

This is a simple example on how to write and read a preference, optionally grouping its values by user. This way, when a preference is persisted, it can be only used by the logged user that persisted it.

Server-side

import org.uberfire.ext.preferences.shared.PreferenceStore;

@Inject
private PreferenceStore preferenceStore;

public void writingAndReadingAUserPreference() {
    // Gets the scope resolver
    final PreferenceScopeResolver preferenceScopeResolver =
            preferenceStore.getDefaultScopeResolver();

    // Gets the user scope
    PreferenceScope userScope =
            preferenceScopeResolver.resolve( DefaultScopes.USER.type() );

    // Gets the all-users scope
    PreferenceScope allUsersScope =
            preferenceScopeResolver.resolve( DefaultScopes.ALL_USERS.type() );

    // Defines a preference just for the logged user.
    preferenceStore.put( userScope, "my.preference.key", "my-value" );

    // Defines a preference for all users.
    preferenceStore.put( allUsersScope, "my.preference.key", "my-value" );
    // or
    preferenceStore.put( "my.preference.key", "my-value" );

    // Reads a preference for the logged user, if defined.
    // If not, reads a preference for all users, if defined.
    // If not, the value returned will be null.
    String value = preferenceStore.get( "my.preference.key" );
}

Client-side

import org.uberfire.ext.preferences.client.ioc.store.PreferenceStore;

@Inject
private PreferenceStore preferenceStore;

public void writingAndReadingAPreference() {
    // Gets the scope resolver
    final PreferenceScopeResolver preferenceScopeResolver =
            preferenceStore.getDefaultScopeResolver();

    // Gets the user scope
    PreferenceScope userScope =
            preferenceScopeResolver.resolve( DefaultScopes.USER.type() );

    // Gets the all-users scope
    PreferenceScope allUsersScope =
            preferenceScopeResolver.resolve( DefaultScopes.ALL_USERS.type() );

    // Defines a preference just for the logged user.
    preferenceStore.put( userScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Defines a preference for all users.
    preferenceStore.put( allUsersScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );
    // or
    preferenceStore.put( "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Reads a preference for the logged user, if defined.
    // If not, reads a preference for all users, if defined.
    // If not, the value returned will be null.
    preferenceStore.get( "my.preference.key", preference -> {
        System.out.println( "Preference read: " + preference );
    } );
}

"Per component" manner

This is a simple example on how to write and read a preference, grouping its values by component. A component is represented here by a key (String).

In order to use this feature, you must qualify your PreferenceStore bean with @ComponentKey( <YOUR_COMPONENT_IDENTIFIER> )

Server-side

import org.uberfire.ext.preferences.shared.PreferenceStore;
import org.uberfire.ext.preferences.backend.annotations.ComponentKey;

@Inject
@ComponentKey( "my-component" )
private PreferenceStore preferenceStore;

public void writingAndReadingAComponentPreference() {
    // Gets the scope resolver
    final PreferenceScopeResolver preferenceScopeResolver =
            preferenceStore.getDefaultScopeResolver();

    // Gets the component scope
    PreferenceScope componentScope =
            preferenceScopeResolver.resolve( DefaultScopes.COMPONENT.type() );

    // Gets the entire-application scope
    PreferenceScope entireApplicationScope =
            preferenceScopeResolver.resolve( DefaultScopes.ENTIRE_APPLICATION.type() );

    // Defines a preference just for my-component.
    preferenceStore.put( componentScope, "my.preference.key", "my-value" );

    // Defines a preference for the entire application.
    preferenceStore.put( entireApplicationScope, "my.preference.key", "my-value" );
    // or
    preferenceStore.put( "my.preference.key", "my-value" );

    // Reads a preference of my-component, if defined.
    // If not, reads a preference for the entire application, if defined.
    // If not, the value returned will be null.
    String value = preferenceStore.get( "my.preference.key" );
}

Client-side

import org.uberfire.ext.preferences.client.ioc.store.PreferenceStore;
import org.uberfire.ext.preferences.client.ioc.annotations.ComponentKey;

@Inject
@ComponentKey( "my-component" )
private PreferenceStore preferenceStore;

public void writingAndReadingAPreference() {
    // Gets the scope resolver
    final PreferenceScopeResolver preferenceScopeResolver =
            preferenceStore.getDefaultScopeResolver();

    // Gets the component scope
    PreferenceScope componentScope =
            preferenceScopeResolver.resolve( DefaultScopes.COMPONENT.type() );

    // Gets the entire-application scope
    PreferenceScope entireApplicationScope =
            preferenceScopeResolver.resolve( DefaultScopes.ENTIRE_APPLICATION.type() );

    // Defines a preference just for my-component.
    preferenceStore.put( componentScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Defines a preference for the entire application.
    preferenceStore.put( entireApplicationScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );
    // or
    preferenceStore.put( "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Reads a preference of my-component, if defined.
    // If not, reads a preference for the entire application, if defined.
    // If not, the value returned will be null.
    preferenceStore.get( "my.preference.key", preference -> {
        System.out.println( "Preference read: " + preference );
    } );
}

"Per component and user" manner

This is a simple example on how to write and read a preference, grouping its values by component and user. A component is represented here by a key (String).

In order to use this feature, you must qualify your PreferenceStore bean with @ComponentKey( <YOUR_COMPONENT_IDENTIFIER> )

Server-side

import org.uberfire.ext.preferences.shared.PreferenceStore;
import org.uberfire.ext.preferences.backend.annotations.ComponentKey;

@Inject
@ComponentKey( "my-component" )
private PreferenceStore preferenceStore;

public void writingAndReadingAComponentPreference() {
    // Gets the scope resolver
    final PreferenceScopeResolver preferenceScopeResolver =
            preferenceStore.getDefaultScopeResolver();

    // Gets the user-component scope
    PreferenceScope userComponentScope =
            preferenceScopeResolver.resolve( DefaultScopes.USER.type(),
                                             DefaultScopes.COMPONENT.type() );

    // Gets the user-entire-application scope
    PreferenceScope userEntireApplication =
            preferenceScopeResolver.resolve( DefaultScopes.USER.type(),
                                             DefaultScopes.ENTIRE_APPLICATION.type() );

    // Gets the all-users-component scope
    PreferenceScope allUsersComponentScope =
            preferenceScopeResolver.resolve( DefaultScopes.ALL_USERS.type(),
                                             DefaultScopes.COMPONENT.type() );

    // Gets the all-users-entire-application scope
    PreferenceScope allUsersEntireApplicationScope =
            preferenceScopeResolver.resolve( DefaultScopes.ALL_USERS.type(),
                                             DefaultScopes.ENTIRE_APPLICATION.type() );

    // Defines a preference just for my-component and the logged user.
    preferenceStore.put( userComponentScope, "my.preference.key", "my-value" );

    // Defines a preference for the logged user in the entire application.
    preferenceStore.put( userEntireApplication, "my.preference.key", "my-value" );

    // Defines a preference just for my-component and for all users.
    preferenceStore.put( allUsersComponentScope, "my.preference.key", "my-value" );

    // Defines a preference for all users in the entire application.
    preferenceStore.put( allUsersEntireApplicationScope, "my.preference.key", "my-value" );
    // or
    preferenceStore.put( "my.preference.key", "my-value" );

    // Reads a preference for the logged user and my-component, if defined.
    // If not, reads a preference for the logged user in the entire application, if defined.
    // If not, reads a preference for all users and my-component, if defined.
    // If not, reads a preference for all users in the entire application, if defined.
    // If not, the value returned will be null.
    String value = preferenceStore.get( "my.preference.key" );
}

Client-side

import org.uberfire.ext.preferences.client.ioc.store.PreferenceStore;
import org.uberfire.ext.preferences.client.ioc.annotations.ComponentKey;

@Inject
@ComponentKey( "my-component" )
private PreferenceStore preferenceStore;

public void writingAndReadingAPreference() {
    // Gets the scope resolver
    final PreferenceScopeResolver preferenceScopeResolver =
            preferenceStore.getDefaultScopeResolver();

    // Gets the user-component scope
    PreferenceScope userComponentScope =
            preferenceScopeResolver.resolve( DefaultScopes.USER.type(),
                                             DefaultScopes.COMPONENT.type() );

    // Gets the user-entire-application scope
    PreferenceScope userEntireApplication =
            preferenceScopeResolver.resolve( DefaultScopes.USER.type(),
                                             DefaultScopes.ENTIRE_APPLICATION.type() );

    // Gets the all-users-component scope
    PreferenceScope allUsersComponentScope =
            preferenceScopeResolver.resolve( DefaultScopes.ALL_USERS.type(),
                                             DefaultScopes.COMPONENT.type() );

    // Gets the all-users-entire-application scope
    PreferenceScope allUsersEntireApplicationScope =
            preferenceScopeResolver.resolve( DefaultScopes.ALL_USERS.type(),
                                             DefaultScopes.ENTIRE_APPLICATION.type() );

    // Defines a preference just for my-component and the logged user.
    preferenceStore.put( userComponentScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Defines a preference for the logged user in the entire application.
    preferenceStore.put( userEntireApplication, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Defines a preference just for my-component and for all users.
    preferenceStore.put( allUsersComponentScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Defines a preference for all users in the entire application.
    preferenceStore.put( allUsersEntireApplicationScope, "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );
    // or
    preferenceStore.put( "my.preference.key", "my-value", onSuccess -> {
        System.out.println( "Preference stored successfully!" );
    } );

    // Reads a preference for the logged user and my-component, if defined.
    // If not, reads a preference for the logged user in the entire application, if defined.
    // If not, reads a preference for all users and my-component, if defined.
    // If not, reads a preference for all users in the entire application, if defined.
    // If not, the value returned will be null.
    preferenceStore.get( "my.preference.key", preference -> {
        System.out.println( "Preference read: " + preference );
    } );
}

Customizing

If you want to, you can customize the provided scope hierarchy and create your own. You only have to provide new CDI @Default implementations for the interfaces PreferenceScopeTypes and PreferenceScopeResolutionStrategy, and let CDI do its magic.

To make this customization easier, we strongly suggest that you take a look at these interfaces javadoc (and possibly their default implementation).

results matching ""

    No results matching ""