OpenWGA 7.11 - OpenWGA Concepts and Features

Design and development

Service APIs

Service APIs are some special services which OpenWGA provides to custom code via a standarized API interface. As OpenWGA is a Java application service APIs are also Java APIs but can also be used from TMLScript.

Service examples provided by OpenWGA are:

  • Hashing passwords using the currently configured hash scheme
  • Scaling images
  • Lookup DNS addresses
  • Send administrative mails

Service APIs are used anywhere in OpenWGA where an integration of a special functionality API into standard TMLScript/WGA Server API is not reasonable as this would bloat this API and the service is too specific for that.

Besides this the service API functionality in OpenWGA has the following additional features:

  • Services may be provided by differing service implementations. OpenWGA provides a way to configure the service implementation to use for a server via server configuration.
  • OpenWGA plugins can define and register their own service APIs for any purpose, so those APIs are easily fetchable via TMLScript/WGA Server API

Retrieving a service API

You can retrieve service API objects from TMLScript or from the OpenWGA Server API.

In TMLScript use method WGA.service() which directly returns a service object. You need to give the Java interface of the service as parameter, which you reference in TMLScript via the "Packages" global object (see Accessing Java and the WGAPI to learn why). For example, retrieving the service API for looking up hashing passwords whose service API interface is "de.innovationgate.utils.security.HashingService":

var hashingService = WGA.service(Packages.de.innovationgate.utils.security.HashingService);

The returned object is a Java object which extends/implements the service class given. You can call its methods, again using the techniques described in Accessing Java and the WGAPI

For example to hash a password using interface de.innovationgate.utils.security.HashingService you need first to call method generateSalt() to create a salt and then call createHash() on the bytes of the password string plus the salt to create the hash. This can be done the following way in TMLScript:

var salt = hashingService.generateSalt();

var hashedPwd = hashingService.createHash(javaObject(password).getBytes("UTF-8"), salt);


In Java you have the object de.innovationgate.wga.server.api.WGA of the OpenWGA Server API which has an identical method service(). Here it looks almost equal:

import de.innovationgate.utils.security.HashingService;

import de.innovationgate.wga.server.api.WGA;

import de.innovationgate.webgate.api.WGException;

import de.innovationgate.utils.security.HashingException;

import java.io.UnsupportedEncodingException;


...


public String hashPassword(String password) throws WGException, HashingException, UnsupportedEncodingException {

  WGA wga = WGA.get();

  HashingService hashingService = wga.service(HashingService.class);

  Object salt = hashingService.generateSalt();

  return hashingService.createHash(password.getBytes("UTF-8"), salt);

}

Service APIs bundled with OpenWGA

The following interface classes come pre-bundled with OpenWGA. The usage of all shoud be documented in WGA Server API Javadoc reference.
Name Interface class Purpose
Administrative mail service de.innovationgate.wgpublisher.WGAAdminNotificationService Sending mail messages to the administrator of this system.
Application log service de.innovationgate.wgpublisher.log.AppLog Retrieving application log content
Hashed password reading/writing service de.innovationgate.wgpublisher.HashedPasswordService Service for reading/writing hashed password items like <tml:input type="hashedpassword"> creates them are used as password items on authentication content stores. This effectively is a high-level version of the uni-directional encryption service which additionally provides the storage format needed for these items.
Image link reader service de.innovationgate.wgpublisher.webtml.utils.ImageLinkReader Reading and parsing image links stored in content items
Image scaling (format-neutral) de.innovationgate.utils.ImageScaler Provides the currently active image scaler implementation, usable for any image format. Same as WGA.createImageScaler() on WGA Server API.
Image scaling (mimetype-specific) de.innovationgate.utils.MimeTypeSpecificImageScalerFactory Provides image scalers for specific mime types
Registration for temporary downloads de.innovationgate.wgpublisher.TemporaryDownloadService Registers files on the servers file system with a users session, so only this user can download the file for a limited time of at least 30 minutes. Does the registration and provides the download URL.
Uni-directional encryption service de.innovationgate.utils.security.HashingService Encrypts binary data into an unrecoverable form, which however can be compared to other data for equality. Interface provides both, encryption and comparision functionality.

Configuring API implementations to use

For some APIs there is a choice of implementations to use. The implementation that a server should use can be configured in OpenWGA Admin Client, on menu "Configuration" > "Basic Settings".

There is a section Platform service choices which offers the choosable APIs and their implementations:

serviceapi.choice.png

Here an administrator can configure which implementation to use. Some APIs have a system default which may also be available for configuration here.

Providing additional service APIs and implementations

OpenWGA modules can provide additional implementations for existing services APIs or could even define new service APIs where implementations could be registered.

The process to do both uses the OpenWGA module registry and simply involves registering additional module definitions for either APIs themselves or API implementations. See the instructions on OpenWGA java modules on how to register modules.

Definining service APIs

First step of defining a new service API is to create an implementation class for it. This is either a Java interface or base class that all implementors of the service must implement/extend. 

Second step (which is not necessary for single implementation services) is to create a module type class, implementing de.innovationgate.wga.modules.DeclaringModuleType, which should be the type by which implementations of your service API need to be registered. Here the method getImplementationBaseClass() should return your service API interface created before. All boolean methods on this class should return false. Method getPropertyClass() should return null.

For example: The module type for the service API to scale images:

public class ImageScalerModuleType implements DeclaringModuleType {


    public String getDescription() {

        return "A tool object to scale image data from TMLScript. OpenWGA will use the first-best that is registered.";

    }


    public String getTitle() {

        return "Image scaler module type";

    }


    public boolean isKeyBased() {

        return false;

    }


    public boolean isSelfRegistered() {

        return false;

    }


    public Class<? extends Object> getImplementationBaseClass() {

        return ImageScaler.class;

    }


    public boolean isPropertiesNeeded() {

        return false;

    }


    public Class<? extends Object> getPropertyClass() {

        return null;

    }


}

As last step create a module definition for the service API and register it in your registrar which should have the following characteristics:

  • Use class de.innovationgate.wga.modules.types.ServiceApiType on getModuleType()
  • Return the module type created in step two in getImplementationClass()
  • Implement de.innovationgate.wga.modules.KeyBasedModuleDefinition and in its method getRegistrationKey() return the name of the service API interface class as String
  • in getProperties() you may return an object of type de.innovationgate.wga.modules.types.ServiceApiProperties to give further specifications
    • setDefaultImplementation() may set the class of a service implementation that should be active by default, without anyone configuring
    • setImplementable() may set if there should actually be multiple implementations. Setting false means that always the default implementation will be used and there will be no configuration opportunity (see section below about single implementation classes)
    • setOptionalConfig() determines if the configuration of the service API implementation in admin client should be optional (i.e. it is not shown until the option is manually added)
  • Do not return any options on getOptionDefinitions() as OpenWGA will not use them

For example, the module definition foir the image scaler service API:

public class ImageScalerApiModuleDefinition implements ModuleDefinition, KeyBasedModuleDefinition {



    protected LocalisationBundleLoader _bundleLoader = 

      new LocalisationBundleLoader(WGUtils.getPackagePath(getClass()) + "/apis", getClass().getClassLoader());


    @Override

    public String getTitle(Locale locale) {

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

    }


    @Override

    public String getDescription(Locale locale) {

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

    }


    @Override

    public OptionDefinitionsMap getOptionDefinitions() {

        return null;

    }


    @Override

    public Class<? extends ModuleType> getModuleType() {

        return ServiceApiType.class;

    }


    @Override

    public Class<? extends Object> getImplementationClass() {

        return ImageScalerModuleType.class;

    }


    @Override

    public void testDependencies() throws ModuleDependencyException {

    }


    @Override

    public Object getProperties() {

        ServiceApiProperties props = new ServiceApiProperties();

        return props;

    }


    @Override

    public String getRegistrationKey() {

        return ImageScaler.class.getName();

    }


}


You can also create a service where always objects of  a single implementation class are returned and where no multiple implementations are allowed, just to provide a way to access that otherwise static service. In that special case modify the process like this:

  • Use this single implementation as your interface API in step one
  • Skip the module type creation on step two
  • On the module definition of the last step return your single implementation also in getImplementationClass() and return a ServiceApiProperties object in getProperties() having setImplementable() set to false and setDefaultImplementation() set to your single implementation class

Providing API implementations

A class that wants to implement a service API must have the following characteristics:

  • It should implement/extend the API interface class
  • It should have a default (no-arg) constructor
  • If it needs application context to operate then it should implement interface de.innovationgate.wga.server.api.WGAAwareService. This defines a method injectWGA() on which the WGA server API object, which currently instantiates the service, will be injected to it right after its instantiation.

Then you also need to register a module definition to the module registry for your implementation:

  • Return the module type of the API (returned as implementation class on the Service API definition) on getModuleType()
  • Return your implementation cass on getImplementationClass()
  • Provide the title, which on service configuration should be used as title for this implementation, in getTitle()
  • No output on getProperties(), getOptionDefinitions() needed


For example the module definition for the Advanced Image Scaler, an implementation of the Image Scaling API, looks like this:

public class AdvancedImageScalerModuleDefinition implements ModuleDefinition {


    public String getDescription(Locale locale) {

        return "An image scaler using an advanced bicubic scaling filter";

    }


    public Class getImplementationClass() {

        return AdvancedImageScaler.class;

    }


    public Class getModuleType() {

        return ImageScalerModuleType.class;

    }


    public OptionDefinitionsMap getOptionDefinitions() {

        return null;

    }


    public Object getProperties() {

        return null;

    }


    public String getTitle(Locale locale) {

        return "Advanced Image Scaler";

    }


    public void testDependencies() throws ModuleDependencyException {

    }


}