Custom Value List Providers

This improvement is available since Bloomreach Experience Manager 16.9.2.
This improvement is available since Bloomreach Experience Manager 17.1.0.

By default the Selections plugin reads its options from Value List documents in the repository. A custom value list provider replaces that source with your own code, for example a REST service, a database, or a computed list.

Available since Bloomreach Experience Manager 16.9.2 and 17.1.0: custom providers are resolved
through a single registry that is shared by the legacy CMS document editor and the Experience
Manager (visual editor), so one registration serves both editors.

Choose an interface

Interface Use for Notes
org.onehippo.forge.selection.frontend.provider.ValueListProvider New code (recommended) Wicket-free. Receives a JCR Session as a method argument.
org.onehippo.forge.selection.frontend.provider.IValueListProvider Existing code Extends Wicket's IClusterable, so it drags Wicket onto the classpath. Kept for backward compatibility.

A minimal provider:

package com.example;

import java.util.Locale;
import javax.jcr.Session;

import org.onehippo.forge.selection.frontend.model.ListItem;
import org.onehippo.forge.selection.frontend.model.ValueList;
import org.onehippo.forge.selection.frontend.provider.ValueListProvider;

public class CountryValueListProvider implements ValueListProvider {

    @Override
    public ValueList getValueList(final String name, final Locale locale, final Session session) {
        final ValueList list = new ValueList();
        list.add(new ListItem("nl", "Netherlands"));
        list.add(new ListItem("be", "Belgium"));
        return list;
    }
}

Requirements:

  • a public no-argument constructor — the provider is instantiated reflectively;
  • no Wicket API calls and no UserSession.get() — the provider is invoked outside a Wicket
    request cycle. Use the session argument for repository access;
  • the name argument is the field's source property, passed through unchanged. It may be
    empty when the field has no source;
  • never return null; return an empty ValueList instead.

Results of custom providers are not cached by the platform. A provider backed by a slow external system is responsible for its own caching and invalidation.

Optional configuration

Implement configure(Node) to read configuration from the provider's own JCR node. It is called once, after instantiation and before registration.

@Override
public void configure(final Node providerNode) throws RepositoryException {
    if (providerNode.hasProperty("endpoint")) {
        this.endpoint = providerNode.getProperty("endpoint").getString();
    }
}

Register the provider

Add a child node under the value list service's module configuration. The providers node ships with the platform; only the child node is project configuration.

definitions:
  config:
    /hippo:configuration/hippo:modules/valuelist-service/hippo:moduleconfig/providers/service.valuelist.countries:
      jcr:primaryType: hipposys:moduleconfig
      class: com.example.CountryValueListProvider
      endpoint: https://example.com/api/countries   # optional, read via configure(Node)
  • The node name is the provider's service name (service.valuelist.countries above). Use it as the value of the field's valuelistProvider property.
  • class is required and must implement ValueListProvider (or IValueListProvider).
  • service.valuelist.default is reserved for the built-in Value List document provider and cannot be used as a custom name.
  • Adding, changing, or removing a provider node takes effect without a repository restart. Deploying a changed provider class requires a redeploy.

Use the provider on a selection field

In the Document Type Editor, set the valuelistProvider property of a Dynamic Dropdown, Radio Group, or multi-select field to the provider's service name. In configuration that is the field's cluster.options node:

/hippo:namespaces/myproject/newsdocument/editor:templates/_default_/country:
  jcr:primaryType: frontend:plugin
  # ... field wiring omitted ...
  /cluster.options:
    jcr:primaryType: frontend:pluginconfig
    valuelistProvider: service.valuelist.countries

source is optional when a custom provider is configured; when present it is passed to the provider as the name argument.

The dotted key valuelist.provider is a deprecated alias of valuelistProvider. It is still honoured (the canonical key wins when both are present) and logs a deprecation warning. Rename it to valuelistProvider.

Legacy registration under cms-services

Providers registered the classic way keep working:

/hippo:configuration/hippo:frontend/cms/cms-services/countryValueListProvider:
  jcr:primaryType: frontend:plugin
  plugin.class: com.example.CountryValueListProvider
  valuelist.provider: service.valuelist.countries

Here valuelist.provider is the service name the provider registers under, not the deprecated field property of the same name. Entries are picked up automatically, provided the class can be instantiated through a public no-argument constructor. If the same service name exists in both locations, the moduleconfig/providers entry wins.

A provider that extends org.hippoecm.frontend.plugin.Plugin (including subclasses of DocumentValueListProvider) cannot have a no-argument constructor, so it cannot be instantiated this way. Such providers keep working in the legacy CMS document editor; the selection field plugins fall back to the Wicket plugin context (restored in 16.9.4 and 17.2.0), but they are invisible to the Experience Manager, where the field renders an empty list. To support both editors, migrate them as shown below.

Editor support

Registration Provider shape Legacy CMS document editor Experience Manager
moduleconfig/providers POJO (ValueListProvider / IValueListProvider) Yes Yes
cms-services POJO with public no-arg constructor Yes Yes
cms-services extends Plugin (legacy) Yes No — empty list
none (default) built-in Value List document Yes Yes

Upgrading an existing provider

Before — a Wicket Plugin provider, working in the legacy editor only:

public class CountryValueListProvider extends Plugin implements IValueListProvider {

    public CountryValueListProvider(final IPluginContext context, final IPluginConfig config) {
        super(context, config);
        context.registerService(this, config.getString(IValueListProvider.SERVICE));
    }

    @Override
    public ValueList getValueList(final String name, final Locale locale) {
        final Session session = UserSession.get().getJcrSession();
        return buildList(session);
    }

    @Override
    public List<String> getValueListNames() {
        return Collections.singletonList("countries");
    }
}

After — a POJO provider, working in both editors:

public class CountryValueListProvider implements ValueListProvider {

    @Override
    public ValueList getValueList(final String name, final Locale locale, final Session session) {
        return buildList(session);
    }
}

Three mechanical changes:

  1. drop extends Plugin and the (IPluginContext, IPluginConfig) constructor. No explicit service registration is needed, the configuration node is the registration;
  2. take the JCR session from the method argument instead of UserSession.get();
  3. move plugin-config reads into configure(Node).

getValueListNames() is not part of ValueListProvider and can be removed.

Bootstrap — replace the cms-services node with a providers node:

# remove
/hippo:configuration/hippo:frontend/cms/cms-services/countryValueListProvider:
  jcr:primaryType: frontend:plugin
  plugin.class: com.example.CountryValueListProvider
  valuelist.provider: service.valuelist.countries

# add
/hippo:configuration/hippo:modules/valuelist-service/hippo:moduleconfig/providers/service.valuelist.countries:
  jcr:primaryType: hipposys:moduleconfig
  class: com.example.CountryValueListProvider

The service name stays the same, so no document type configuration has to change. Rename any valuelist.provider field property to valuelistProvider while you are there.

Troubleshooting

Log message Cause
Provider '<name>' class '<fqcn>' is not Experience Manager compatible No public no-argument constructor (typically still extends Plugin), or the class failed to load. The provider is skipped.
Provider '<name>' class '<fqcn>' does not implement ValueListProvider Wrong class in class / plugin.class.
Skipping provider '<name>': missing 'class' property A providers child node without a class property.
No ValueListProvider registered under name '<name>'; returning empty list The field references a service name that is not registered — check for a typo, or the provider was skipped at startup for one of the reasons above.
Plugin config uses deprecated key 'valuelist.provider'; rename it to 'valuelistProvider'. Field still uses the deprecated dotted key.

An empty dropdown in the Experience Manager while the legacy editor works correctly almost always means the provider is a Plugin subclass registered under cms-services. See Upgrading an existing provider above.

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?