OpenWGA 7.10 - OpenWGA Concepts and Features

Design and development » Extending OpenWGA » OpenWGA java modules

Module definitions classes

A module definition itself is a class which serves all information about a module. Lets see an example definition for some pretty basic WebTML encoder:

package de.innovationgate.wga.encoders.modules;


import java.util.Locale;


import de.innovationgate.wga.encoders.EnhanceURLEncoder;

import de.innovationgate.wga.modules.KeyBasedModuleDefinition;

import de.innovationgate.wga.modules.ModuleDefinition;

import de.innovationgate.wga.modules.ModuleDependencyException;

import de.innovationgate.wga.modules.ModuleType;

import de.innovationgate.wga.modules.OptionDefinitionsMap;

import de.innovationgate.wga.modules.types.WebTMLEncoderModuleType;


public class EnhanceURLEncoderModuleDefinition implements ModuleDefinition, KeyBasedModuleDefinition {


    public String getDescription(Locale locale) {

        return "An encoder that converts URLs into full HTML links";

    }


    public Class<? extends Object> getImplementationClass() {

        return EnhanceURLEncoder.class;

    }


    public Class<? extends ModuleType> getModuleType() {

        return WebTMLEncoderModuleType.class;

    }


    public OptionDefinitionsMap getOptionDefinitions() {

        return new OptionDefinitionsMap();

    }


    public Object getProperties() {

        return null;

    }


    public String getTitle(Locale locale) {

        return "EnhanceURL Encoder";

    }


    public void testDependencies() throws ModuleDependencyException {

    }


    public String getRegistrationKey() {

        return "enhanceurl";

    }


}

This is the encoder "enhanceurl" that is available from the plugin "WGA additional encoders". Things to note:

  • Methods getTItle() and getDescription() offer descriptive information about the module, optionally for the given locale but this is no must. Where this is actually used is up to the module type. For encoders this is really only used in the registry browser. On other module types, for example authentication sources, title and description are shown in the OpenWGA admin client when those sources are chosen.
  • Method getModuleType() returns the type of module which is also a class
  • Method getImplementationClass()  returns the class that implements the encoder. This must be a subclassing/implementing the module types "base type for implementation" that we saw in the registry browser.
  • Method testDependencies() could be used to do some test if the environment is satisfying for the module to run. if it is not the definition may throw a ModuleDependencyException.
  • Encoders are KeyBasedModuleDefinitions, therefor implement the corresponding interface and offer a key by method getRegistrationKey(). For encoders this is the name by which they are used within WebTML
  • Methods getProperties() and getOptionDefinitions() are of no matter for encoders. While the first is just for special module types we will see the latter one in action on the next example

The following is the simple implementation class "EnhanceURLEncoder", which is defined via this definition:

package de.innovationgate.wga.encoders;


import java.util.regex.Matcher;

import java.util.regex.Pattern;


import de.innovationgate.utils.FormattingException;

import de.innovationgate.utils.ObjectFormatter;


public class EnhanceURLEncoder implements ObjectFormatter {

    

    public static final Pattern URL_PATTERN = Pattern.compile("... a very lengthy regex pattern ...", Pattern.CASE_INSENSITIVE);


    public String format(Object obj) throws FormattingException {

        Matcher matcher = URL_PATTERN.matcher(String.valueOf(obj));

        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {

            matcher.appendReplacement(sb, "$1<a href=\"$2\">$2</a>");

        }

        matcher.appendTail(sb);

        return sb.toString();

    }

}

This is even simpler than the module definition. Note that the class implements "ObjectFormatter", the base class for encoder implementations.

Lets have another example of a different module type. Authentication sources are good examples as their interface is quite clean, but they are configured via OpenWGA admin client so they need option definitions. Lets this time start with the implementation of the module, which normally is the first thing that exists:

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Map;

import java.util.Properties;

import java.util.Set;


import de.innovationgate.webgate.api.WGDatabase;

import de.innovationgate.webgate.api.WGQueryException;

import de.innovationgate.webgate.api.auth.AuthenticationException;

import de.innovationgate.webgate.api.auth.AuthenticationModule;

import de.innovationgate.webgate.api.auth.AuthenticationSession;

import de.innovationgate.webgate.api.auth.AuthenticationSourceListener;

import de.innovationgate.webgate.api.auth.ConfigurationException;

import de.innovationgate.wga.modules.options.OptionReader;



public class ExampleAuthModule implements AuthenticationModule {

    


    public static final String COPTION_AUTH_FILE = "File";

    public static final String COPTION_RETURN_EMAIL = "ReturnEMail";

    public static final String COPTION_EMAIL_DOMAIN = "EMailDomain";

    private File _file;

    private Properties _logins;

    private boolean _returnEMailAddresses;

    private String _eMailDomain = null;


    @Override

    public void init(Map params, WGDatabase db) throws ConfigurationException {

        

        try {

            OptionReader optionReader = OptionReader.create(params, new ExampleAuthModuleDefinition());

                    

            String fileName = (String) optionReader.readOptionValueOrDefault(COPTION_AUTH_FILE);

           _file = new File(fileName);  

            if (!_file.exists()) {

                _file = new File(fileName);

            }

            

            _returnEMailAddresses = (Boolean) optionReader.readOptionValueOrDefault(COPTION_RETURN_EMAIL);

            if (_returnEMailAddresses) {

                _eMailDomain = (String) optionReader.readOptionValueOrDefault(COPTION_EMAIL_DOMAIN);

            }


            readFile();

        }

        catch (Exception e) {

            throw new ConfigurationException("Exception configuring authentication", e);

        }

        

    }


    private void readFile() throws IOException {

        Properties logins = new Properties();

        FileReader reader = new FileReader(_file);

        logins.load(reader);

        reader.close();

        _logins = logins;

    }


    @Override

    public AuthenticationSession login(final String user, Object credentials) throws AuthenticationException {

        

        String password = _logins.getProperty(user);

        if (password == null || !password.equals(credentials)) {

            return null;

        }

        

        return new AuthenticationSession() {

            

            @Override

            public void logout() {

            }

            @Override

            public boolean isValid() {

                return true;

            }

            @Override

            public String getSessionToken() {

                return null;

            }

            @Override

            public Set getNames() {

                return Collections.singleton(user);

            }

            @Override

            public String getMailAddress() {

                return (_returnEMailAddresses ? user + "@" + _eMailDomain: null);

            }

            @Override

            public Set getGroups() {

                return Collections.emptySet();

            }

            @Override

            public String getDistinguishedName() {

                return user;

            }

        };

        

    }


    // .... interface methods whith empty implementation omitted here 


}


This simple example reads username/password definitions from a properties file. Then upon a login attempt it simply compares user/password given to the read definitions. If those match it returns a session with the most basic data.

The really interesting part here is the init() method which s called once the module is loaded (some kind of "constructor method" as constructors with arguments are discouraged for modules) and sets the authentication module up. It gets configuration parameters via Map argument "params" which it reads into local fields. The following configuration options are expected:

  • The path of the properties file containing the username/password information, expected as map entry COPTION_AUTH_FILE having a String value
  • Whether the module should return eMail addresses, expected as map entry COPTION_RETURN_EMAIL having a Boolean value
  • The eMail domain to append to the user name, expected as map entry COPTION_EMAIL_DOMAIN, again having a string value

In the option definition of this auth module we must declare these options so someone using and configuring this module is able to provide those option values. Note that we use a special utility class "de.innovationgate.wga.modules.options.OptionReader" to read those option values, which takes the params and our module definitions for arguments. It will automatically convert option values to their target type and provide default values if options are not provided.

Now to the module definition:

import java.util.Locale;


import de.innovationgate.utils.WGUtils;

import de.innovationgate.webgate.api.auth.AuthenticationModule;

import de.innovationgate.wga.modules.LocalisationBundleLoader;

import de.innovationgate.wga.modules.ModuleDefinition;

import de.innovationgate.wga.modules.ModuleDependencyException;

import de.innovationgate.wga.modules.ModuleType;

import de.innovationgate.wga.modules.OptionDefinitionsMap;

import de.innovationgate.wga.modules.options.BooleanOptionType;

import de.innovationgate.wga.modules.options.LocalizedOptionDefinition;

import de.innovationgate.wga.modules.options.PredefinedValuesOptionType;

import de.innovationgate.wga.modules.options.StringOptionType;

import de.innovationgate.wga.modules.types.AuthenticationSourceModuleType;



public class ExampleAuthModuleDefinition implements ModuleDefinition {

    

    private LocalisationBundleLoader _bundleLoader = new LocalisationBundleLoader(

        WGUtils.getPackagePath(this.getClass()) + "/example", getClass().getClassLoader());


    @Override

    public String getTitle(Locale locale) {

       return  _bundleLoader.getBundle(locale).getString("module.title");

    }


    @Override

    public String getDescription(Locale locale) {

        return _bundleLoader.getBundle(locale).getString("module.description");

    }


    @Override

    public OptionDefinitionsMap getOptionDefinitions() {


        OptionDefinitionsMap map = new OptionDefinitionsMap();

        

        LocalizedOptionDefinition file = new LocalizedOptionDefinition(

            ExampleAuthModule.COPTION_AUTH_FILE, StringOptionType.INSTANCE, _bundleLoader);

        map.addOption(file);

        

        LocalizedOptionDefinition returnEMail = new LocalizedOptionDefinition(

            ExampleAuthModule.COPTION_RETURN_EMAIL, BooleanOptionType.INSTANCE, _bundleLoader);

        returnEMail.setOptional(true);

        returnEMail.setDefaultValue(Boolean.FALSE.toString());

        map.addOption(returnEMail);

        

        PredefinedValuesOptionType domainsOptionType = new PredefinedValuesOptionType(_bundleLoader, ExampleAuthModule.COPTION_EMAIL_DOMAIN);

        domainsOptionType.addValue("gmail.com");

        domainsOptionType.addValue("yahoo.com");

        domainsOptionType.addValue("msn.com");

        LocalizedOptionDefinition eMailDomain = new LocalizedOptionDefinition(

            ExampleAuthModule.COPTION_EMAIL_DOMAIN, domainsOptionType, _bundleLoader);

        eMailDomain.addDependentOption(ExampleAuthModule.COPTION_RETURN_EMAIL, Boolean.TRUE.toString());

        map.addOption(eMailDomain);

        

        return map;

        

    }


    @Override

    public Class<? extends ModuleType> getModuleType() {

        return AuthenticationSourceModuleType.class;

    }


    @Override

    public Class<? extends Object> getImplementationClass() {

        return ExampleAuthModule.class;

    }


    @Override

    public void testDependencies() throws ModuleDependencyException {

    }


    @Override

    public Object getProperties() {

        return null;

    }


}


The most interesting part here is of course the method getOptionDefinitions() where we tell OpenWGA what configuration options are to be configured when using this auth module. But first lets have a look at another interesting part: the field "_bundleLoader". Here we use another utility class "de.innovationgate.wga.modules.LocalisationBundleLoader" to load titles and descriptions from a label properties file with base name "example" in the same package folder as this module defnition (WGUtils.getPackagePath() builds the path for this). It allows us to specify multiple language versions for those titles and descriptions in separate files and load them here in the module definition. This is used throughout the module definition for this purpose, for example on the methods getTitle() and getDescription() directly. An example file is shown later down this article.

Now first to the option definitions: We see three instances of LocalizedOptionDefinition  being created and added to an OptionDefinitionsMap which at the end of getOptionDefinitions() is returned. Each one stands for an option definition with localized titles and takes these arguments:

  • Then name of the option (as it will be available on the params map in the auth module implementation)
  • The type of option, which is an object implementing interface "de.innovationgate.wga.modules.options.OptionType". This determines the actual data type, eventually predefined possible values and even validations. There is a hoard of predefined OptionType implementations available on the WGAPI utility classes for very many purposes. We use three different here: The StringOptionType and the BooleanOptionType - where both have default instances as constants - and a PredefineValuesOptionType, which we construct to offer only those values we give it. Other values will not be possible for to be configured.
  • The bundle loader  which the LocalizedOptionDefinition uses to read titles and description of the options and also titles for predefined values.

Also to note is the properties we set on the option definitions. The definition for COPTION_RETURN_EMAIL is set to be optional and default to false. This means that the option will not be initially available on the configuration. The administrator must click "show/hide more options" to be provided with this option. Until then the default value will be false. As all options are natively stored as strings we specify the string value of boolean false here.

The option COPTION_EMAIL_DOMAIN on the other hand is specified to be dependent on option COPTION_RETURN_EMAIL. It should only be offered if this option is of value "true". But as COPTION_EMAIL_DOMAIN is not marked as optional it will automatically appear once COPTION_RETURN_EMAIL has the right value.

Now finally lets have a look at the properties file which defines the titles that are actually seen in admin client. Like usual for localisation properties files you build their name from the base name, in this case "examples" plus the locale they are valid for - divided by an underscore - and use the suffix ".properties". Leave out the locale part to specify the base localisation labels which are used when no specific label file for the needed locale is available. So in our case the base file is "examples.properties". Specific german labels could go in another file "examples_de.properties".

For the example we just have an "example.properties" which specifies english default titles, and its contents goes like this:

module.title=Example authentication module

module.description=An embarrassingly simple example for an OpenWGA authentication module


option.File.title=Properties file

option.File.description=Full path of a properties file containing user/password definitions


option.ReturnEMail.title=Return eMail address

option.ReturnEMail.description=Enable to have the module return E-Mail addresses built from the user name plus a configurable domain


option.EMailDomain.title=eMail domain

option.EMailDomain.description=A domain to use together with the username to build an eMail address

option.EMailDomain.value.gmail.com.title=Google Mail (gmail.com)

option.EMailDomain.value.yahoo.com.title=Yahoo Mail (yahoo.com)

option.EMailDomain.value.msn.com.title=MSN Mail (msn.com)

On top you see "module.title" and "module.description", the labels which are directly retrieved in getTitle() and getDescription(). More interesting are those labels beginning with "option.", which are the option titles and descriptions which are automatically loaded by the LocalizedOptionDefinition. There are two labels per option definition with the following keys:

  • "options." + option name + ".title" for a short title becoming the display label for that option in OpenWGA admin client
  • "options." + option name + ".description" for a longer description of the option available in OpenWGA admin client when clicking the option help symbol

Also there are individual title definitions for predefined option values:

  • "options." + option name + ".value." + value + ".title" for a title for single predefined option value. Note that the option value may again contain dots

Taking everything together the configuration of for this auth module will look like this:

screenshot at 2013-04-18 13:29:54.png