Metadata plugins

Writing metadata plugins is easy in Hop. Any Plain Old Java Object can be used as a starting point.

@HopMetadata Annotation

This annotation signals to the Metadata plugin type that there is something worth looking at. The class which carries this annotation will contain the metadata.

Here are the attributes of the @HopMetadata annotation:

  • key : this uniquely identifies the plugin and will be the name of the folder in which the metadata resides when serialising to JSON (see below). Use lower-case words separated by dashes, see Choosing a key.

  • legacyKeys : the keys this metadata type was known under before its key was renamed (optional), see Renaming a key

  • name : a human-readable name

  • description : an extended description

  • image : the path to an image which helps identify the metadata in the Hop GUI

The class with this annotation will be found either because it lives in the plugins/ folder of Hop or if it’s an internal class and is described in the file engine/src/main/resources/hop-metadata-plugins.xml

Choosing a key

The key is more than an internal ID. It is the name of the folder in a project which holds the metadata objects (metadata/<key>/<name>.json), it is written into the metadata exported for remote execution, and it is the ID used to disable the type in disabledGuiElements. Users see it and check it into version control, so it is effectively public.

Use lower-case words separated by dashes (kebab-case) which describe the type, like the existing pipeline-run-configuration, sftp-connection or mail-server-connection. Don’t use the Java class name or PascalCase (MailServerConnection): the folders in a project then no longer follow one convention.

Renaming a key

Changing the key of an existing metadata type would make all the objects users already have invisible, because they live in a folder named after the old key. If you have to rename a key, move the old one to legacyKeys:

java
@HopMetadata(
    key = "mail-server-connection",
    legacyKeys = {"MailServerConnection"},
    name = "i18n::MailServerConnection.name",
    ...)
public class MailServerConnection extends HopMetadataBase implements IHopMetadata {

With a legacy key in place:

  • objects in the folder named after the legacy key are still listed and loaded. If an object exists in both folders, the one in the folder named after the current key wins.

  • saving an object always writes it to the folder named after the current key and removes the copy in the legacy folder. Projects move to the new folder one object at a time, as users edit them.

  • deleting an object removes it from all the folders.

  • a metadata export which uses the legacy key, for example one sent by an older Hop client to a Hop server, is still understood.

Code which looks for metadata files or compares type keys should use HopMetadataUtil.getAllKeys(), HopMetadataUtil.matchesKey() or JsonMetadataSerializer.findFilename() rather than the key alone, so it also finds objects which haven’t moved yet.

Note that older Hop versions only know the old key: once an object has been saved by a newer version, older versions no longer see it. Mention the rename in the release notes.

Metadata Properties

All properties you want to have as part of the shared Hop Metadata should get the @HopMetadataProperty annotation. All top level classes flagged with @HopMetadata should have a name property of type String.

Here are the @HopMetadataProperty attributes:

  • key : optional key if you want it to be different from the name of the field

  • password: set this to true if you want the String field to be encoded using the TwoWayPasswordEncoder of the IHopMetadataProvider interface.

  • storeWithName: if you want to store a reference to another shared metadata object, you can set this to true, otherwise all the properties of the object will be stored.

Here are the supported data types:

  • enum : any enum is serialized using its name

  • String : also see the password attribute above.

  • Integer / int

  • Long / long

  • Boolean / boolean

  • java.util.Date

  • java.util.Map<String,String>

  • java.util.List<T> : with T any of the data types listed here.

  • POJO : Any class with more @HopMetadataProperty annotations in it.

An Editor class to edit the metadata

You also want to have a way to edit the metadata in the GUI. This can be done by extending the class MetadataEditor<IHopMetadata>.

The path to the Editor class will be found automatically by looking at the name of the metadata plugin class and then simply by appending Editor to it. If you prefer to keep metadata and GUI code separate the Hop GUI will also look in package org.apache.hop.ui instead of org.apache.hop

Working examples:

  • org.apache.hop.path.to.MyMetadata → org.apache.hop.path.to.MyMetadataEditor

  • org.apache.hop.partition.PartitionSchema → org.apache.hop.ui.partition.PartitionSchemaEditor

Some methods explained

  • createControl() : create the various controls on the given parent Composite. The composite has a FormLayout set.

  • setWidgetsContent(): Using the metadata object (or getMetadata()) you can set the content on the created controls.

  • getWidgetsContent(): Grab the values of the various controls and modify the metadata

  • save(): verify settings, grab metadata and call super.save()

  • setFocus(): sets the focus on the metadata dialog or tab. Choose the control to set the focus to (usually the name).

  • createButtonsForButtonBar(): if you want to add buttons at the bottom to do things like testing, viewing, …​ you can use this method

Metadata serialisation

As mentioned above, the key or ID the @HopMetadata plugin is used as a top level folder to store objects in. For the serialisation to JSON most simple data types are supported. However we suggest you use the KISS principle. If you want to serialize interfaces (for example like IDatabase used by DatabaseMeta) you might want to flag the interface with the @HopMetadataObject annotation. This annotation allows you to specify an object factory for those classes. Such an object factory implements interface IHopMetadataObjectFactory with the 2 following methods:

  • public Object createObject( String id, Object parentObject ) throws HopException → Creates an object using an ID. The parent object is often another metadata object. You can use it to check if it implements IVariables so you can inherit variables from there.

  • public String getObjectId( Object object ) throws HopException → Retrieves the object ID from the given object. We recommend that you check the instance of the object until the factory interface supports generics. (TODO)