Skip to content

SolarNode Settings API

Matt Magoffin edited this page Sep 18, 2018 · 2 revisions

The SolarNode platform includes a declarative API for exposing configurable settings on services provided by plug-ins. The SettingSpecifierProvider interface is the entry point to this API, which allows a service to expose configurable properties to the SolarNode runtime, which can then generate a web UI that allows a user to edit that property.

For example, here's a screen shot of SolarNode rendering the settings for a weather data source service:

When writing a SolarNode plugin that you want to be configurable in the SolarNode UI, implement the SettingSpecifierProvider interface in your service and expose that service to the OSGi runtime. This API provides a method to return any number of SettingSpecifier objects, each of which represents a property to expose to the UI. Typically these are simple, mutable properties such as the TextFieldSettingSpecifier used for mutable string properties. The SolarNode web UI renders HTML form elements on behalf of the plug-in, and manages persisting changes to them via a SettingsService provided by the platform.

For each property exposed as a setting, your service must provide a standard JavaBean setter method that will accept values changed by a user. For example, a username setting would expect a setUsername() method on the service like this:

// the SettingSpecifierProvider API provides the list of settings to expose
@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(1);

	// expose a "username" setting with a default value of "admin"
	results.add(new BasicTextFieldSettingSpecifier("username", "admin"));

	return results;
}

// settings are updated at runtime via JavaBean setter methods
public void setUsername(String username) {
	this.username = username;
}

Settings playpen

The Settings Playpen SolarNode plugin is a demonstration of most of the available setting types. If you deploy this plugin into a SolarNode runtime you'll see a Settings playpen component that you can configure and see how the settings are implemented.

SettingSpecifier types

The SettingSpecifier interface is extended by other interfaces to provide more specific functionality.

Interface Description
GroupSettingSpecifier Group a set of settings together.
KeyedSettingSpecifier Mutable setting marker for a specific property.
LocationLookupSettingSpecifier Specialized setting for SolarNet location IDs.
MultiValueSettingSpecifier Setting to choose from a list of values.
RadioGroupSettingSpecifier Setting to choose from a list of values, using radio buttons.
SetupResourceSettingSpecifier Provides custom UI elements to SolarNode.
SliderSettingSpecifier A number setting with a fixed minimum/maximum range.
TextFieldSettingSpecifier A simple editable text field based property.
TextFieldSettingSpecifier secure A "secure entry" style text field property.
TitleSettingSpecifier A simple read-only text based property.
ToggleSettingSpecifier An on/off style toggle setting.

Group Setting Specifier

The purpose of this specifier is to group other specifiers into a visually distinct way in the settings UI. The BasicGroupSettingSpecifier class provides a default implementation of this interface.

In the following screen shot, the SolarNode web UI is rendering a group of setting form fields with horizontal rules before/after the group:

This setting is also used for dynamic setting lists.

Keyed Setting Specifier

This is an abstract interface used to mark a setting as being mutable based on a specific property, or key. This interface isn't typically used directly, as it is extended by other interfaces for specific setting types. The BaseKeyedSettingSpecifier class provides a default (abstract) implementation of this interface.

In SolarNode, setting keys generally equate to JavaBean property names, which thus corespond to setter methods following the JavaBean standard. For example, a setting key username would expect a method on the service like this:

public void setUsername(String username) {
	this.username = username;
}

Location Lookup Setting Specifier

The purpose of this specifier is to provide a way to easily find a SolarNetwork location ID to associate with a service. For example, weather data sources need to associate a location ID with the data they collect. The BasicLocationLookupSettingSpecifier class provides a default implementation of this interface.

In the following screen shot, the SolarNode web UI is rendering a location lookup setting, which includes a Change button:

Clicking that Change button brings up a specialized search form where locations can be searched for by their names and other details:

Multi Value Setting Specifier

This specifier is used for making a choice from a list of items. The BasicMultiValueSettingSpecifier class provides a default implementation of this interface. The SolarNode web UI renders this type as a drop-down menu:

The code for that settings looks like this:

private static final String[] DEFAULT_MENU = new String[] { "Option 1", "Option 2", "Option 3" };

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(1);

	BasicMultiValueSettingSpecifier menuSpec = new BasicMultiValueSettingSpecifier("menu",
			DEFAULT_MENU[0]);
	Map<String, String> menuValues = new LinkedHashMap<>(DEFAULT_MENU.length);
	for ( String s : DEFAULT_MENU ) {
		menuValues.put(s, s);
	}
	menuSpec.setValueTitles(menuValues);
	results.add(menuSpec);

	return results;
}

public void setMenu(String menu) {
	this.menu = menu;
}

Radio Group Setting Specifier

This specifier is used for making a choice from a list of items. The BasicRadioGroupSettingSpecifier class provides a default implementation of this interface. The SolarNode web UI renders this type as a group of radio buttons:

The code for that settings looks like this:

private static final String[] DEFAULT_RADIO = new String[] { "One", "Two", "Three" };

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(1);

	BasicRadioGroupSettingSpecifier radioSpec = new BasicRadioGroupSettingSpecifier("radio",
			DEFAULT_RADIO[0]);
	Map<String, String> radioValues = new LinkedHashMap<String, String>(DEFAULT_RADIO.length);
	for ( String s : DEFAULT_RADIO ) {
		radioValues.put(s, s);
	}
	radioSpec.setValueTitles(radioValues);
	results.add(radioSpec);

	return results;
}

public void setRadio(String radio) {
	this.radio = radio;
}

Setup Resource Setting Specifier

This interface defines a way for a setting to provide custom UI elements to SolarNode. The BasicSetupResourceSettingSpecifier class provides a default implementation of this interface. See the Custom SolarNode Settings UI guide for more information.

Slider Setting Specifier

This specifier defines a mutable numeric property that is constrained between minimum and maximum values and a step size. The BasicSliderSettingSpecifier class provides a default implementation of this interface. The SolarNode web UI renders this setting as a draggable slider:

It is configured in code like this:

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(1);

	// expose a number slider between 0 and 10, default value of 5, step size 0.5
	results.add(new BasicSliderSettingSpecifier("slide", 5.0, 0.0, 10.0, 0.5));

	return results;
}

// runtime setter method to accept changes
public void setSlide(Double slide) {
	this.slide = slide;
}

Text Field Setting Specifier

This specifier defines a mutable property that is rendered as an editable text field in the SolarNode UI. The BasicTextFieldSettingSpecifier class provides a default implementation of this interface.

In the following screen shot, all the text fields shown are exposed via BasicTextFieldSettingSpecifier settings:

Non-string properties can be configured via the text field specifier as well, as long as the SolarNode runtime can convert the text field string value into the required type. For example, the last field in the example above allows configuring an Integer property. In cases where SolarNode cannot automatically convert from a string into the required type, then service can expose an additional property that accepts a string value and the setting would expose that property so that the setter method for that property can perform the conversion itself.

For example, a text field setting customerNumber can work with a JavaBean setter method

public void setCustomerNumber(Integer num) {
	this.customerNumber = num;
}

by converting the text field string value into an Integer.

Text Field (Secure) Setting Specifier

This is not actually a specific interface, but rather a configurable property of the TextFieldSettingSpecifier. The BasicTextFieldSettingSpecifier has a constructor that accepts a boolean secureTextEntry argument. When set to true the SolarNode web UI renders the text field as a password text field:

The code for that setting looks like this:

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(1);

	results.add(new BasicTextFieldSettingSpecifier("password", null, true));

	return results;
}

public void setPassword(String password) {
	this.password = password;
}

Title Setting Specifier

This specifier defines an immutable text property that is rendered as non-editable text. The BasicTitleSettingSpecifier class provides a default implementation of this interface. This can be used to display status information to the SolarNode web UI. For example, here is a meter data source that exposes two title settings with information about the meter device and the latest reading from the device:

The code for those settings looks like this:

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(2);

	results.add(new BasicTitleSettingSpecifier("info", getInfoMessage(), true));
	results.add(new BasicTitleSettingSpecifier("sample", getSampleMessage(sample), true));

	return results;
}

Toggle Setting Specifier

This specifier defines a basic on/off type switch setting. The BasicToggleSettingSpecifier class provides a default implementation of this interface.

The code for a toggle setting looks like this:

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
	List<SettingSpecifier> results = new ArrayList<>(1);

	results.add(new BasicToggleSettingSpecifier("toggle", false));

	return results;
}

public void setToggle(Boolean toggle) {
	this.toggle = toggle;
}

Dynamic setting lists

The SolarNode settings framework supports configuring dynamic lists of settings, either as a list of basic properties like a list of strings, or even complex objects like a list of contact details. The GroupSettingSpecifier defines a dynamic property to trigger this behavior. The BasicGroupSettingSpecifier class provides a constructor that can be used to enable this mode.

For dynamic setting lists to work, the SettingSpecifierProvider service must provide a List based JavaBean property to manage the dynamic list. Then, some additional methods to dynamically adjust the number of items in the list are required. All these methods follow a naming convention based on the name of the setting. In the following table, X stands for a setting name:

Method Example Description
getX() getAllowedNames() Getter method for the list instance. Must return a List or array.
getXCount() getAllowedNamesCount() Returns an int value of the size of the list.
setXCount(int) setAllowedNamesCount(int size) Set the size of the list to size. Note it must be able to adjust the list size smaller or larger, and must populate default objects when expanding the list.

Let's look at an example of a simple string list setting. SolarNode will render the list like this:

The name of this setting is listString. The SettingsUtil class has a useful method to help construct the dynamic list group setting:

@Override
public List<SettingSpecifier> getSettingSpecifiers() {
  List<SettingSpecifier> results = new ArrayList<>(1);

  // basic dynamic list of strings
  Collection<String> listStrings = getListString();
  BasicGroupSettingSpecifier listStringGroup = SettingsUtil.dynamicListSettingSpecifier(
      "listString", listStrings, new SettingsUtil.KeyedListCallback<String>() {

        @Override
        public Collection<SettingSpecifier> mapListSettingKey(String value, int index,
            String key) {
          return Collections.singletonList(
              new BasicTextFieldSettingSpecifier(key, ""));
        }
      });
  results.add(listStringGroup);

  return results;
}

// getter for the dynamic list
public List<String> getListString() {
  return listString;
}

// setter for the dynamic list
public void setListString(List<String> listString) {
  this.listString = listString;
}

// getter for the dynamic list size
public int getListStringCount() {
  List<String> l = getListString();
  return (l == null ? 0 : l.size());
}

// setter for the dynamic list size
public void setListStringCount(int count) {
  if ( count < 0 ) {
    count = 0;
  }
  List<String> l = getListString();
  int lCount = (l == null ? 0 : l.size());
  while ( lCount > count ) {
    l.remove(l.size() - 1);
    lCount--;
  }
  while ( lCount < count ) {
    l.add("");
    lCount++;
  }
}
Clone this wiki locally