Categories

Versions

You are viewing the RapidMiner Developers documentation for version 9.10 - Check here for latest version

Converting Legacy Configurators to New Connection Management

Up until now, we had three separate connection types: JDBC connections for databases, Configurables and Configurators for "simple" property based connections like Twitter or any of the cloud connections, and Radoop, which has more extra files and configurations. To streamline this, we moved to the new connection management in 9.3 that can serve all three purposes with the added benefit of being able to use different driver versions for JDBC for each connection.

This is a guide how to move legacy configurator connections to the new connection management, using some helper classes for minimal effort. You can follow along the changes with the provided extension project, which showcases a small feature set of the new connections and its UI. It also provides a git history so you can follow along step by step. We recommend to build your extension against RapidMiner Studio 9.6.0, which has some more convenience methods than the original introduction of the new connection management (9.3.0). This is especially helpful for the UI.

Using the following steps you get a lot of things "for free", including a nice convert button in the old configurator UI, so users can easily move to the new connection management without retyping all configurations. It is still possible that passwords or api keys might break and need to be set in the new connection again. Make sure to test the connection after conversion.

  1. Change the super class of your Configurable from AbstractConfigurable to ConnectionAdapter

  2. Change the super class of your Configurator from AbstractConfigurator to ConnectionAdapterHandler

    1. Add a public default constructor that calls the super constructor with the namespace of your extension (e.g. cloud_connectivity for the Cloud Connectivity extension). Best practice is to have the namespace as a constant in the PluginInit* class. An alternative to the public constructor is to make the class a singleton.
    2. Override the parametersByGroup method if you want to split up your parameters; this must include all keys available through the getParameterTypes method. By default, all parameters are in the group with the key "basic".
    3. Implement the test and validate methods if you need specialty checks; These methods are called when your connection should be saved (validate) or you want to test your connection (test). The test method is exposed through a button in the UI. There is a default implementation provided for both methods. In general the fast validation method only checks on the surface, e.g. if all needed parameters are set. The test method includes more time consuming checks, it uses the injection mechanism and tests the actual connection. There is no need to call the validation inside the testing. If your old configurator provided a test action, you do not need to override this method.
    4. If you had additional actions, you can override getAdditionalActions to make them known in the new system, too. Make sure you actually need those additional actions. (Currently, this does not appear in the UI if you do not implement that yourself!)
  3. Register the Configurator with ConnectionAdapterHandler.registerHandler instead of registering it with the ConfigurationManager (usually done in the PluginInit* class). The registration with ConfigurationManager will be done implicitly, so if it might get removed in a future version, you do not have to take care of that.

  4. Adapt each associated operator and other related classes

    1. The operator needs to implement ConnectionSelectionProvider. The methods of this interface are accessed by the framework itself, so the only thing you need to do is to add a private ConnectionInformationSelector field in your operator class and use it in the two method implementations. If you need something more sophisticated, you can create and set it in the operator constructor yourself or even create your own subclass if necessary. See also the javadoc for getConnectionParameters to get more information.

      If the hands-off approach is chosen, the selector will add a pass through port pair to your operator (both called "connection") and will automatically pass data through when you request the connection adapter (see 4.4).

    2. Change the associated parameters for your operator. Create the old parameter (of type ParameterTypeConfigurable), and call ConnectionAdapterHandler.getConnectionParameters with the appropriate arguments. This will give you a list of parameter types, namely a dropdown to choose between predefined and repository, the old parameter and the new connection location parameter. The default value depends on the compatibility level of the operator, see the next step for details; predefined will be selected to keep compatibility to older versions, but new operators will have "repository" preselected. Add this list to your parameter type list instead of the old connection parameter

        ParameterTypeConfigurable configurable = new ParameterTypeConfigurable("old_parameter_key", "Old description", TYPE_ID);
        parameterTypes.addAll(ConnectionAdapterHandler.getConnectionParameters(this, TYPE_ID, configurable)));
      

      Note: Make sure that the old parameter did not have the key "connection_source" or "connection_entry"! If that is the case, we recommend to deprecate the old operator (and keep it for compatibility) and create a new one that only accepts the connection repository objects. See the documentation for details how to setup the selector and the parameters.

    3. (Optional) Add a new compatibility level to your operator, namely ConnectionHandlerRegistry.BEFORE_NEW_CONNECTION_MANAGEMENT (RapidMiner version 9.2.1). It can look something like this:

       @Override
       public OperatorVersion[] getIncompatibleVersionChanges() {
          return (OperatorVersion[]) ArrayUtils.add(super.getIncompatibleVersionChanges(),
                ConnectionHandlerRegistry.BEFORE_NEW_CONNECTION_MANAGEMENT);
       }
      

      As described above, this will change which connection source will be preselected. If your extension version does not correspond to the RapidMiner version (e.g. your extension is version 2.1.0), this will have no effect. But there should be a warning on your operator automatically to encourage people to use the new connection management.

    4. Check your doWork method and metadata transformation for ConfigurationManager.getInstance().lookup calls and replace them with appropriate ConnectionAdapterHandler.getAdapter calls. This is also relevant for other classes/places where the lookup is used! The typeID stays the same as before, but you will also need to provide the key (not the value!) of the old parameter (see step 4.2) ).
    5. To make sure to capture errors regarding mismatched connection types or wrong repository entries, add a new meta data transformation rule in the constructor of your operator, with the following call:

       getTransformer().addRule(ConnectionAdapterHandler.createProcessSetupRule(this));
      
  5. Create all relevant i18n keys (the namespace referenced here is the namespace of your extension)

    1. You can use a helper class like the following that depends on CreateI18NKeysForConnectionHandler to create the initial i18n keys that might be necessary. This will reuse a lot of i18n/descriptions from the old configurable where possible. Make sure to proofread those generated i18n mappings for validity!

       import static com.rapidminer.connection.CreateI18NKeysForConnectionHandler.appendOrReplaceConnectionKeys;
       import static com.rapidminer.connection.CreateI18NKeysForConnectionHandler.connectionAdaptionHandlerDefaultValues;
      
       import java.io.IOException;
       import java.nio.file.Files;
       import java.nio.file.Path;
       import java.nio.file.Paths;
       import java.nio.file.StandardCopyOption;
       import java.util.Properties;
       import java.util.function.BiFunction;
      
       /**
        * Helper class to update Gui*.properties file with connection keys
        */
       public class I18NHelper {
      
           /**
            * Run this to update the GUI*.properties file
            *
            * @see com.rapidminer.connection.CreateI18NKeysForConnectionHandler CreateI18NKeysForConnectionHandler
            */
           public static void main(String[] args) throws IOException {
               Runnable initializer = PluginInit<ExtensionName>::initPlugin;
               String namespace = PluginInit<ExtensionName>.NAMESPACE;
               Path propertyPath = Paths.get("src/main/resources/com/rapidminer/extension/resources/i18n/GUI<ExtensionName>.properties").toAbsolutePath();
               BiFunction<String, Properties, String> defaultValues = connectionAdaptionHandlerDefaultValues(namespace);
               Path newPath = appendOrReplaceConnectionKeys(initializer, namespace, propertyPath, defaultValues);
               if (newPath == null) {
                   return;
               }
               Files.copy(Files.newInputStream(newPath), propertyPath, StandardCopyOption.REPLACE_EXISTING);
           }
       }
      

      The helper class will create a backup of the old file (file ending .bak) and the new file (file ending .new) and overwrite the original with the new version.

    2. You can reuse the icon (needed in 16px and @2x) and name you used for the configurable; the old key was gui.configurable.{baseKey}.[icon|name], the new one is gui.label.connection.type.{namespace}.{type}.[icon|label] Each group and parameter can be i18n resolved, by the key gui.label.connection.[group|parameter].{namespace}.{type}.{group}.[{parameter}].[label|tip|icon] For more information, look at the class ConnectionI18N or see the documentation on i18n.
  6. Register the Configurator with the default UI or write your own. The registration is typically done in the PluginInit*.initGUI method. For more details, see the general UI section. The example project mentioned in the beginning shows a basic implementation, including a combo box, checkbox and dependencies.

    1. If you don't have any specific needs and just want a label/textfield style UI, you don't have to do anything, but we recommend registering the default UI anyway to prevent warnings in the log file.

       ConnectionGUIRegistry.INSTANCE.registerGUIProvider(new DefaultConnectionGUIProvider(), <namespace:typeID>);
      
    2. If you want still a simple, but not too involved UI, you can subclass the DefaultConnectionGui[Provider] and register that.
    3. If you want to have a fully customized UI, override AbstractConnectionGUI and register that.
  7. Lastly, update all operator documentation! This could look something like this:

<inputPorts>
    <port name="connection" type="com.rapidminer.connection.ConnectionInformationContainerIOObject">
        This input port expects a Connection object if any.
        See the parameter <em>connection entry</em> for more information.
    </port>
</inputPorts>

<outputPorts>
    <port name="connection" type="com.rapidminer.connection.ConnectionInformationContainerIOObject">
        This output port delivers the Connection object from the input port.
        If the input port is not connected the port delivers nothing.
    </port>
</outputPorts>

<parameters>
    <parameter key="connection_source" type="selection">
        This parameter indicates how the connection should be specified. It gives you two options, predefined and
        repository. The parameter is not visible if the <em>connection</em> input port is connected.
    </parameter>
    <parameter key="connection_entry" type="string">
        This parameter is only available when the <em>connection source</em> parameter is set to <em>repository</em>. This
        parameter is used to specify a repository location that represents a connection entry. The connection can also be
        provided using the <em>connection</em> input port.
    </parameter>
    <parameter key="old_parameter_key" type="configurable">
        This parameter is only available when the <em>connection source</em> parameter is set to <em>predefined</em>.
        {old description}
    </parameter>
</parameters>