This documentation is written for developers and administrators and describes how to implement auto-complete logic which is used by the client.
Introduction
Beginning with yuuvis® RAD 10.6, the client-service can be extended by auto-complete plug-ins. This feature will be used by the client in one of the next minor releases. A new string field property URL for auto-complete service was introduced in the schema in version 10.4 and can be maintained with the help of the designer.
This URL will be resolved and used by the client if it is found in a form and offers the user a behavior similar to a dynamic list field.
Including a Plug-in in the Client-Service
The jar file of the plug-in must be stored in the directory <yuuvis-rad-data-dir>/autocomplete-plugins:
The client-service must be restarted to enable the plug-in.
The plug-ins will automatically be registered and be listed via this endpoint: <host>/app/api/autocomplete/info
Any number of plug-in files can be stored in this directory.
Checking the Logging of the Plug-in
As soon as the client-service has been restarted, available plug-ins are detected. Information on these plug-ins can be found in the log.
In this example the “dummy” plug-in is resolved and started:
... org.pf4j.DefaultPluginStatusProvider : Enabled plugins: [] ... org.pf4j.DefaultPluginStatusProvider : Disabled plugins: [] ... org.pf4j.DefaultPluginManager : PF4J version 3.11.0 in 'deployment' mode ... org.pf4j.AbstractPluginManager : Plugin 'AutocompleteCatalog-Dummy-Plugin@10.0.0' resolved ... org.pf4j.AbstractPluginManager : Start plugin 'AutocompleteCatalog-Dummy-Plugin@10.0.0' ... c.o.s.a.p.t.DummyPlugin$DummyProducer : Start
Result of the Request
The result of all plug-ins will look like this:
{ "entries": [ { "value": "alpha", "description": "first letter", "selectable": true }, { "value": "beta", "description": "second letter", "selectable": true }, .... ] }
Creating a Plug-in
This section describes how to set up and configure a new plug-in including its deployment and check.
The plug-in capability of the client-service is based on the PF4J framework.
The plug-in must comply with the interface defined by PF4J-Spring. In particular, this is a constructor with a PluginWrapper as a parameter:
public DummyPlugin(PluginWrapper wrapper) { super(wrapper); }
Including the Library for the Definition of the Plug-in Interface
OPTIMAL SYSTEMS provides a library for the definition of the plug-in interface. The autocomplete-plugin-interface.jar
file is available for download below and must be added to the project. It defines the interface for the client-service and also brings the plug-in capability of PF4J.
It is important that the dependency of the plug-in to the interface library is defined as provided
(in gradle compileOnly
):
<dependency> <groupId>com.os.enaio.services.extraction</groupId> <artifactId>autocomplete-plugin-interface</artifactId> <version>10.0.4</version> <scope>provided</scope> </dependency>
Implementing the Plug-in
The following example operates with JSON files as source.
Plug-in example code:
package com.os.services.autocomplete.plugin.test; import com.os.services.autocomplete.plugin.ListEntry; import com.os.services.autocomplete.plugin.ListProducer; import com.os.services.autocomplete.plugin.AutocompleteInfo; import com.os.services.autocomplete.plugin.PluginContext; import org.apache.commons.lang.StringUtils; import org.pf4j.Extension; import org.pf4j.Plugin; import org.pf4j.PluginWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.LinkedList; import java.util.List; import java.util.Map; public class DummyPlugin extends Plugin { /** * Constructor to be used by plugin manager for plugin instantiation. * Your plugins have to provide constructor with this exact signature to * be successfully loaded by manager. * * @param wrapper */ public DummyPlugin(PluginWrapper wrapper) { super(wrapper); } private final static Logger LOGGER = LoggerFactory.getLogger(DummyProducer.class); @Override public void start() { LOGGER.info("Start"); } @Override public void stop() { LOGGER.info("Stop"); } @Extension public static class DummyProducer implements ListProducer { private final static Logger LOGGER = LoggerFactory.getLogger(DummyProducer.class); AutocompleteInfo info = new AutocompleteInfo("dummy", null); @Override public AutocompleteInfo info() { return info; } @Override public List<ListEntry> getList(PluginContext context) { List<ListEntry> stage = new LinkedList<>(); stage.add(new ListEntry("alpha", null)); //... the other letters... stage.add(new ListEntry("omega", null)); List<ListEntry> result = new LinkedList<>(); if (StringUtils.isNotBlank(context.getQuery())) { String query = context.getQuery().replace("*", ""); for (ListEntry e: stage) { if (e.getValue().toLowerCase().contains(query.toLowerCase())) { result.add(e); } } } else { result = stage; } return result; } } }
Building the jar
File
If the plug-in is packaged in a jar
file, there are important points to note:
The following attributes must be specified in the MANIFEST.MF
file (in the example plug-in):
Plugin-Class: com.os.services.autocomplete.plugin.demo.DummyPlugin Plugin-Dependencies: Plugin-Id: Dummy-Plugin Plugin-Provider: os Plugin-Version: 10.0.0
For these values to be automatically inserted into the jar
file, the maven-assembly-plugin
can be configured accordingly:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.1.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <finalName>${project.artifactId}-${project.version}-all</finalName> <appendAssemblyId>false</appendAssemblyId> <attach>false</attach> <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> </manifest> <manifestEntries> <Plugin-Id>${plugin.id}</Plugin-Id> <Plugin-Version>${plugin.version}</Plugin-Version> <Plugin-Provider>${plugin.provider}</Plugin-Provider> <Plugin-Class>${plugin.class}</Plugin-Class> <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> </manifestEntries> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
The jar
file must contain all required libraries (except autocomplete-plugin-interface
and thus also pf4j).
The MANIFEST.MF
file must have the appropriate entries.
The extensions.idx
file must be included. It is automatically generated by PF4J during build.
This generated jar
file can now be placed in the appropriately configured directory of the client-service.
Checking the Plug-in
To check the new plugin, it must be placed in the directory described above and the client-service must be restarted.
Form script usage for dynamic list fields
The endpoints can be directly used for string properties which are classified as a dynamic list. The following form script gives an example:
// fill a dynamic list field with catalog data var greekalphabet = scope.model.stringc1 scope.api.http.get('/dummy','/app/api/autocomplete').then( function(result) { greekalphabet.setList({ entries: result.data.entries }); }, function(error) { scope.api.util.notifier.error('Failed to load greek alphabet',error+''); } )
To produce the data used by the script, the following plug-in must be placed in the folder “autocomplete-plugins”.
The data looks like this:
{ "entries": [ { "value": "alpha", "selectable": true }, { "value": "beta", "selectable": true }, ... ] }