Interface IPluginActionNodeHandler<TData extends BaseActionProps>

Type Parameters:
TData - Type of the node's properties model, which must extend BaseActionProps. The properties themselves are stored as JSON and serialized to / deserialized from this type automatically. You should make sure this type can be serialized / deserialized via JSON. This means you should only basic field types such as number, booleans, or strings, or nested types. Furthermore, If you wish, you can also use the type JSONObject to skip serialization / deserialization and work with the raw JSON data.
All Superinterfaces:
IBaseActionClientHandlerNode<TData>, IBaseActionNode<TData>, IBeanValidatingElement<TData,WorkflowNode>, IBeanValidatingNode<TData>, ICustomParametersUpdateable, IElementHandler<TData,WorkflowNode>, IExecutingLikeActionNode<TData>, IExecutionResultDescriptor, IFCPlugin, IHierarchyValidatingNode<TData>, INamedUiElement, INameProviding, INodeHandler<TData>, IPluginGenericCustomGUI<IPluginWorkflowNodeBean>, IPluginWorkflowNode, IResourceBundleLocator, ISingleBaseActionNodePrototype<TData>, ISingleElementPrototype<TData,WorkflowNode>, ISingleNodePrototype<TData>, ITransferable, IWorkflowElementTypeProviding, IWorkflowNodeFlowAnalyzer<TData>, IWorkflowNodeTypeProviding, Serializable
All Known Implementing Classes:
APluginActionNodeHandler

public interface IPluginActionNodeHandler<TData extends BaseActionProps> extends IPluginWorkflowNode, INodeHandler<TData>, IBaseActionNode<TData>, IExecutingLikeActionNode<TData>, IBaseActionClientHandlerNode<TData>, IBeanValidatingNode<TData>, ISingleBaseActionNodePrototype<TData>
Mixin meant for IPluginWorkflowNode plugins that only wish to provide a workflow action that executes some business logic. The INodeHandler offers many methods that are irrelevant for this use case - this mixin implements most methods with the appropriate defaults.

By default, you only need to implement execute(INodeExecutionParams) and getName(), as well as getPropertiesViewXhtmlName() for the UI. Make sure that the getName() is unique among all plugins. This name is also used as the getType() of the workflow node.

The XHTML page with the UI for editing the action's property must be placed in the directory getPropertiesViewXhtmlPath(), with the file name getPropertiesViewXhtmlName(). You can also add properties files in getResourceBundlePath() with localized messages for the UI.

Assume the getType() is MyActionPlugin. Then, if you do not override the defaults:

  • For each language you want to support, add a properties file in /src/main/resources/WEB-INF/properties/i18n_LOCALECODE.properties. The LOCALECODE is en for English, de for German, fr for French etc. These properties are then available in the XHTML page via the variable msg, see IElementHandler.getPropertiesViewXhtml()
  • Add an entry in the properties file for the keys MyActionPlugin.name, MyActionPlugin.desc, MyActionPlugin.label to customize the name of the plugin, the description of the plugin, and the name of the workflow action.

You can also override most methods of this mixin to customize the action plugin. The following lists a few common use cases:

To use a localized name or description
Add an entry to your getResourceBundle(Locale). For the name of the plugin as it appears in the backend plugin configuration menu, use getI18nKeyDisplayName(). For the description that also appears in the backend plugin configuration menu, use getI18nKeyDescription(). For the label of the action as it appears in the workflow designer, use getI18nKeyActionLabel().
To update the properties model for new versions of the plugin
First make sure the MANIFEST.MF of your plugin contains the appropriate version information, see getVersion(). Then, override ICustomParametersUpdateable.updateCustomParams(de.xima.fc.interfaces.IUpdateCustomParametersParams). If you are using the SemVer (semantic version) format (i.e. your version looks like x.y.z), you may want to use the default methods provided by the mixin ISemverUpdating.
To return a value form the exec method and have it displayed in the workflow designer
Override getSuccessValueDescriptor(IValueDescriptorFactory) (which defines which values the exec method may return, and also contain a human-readable description for each) and adjust the return value of execute(INodeExecutionParams) accordingly.
To provide pre-configured actions to the drawer panel to the left of the workflow designer
Override INodeHandler.getNodePrototypes(de.xima.fc.interfaces.workflow.params.IGetNodePrototypesParams), optionally adding the list of prototypes (which contains just one item) returned by the super method implementation.
To display the action in a different category in the workflow designer
Override ISingleElementPrototype.getSubCategory(IGetElementPrototypesParams)
To use a custom UI
Create an XHTML page in getPropertiesViewXhtmlPath(); and override getPropertiesViewXhtmlName() with the name of the XHTML page. For example, if you name the XHTML page myCustomUi and do not override the XHTML path, add the file /src/main/resources/WEB-INF/ui/myCustomUi.xhtml. If you need a custom bean as a controller, also override getMainPluginBeanClass(). See IElementHandler.getPropertiesViewXhtml() for more details on the XHTML page.
To use more than one bean
Override getUnmanagedBeans(), but make sure to include getMainPluginBeanClass(). The additional beans can be used in XHTML pages, or referenced via Inject.
To customize the serialization / deserialization process of the properties model
Use the JSONField annotation on the fields of your properties model class, and/or override IElementHandler.getFastJsonConverter()

Examples

A simple skeletal implementation for an action that sends an HTTP post request might look like as follows. Note that you replace APluginActionNodeHandler with this interface IPluginActionNodeHandler if you need a custom super class (you only need to implement the getter getPluginInitializeData()).

 package com.example;
 import java.net.MalformedURLException;
 import java.net.URL;
 import de.xima.fc.exceptions.AbstractAbruptCompletionException;
 import de.xima.fc.interfaces.workflow.execution.INormalCompletionResult;
 import de.xima.fc.interfaces.workflow.params.INodeExecutionParams;
 import de.xima.fc.plugin.escalation.MyPostRequestPlugin.EMyPostRequestError;
 import de.xima.fc.plugin.escalation.MyPostRequestPlugin2.MyPostRequestProps;
 import de.xima.fc.workflow.mixin.APluginActionNodeHandler;
 import de.xima.fc.workflow.taglib.model.BaseActionProps;
 @SuppressWarnings("serial")
 public class MyPostRequestPlugin extends APluginActionNodeHandler<MyPostRequestProps> {
   public static class MyPostRequestProps extends BaseActionProps {
     private String url;
     public String getUrl() {
       return url;
     }
     public void setUrl(String url) {
       this.url = url;
     }
   }
   public Class<EMyPostRequestError> getErrorCodeClass() {
     return EMyPostRequestError.class;
   }
   @Override
   public String getName() {
     return "My Awesome Post Plugin";
   }
   @Override
   public INormalCompletionResult execute(INodeExecutionParams<MyPostRequestProps params) throws AbstractAbruptCompletionException {
     try {
       URL url = new URL(params.getData().getUrl());
       // todo: implement sending an HTTP post request
       // ...
     }
     catch (MalformedURLException e) {
       // Do not catch unexpected "Exception" - this is handled by the system.
       // Only catch exceptions you anticipate may occur.
       throw params.throwingException().cause(e).build();
     }
     return params.normalResult().build();
   }
 }
 

A more advanced version with custom return values for the success and error case might look like this:

 package com.example;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Map;
 import org.apache.commons.lang3.tuple.Pair;
 import de.xima.fc.exceptions.AbstractAbruptCompletionException;
 import de.xima.fc.interfaces.workflow.execution.INormalCompletionResult;
 import de.xima.fc.interfaces.workflow.params.INodeExecutionParams;
 import de.xima.fc.interfaces.workflow.value.IRecordValueDescriptor;
 import de.xima.fc.interfaces.workflow.value.IUnionValueDescriptor;
 import de.xima.fc.interfaces.workflow.value.IValueBuilder;
 import de.xima.fc.interfaces.workflow.value.IValueDescriptorFactory;
 import de.xima.fc.plugin.escalation.MyPostRequestPlugin.EMyPostRequestError;
 import de.xima.fc.plugin.escalation.MyPostRequestPlugin.MyPostRequestProps;
 import de.xima.fc.workflow.mixin.APluginActionNodeHandler;
 import de.xima.fc.workflow.taglib.model.BaseActionProps;
 @SuppressWarnings("serial")
 public class MyPostRequestPlugin extends APluginActionNodeHandler<MyPostRequestProps> {
   public static enum EMyPostRequestError {
     MALFORMED_URL,
   }
   public static class MyPostRequestProps extends BaseActionProps {
     private String url;
     public String getUrl() {
       return url;
     }
     public void setUrl(String url) {
       this.url = url;
     }
   }
   @Override
   public String getName() {
     return "My New Action Plugin";
   }
   @Override
   public INormalCompletionResult execute(INodeExecutionParams<MyPostRequestProps> params) throws AbstractAbruptCompletionException {
     MyPostRequestProps props = params.getData();
     try {
       // do something awesome
       Pair<Integer, String> result = sendPostRequest(props.getUrl());
       return params.normalResult().success(f -> f.asRecord() //
           .property("responseCode", result.getLeft()) //
           .property("responseMessage", result.getRight())) //
           .build();
     }
     catch (MalformedURLException e) {
       throw params.throwingException().error(EMyPostRequestError.MALFORMED_URL.name(), Map.class, b -> b.asRecord() //
           .property("message", e.getMessage()) //
           .property("invalidUrl", props.getUrl()) //
           .build() //
       ).build();
     }
     // Do not catch unexpected "Exception" - this is handled by the system.
     // Only catch exceptions you anticipate may occur.
   }
   private Pair<Integer, String> sendPostRequest(String urlString) throws MalformedURLException {
     URL url = new URL(urlString);
     // todo: implement sending an HTTP post request
     return Pair.of(404, "Not Found");
   }
   public Class<EMyPostRequestError> getErrorCodeClass() {
     return EMyPostRequestError.class;
   }
   @Override
   public IUnionValueDescriptor<String> getErrorValueDescriptor(IValueDescriptorFactory factory) {
     return factory.unionStringBuilder() //
         // When GENERAL error occurs, a string with the error message is returned
         .addAndUseAsDefault(EMyPostRequestError.GENERAL.name(), f -> f.string()) //
         // When the malformed URL error occurs, an JSON object with two properties is returned
         .add(EMyPostRequestError.MALFORMED_URL.name(), f -> f.recordBuilder() //
             .requiredProperty("message", v -> v.string()) //
             .requiredProperty("invalidUrl", v -> v.string()) //
             .build()) //
         .build();
   }
   @Override
   public IRecordValueDescriptor getSuccessValueDescriptor(IValueDescriptorFactory factory) {
     return factory.recordBuilder() //
         .requiredProperty("responseCode", f -> f.integer(200)) //
         .requiredProperty("responseMessage", f -> f.string()) //
         .build();
   }
 }
 
Since:
7.0.0
Author:
XIMA MEDIA GmbH
  • Method Details

    • execute

      Executes the business logic of this action plugin, and returns the values made available by this action.

      In case the action was executed successfully, you need to return a value that conforms to the data type described by getSuccessValueDescriptor(IValueDescriptorFactory). See that method for an example on how to use a custom result type. If you did not override that method, no value is returned by this action, and you can simply use:

       return params.normalResult().build();
       

      When the action could not be completed normally, you need to throw an appropriate exception indicating the reason for why the action could not be completed. Most commonly this happens when the business logic threw an exception. In this case you should use the provided INodeExecutionParams.throwingException() to build the NodeThrewException, and throw it:

       throw params.throwingResult().cause(exceptionThatWasThrown).build();
       
      If you did override getErrorValueDescriptor(IValueDescriptorFactory), you should also include the error code and error details via INodeThrewExceptionBuilder.error(String, Object) or INodeThrewExceptionBuilder.error(String, Class, IValueCreator).
      Specified by:
      execute in interface INodeHandler<TData extends BaseActionProps>
      Parameters:
      params - Data this action plugin may use. Most importantly, you will need INodeExecutionParams.getData(), which contains the configuration as set by the user in the workflow designer.
      Returns:
      The values returned by this action, as specified by getSuccessValueDescriptor(IValueDescriptorFactory)}. Any value not set explicitly will use its default value from the success vale descriptor.
      Throws:
      AbstractAbruptCompletionException - When the action could not be completed normally. Use the provided INodeExecutionParams.throwingException() and INodeExecutionParams.returningException() to create an instance of the exception.
      NodeThrewException - When this exception is thrown, execution of the workflow task is aborted, unless an exception handler was defined in the workflow configuration.
      NodeReturnedException - When this exception is thrown, the action is judged to have been executed successfully, and execution of the workflow task is stopped.
      RuntimeException - A runtime exception may be thrown to indicate an unforeseen error, such as general database errors. For all errors you expect to happen, you should throw an appropriate NodeThrewException with the details of the exception. When a runtime exception is thrown, it is wrapped in a NodeThrewException and treated as if that exception had been thrown.
    • extractDescription

      default String extractDescription(TData data)
      Description copied from interface: IElementHandler
      Retrieves the name that will be stored as AWorkflowElement.getDescription(). For example, actions may let the user enter a description for the action, which would be returned by this method. A condition node may an explanation of the test condition as the description.

      The default implementation checks whether the IElementHandler.getDataModelClass() implements IDescriptionProviding and if it does, returns IDescriptionProviding.getDescription(). Otherwise returns an empty string.

      Specified by:
      extractDescription in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      data - The data of the workflow element for which to retrieve the description.
      Returns:
      The description of the workflow element with the given data.
    • extractName

      default String extractName(TData data)
      Description copied from interface: IElementHandler
      Retrieves the name that will be stored as AWorkflowElement.getName(). For example, actions may let the user enter the name of the action, which would be returned by this method. A condition node may use a summary of the test condition as the name. A button trigger event may use the name of the button.

      The default implementation checks whether the IElementHandler.getDataModelClass() implements INameProviding and if it does, returns INameProviding.getName().

      Specified by:
      extractName in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      data - The data of the workflow element for which to retrieve the name.
      Returns:
      The name of the workflow element with the given data.
    • getAlwaysValueDescriptor

      default IValueDescriptor<?,? extends IValueBuilder<?>> getAlwaysValueDescriptor(IValueDescriptorFactory factory)
      This default implementation returns a void value descriptor. This is appropriate when this plugin does not need to return any values. If you wish to return values (and make them available to other action via variables / placeholders), override this method. If you do, make sure you also modify execute(INodeExecutionParams) and supply the appropriate values upon execution.
      Specified by:
      getAlwaysValueDescriptor in interface IExecutionResultDescriptor
      Parameters:
      factory - Factory that may be used for creating the descriptor. You may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field.
      Returns:
      The value descriptor for that data that is made available to the workflow when the node is executed, regardless of whether is succeeded or failed.
      See Also:
    • getDataModelClass

      default Class<? extends TData> getDataModelClass()
      This default implementation attempts to read the type of the TData parameter via reflection. If that does not work, or you need to optimize, override this method and return the type directly.
      Specified by:
      getDataModelClass in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Returns:
      The class corresponding to the type parameter TData.
      See Also:
    • getDescription

      default String getDescription(Locale locale)
      The default implementation returns the localized value of the getI18nKeyDescription() in the getResourceBundle(Locale). When no value exists for that key, returns an empty string (= no description).
      Specified by:
      getDescription in interface IFCPlugin
      Parameters:
      locale - The locale for which to get the description.
      Returns:
      An HTML markup string with the description for this plugin, or null if no description is available.
      See Also:
    • getDisplayName

      default String getDisplayName(Locale locale)
      The default implementation returns the localized value of the getI18nKeyDisplayName() in the getResourceBundle(Locale). When no value exists for that key, returns the getName() of this plugin.
      Specified by:
      getDisplayName in interface IFCPlugin
      Specified by:
      getDisplayName in interface INamedUiElement
      Returns:
      Wert, der das entsprechende Objekt an Oberfläche repräsentiert (wird i.A. zur Laufzeit ermittelt).
      See Also:
    • getErrorCodeClass

      default Class<? extends Enum<?>> getErrorCodeClass()
      Returns the type of the enumeration that defines the error types that may occur during the execution of a node. The default returns null, which corresponds to no custom errors. In that case, if the plugin throws an exception, it is treated as a general exception.
      Returns:
      The enumeration class that defines the possible error codes.
    • getSoftErrorCodeClass

      default Class<? extends Enum<?>> getSoftErrorCodeClass()
      Returns the type of the enumeration that defines the soft error types that may occur during the execution of a node. The default returns null, which corresponds to no custom soft errors. In that case the plugin cannot add any soft errors.
      Returns:
      The enumeration class that defines the possible soft error codes.
    • getErrorValueDescriptor

      default IUnionValueDescriptor<String> getErrorValueDescriptor(IValueDescriptorFactory factory)
      This default implementation returns the error codes from the getSoftErrorCodeClass(), if any. No error data is made available for the error codes. When the action can have errors with error data, override this method and use IValueDescriptorFactory.unionStringBuilder() to add the errors.
       public IUnionValueDescriptor<String> getSoftErrorValueDescriptor(IValueDescriptorFactory f) {
         return f.recordBuilder() //
           .add("FILE_NOT_FOUND", f.recordBuilder().requiredProperty("file", f.string("")).build()) //
           .add("NETWORK_ERROR", f.recordBuilder().requiredProperty("url", f.string("")).build()) //
           .build();
       
      Note that it is recommended that you keep the available error codes in an enum, so you can reuse them later.

      In your execute(INodeExecutionParams) method, you can then add a soft error when necessary while building an error result:

       public INormalCompletionResult execute(INodeExecutionParams<TData> params) throws AbstractAbruptCompletionException {
         try {
           // Run business logic...
           return params.normalResult().success(result).build();
         }
         catch (final IOException e) {
           // Add the soft error
           final var errorData = new HashMap<String, Object>();
           errorData.put("file", fileName);
           throw throw params.throwingException().error("FILE_NOT_FOUND", errorData).cause(e).build();
         }
       }
       

      Instead of the factory passed in to this method, you may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field. *

      Specified by:
      getErrorValueDescriptor in interface IExecutionResultDescriptor
      Parameters:
      factory - Factory that may be used for creating the descriptor. You may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field.
      Returns:
      The value descriptor for the data that is made available to the workflow when the execution of the node failed (such as by throwing an exception).
      See Also:
    • getI18nKeyActionLabel

      default String getI18nKeyActionLabel()
      Returns:
      The i18n key for the name of the action, as shown in the workflow designer. Defaults to the getI18nKeyDisplayName().
    • getI18nKeyActionSearchText

      default String getI18nKeyActionSearchText()
      Returns:
      The i18n key for the search text of the action, not shown in the UI but used for filtering. Defaults to "getName().searchtext".
    • getI18nKeyActionSubLabel

      default String getI18nKeyActionSubLabel()
      Returns:
      The i18n key for the sub label of the action, as shown in the workflow designer below the name of the item. Defaults to "getName().sublabel".
    • getI18nKeyActionTitle

      default String getI18nKeyActionTitle()
      Returns:
      The i18n key for the title of the action, as shown in the workflow designer when hovering over the item with the mouse. Defaults to "getName().title".
    • getI18nKeyDescription

      default String getI18nKeyDescription()
      Returns:
      The i18n key for the description of this plugin. Defaults to "getName().desc".
    • getI18nKeyDisplayName

      default String getI18nKeyDisplayName()
      Returns:
      The i18n key for the display name of this plugin, as shown in the plugin configuration backend menu. Defaults to "getName().name".
    • getMainCategory

      default IElementCategory getMainCategory(IGetElementPrototypesParams params)
      Specified by:
      getMainCategory in interface ISingleElementPrototype<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      params - Parameters with the current client, locale, entity context etc.
      Returns:
      IElementCategory element's main category, which is used for grouping the elements.
    • getMainPluginBeanClass

      default Class<? extends INodePropertyPluginBean<TData>> getMainPluginBeanClass()
      Returns:
      The class of the bean that should be made available in the getPropertiesViewXhtml() page. When this returns null, no bean is made available This is usually sufficient for most simple use cases, see IElementHandler.getPropertiesViewXhtml() for further details.
    • getName

      String getName()
      Description copied from interface: IFCPlugin
      Getter for the name of this plugin. This name may appear on the user interface.
      Specified by:
      getName in interface IFCPlugin
      Specified by:
      getName in interface INameProviding
      Returns:
      String The name of this plugin.
    • getNodeHandler

      default INodeHandler<TData> getNodeHandler()
      Specified by:
      getNodeHandler in interface IPluginWorkflowNode
      Returns:
      The node handler that implements all the logic required by the workflow node. This lets you customize all features available to built-in workflow nodes.
    • getPluginInitializeData

      IPluginInitializeData getPluginInitializeData()
      Returns:
      The IPluginInitializeData, as they were passed to IFCPlugin.initialize(IPluginInitializeData). Implementation should usually just store the data in a private volatile field and return it in this method.
    • getPropertiesBeanClass

      default Class<? extends INodePropertiesBean<TData>> getPropertiesBeanClass()
      Description copied from interface: IElementHandler
      Returns the class of the bean that should be used when editing the properties of a workflow element. May be null if you do not required any bean or custom logic. When you only wish to access the properties of your IElementHandler.getDataModelClass(), you do have to use a custom bean - the model is available via the expression language variable model. See IElementHandler.getPropertiesViewXhtml() for further details.

      The default returns null, which uses no extra bean. An extra bean may not be required for simple UIs if you only need to access the properties model of the workflow element - see IElementHandler.getPropertiesViewXhtml().

      Specified by:
      getPropertiesBeanClass in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Specified by:
      getPropertiesBeanClass in interface INodeHandler<TData extends BaseActionProps>
      Returns:
      The class of the bean to use for editing a workflow node's properties.
      See Also:
    • getPropertiesViewXhtml

      default URL getPropertiesViewXhtml() throws MalformedURLException
      This default implementation returns the XHTML page located at getPropertiesViewXhtmlPath()/getPropertiesViewXhtmlName().
      Specified by:
      getPropertiesViewXhtml in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Returns:
      Path to the XHTML view. If the action can be configured, this must not return null. If you do return null, it will be treated as an error and an appropriate message is displayed to the user informing them that the properties panel could not be loaded. If the action cannot be configured (i.e. when the action is not selectable), this should return null.
      Throws:
      MalformedURLException - This exception is declared for convenience - normally you would use new URL(String) with a constant URL string that should not throw. In case an exception is thrown, it is treated the same as if this returned null.
      See Also:
    • getPropertiesViewXhtmlName

      String getPropertiesViewXhtmlName()
      Returns:
      The file name (without the path) to the XHTML page with the custom UI. The file extension .xhtml is added automatically if the returned name does not contain it already.
      See Also:
    • getPropertiesViewXhtmlPath

      default String getPropertiesViewXhtmlPath()
      Returns:
      The path (without the file name) to the XHTML page with the custom UI. Defaults to WEB-INF/ui. That is, you should put the file in src/main/resources/WEB-INF/ui.
      See Also:
    • getPrototypeIcon

      default IGuiIcon getPrototypeIcon(IGetElementPrototypesParams params)
      This default implementation returns an icon resembling a jigsaw puzzle piece, which is the default icon for an action plugin.
      Specified by:
      getPrototypeIcon in interface ISingleElementPrototype<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      params - Parameters with the current client, locale, entity context etc.
      Returns:
      The icon for the element prototype, shown in the element drawer panel to the left of the workflow designer. See IElementPrototypeDescriptor.getIcon().
      See Also:
    • getPrototypeLabel

      default String getPrototypeLabel(IGetElementPrototypesParams params)
      This default implementation returns the localized value of the getI18nKeyActionLabel() in the getResourceBundle(Locale). If no localized value exists for that key, defaults the getName() of this plugin.
      Specified by:
      getPrototypeLabel in interface ISingleElementPrototype<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      params - Parameters with the current client, locale, entity context etc.
      Returns:
      The icon for the element prototype, shown in the element drawer panel to the left of the workflow designer. See IElementPrototypeDescriptor.getLabel().
      See Also:
    • getPrototypeSearchText

      default String getPrototypeSearchText(IGetElementPrototypesParams params)
      This default implementation returns the localized value of the getI18nKeyActionSearchText() in the getResourceBundle(Locale). If no localized value exists for that key, no title is used.
      Specified by:
      getPrototypeSearchText in interface ISingleElementPrototype<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      params - Parameters with the current client, locale, entity context etc.
      Returns:
      The search text for the element prototype, never shown to the user but used for filtering. See IElementPrototypeDescriptor.getSearchText().
      See Also:
    • getPrototypeSubLabel

      default String getPrototypeSubLabel(IGetElementPrototypesParams params)
      This default implementation returns the localized value of the getI18nKeyActionSubLabel() in the getResourceBundle(Locale). If no localized value exists for that key, no sub label is used.
      Specified by:
      getPrototypeSubLabel in interface ISingleElementPrototype<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      params - Parameters with the current client, locale, entity context etc.
      Returns:
      The sub label for the element prototype, shown in the element drawer panel to the left of the workflow designer. See IElementPrototypeDescriptor.getLabel().
      See Also:
    • getPrototypeTitle

      default String getPrototypeTitle(IGetElementPrototypesParams params)
      This default implementation returns the localized value of the getI18nKeyActionTitle() in the getResourceBundle(Locale). If no localized value exists for that key, no title is used.
      Specified by:
      getPrototypeTitle in interface ISingleElementPrototype<TData extends BaseActionProps,WorkflowNode>
      Parameters:
      params - Parameters with the current client, locale, entity context etc.
      Returns:
      The title for the element prototype, shown in the element drawer panel to the left of the workflow designer. See IElementPrototypeDescriptor.getLabel().
      See Also:
    • getResourceBundle

      default ResourceBundle getResourceBundle(Locale locale)
      This default implementation attempts to read the resource bundle from the getResourceBundlePath(). In most cases this is fine and does not have to be overridden, although you may want to override the getResourceBundlePath().
      Specified by:
      getResourceBundle in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Specified by:
      getResourceBundle in interface IResourceBundleLocator
      Parameters:
      locale - A locale, for which a resource bundle shall be retrieved. Must not be null.
      Returns:
      The resource bundle to use for localized messages.
      See Also:
    • getResourceBundlePath

      default String getResourceBundlePath()
      Returns:
      The path to the resource bundle with the localized message for this plugin. Defaults to WEB-INF/properties/i18n. That is, you should create the properties files src/main/resources/WEB-INF/properties/i18n_en.properties (for English), src/main/resources/WEB-INF/properties/i18n_de.properties (for German), etc.
    • getSoftErrorValueDescriptor

      default IUnionValueDescriptor<String> getSoftErrorValueDescriptor(IValueDescriptorFactory factory)
      This default implementation returns the error codes from the getSoftErrorCodeClass(), if any. No error data is made available for the error codes. When the action can have soft errors with error data, override this method and use IValueDescriptorFactory.unionStringBuilder() to add the soft errors.
       public IUnionValueDescriptor<String> getSoftErrorValueDescriptor(IValueDescriptorFactory f) {
         return f.recordBuilder() //
           .add("FILE_NOT_FOUND", f.recordBuilder().requiredProperty("file", f.string("")).build()) //
           .add("NETWORK_ERROR", f.recordBuilder().requiredProperty("url", f.string("")).build()) //
           .build();
       
      Note that it is recommended that you keep the avaiable soft error codes in an enum, so you can reuse them later.

      In your execute(INodeExecutionParams) method, you can then add a soft error when necessary, which you can do like this:

       public INormalCompletionResult execute(INodeExecutionParams<TData> params) throws AbstractAbruptCompletionException {
         try {
           // Run business logic...
         }
         catch (final IOException e) {
           // Add the soft error
           final var errorData = new HashMap<String, Object>();
           errorData.put("file", fileName);
           params.softError("FILE_NOT_FOUND", e, b -> b.value(errorData))
         }
         return params.normalResult().success(result).build();
       }
       

      Instead of the factory passed in to this method, you may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field. *

      Specified by:
      getSoftErrorValueDescriptor in interface IExecutionResultDescriptor
      Parameters:
      factory - Factory that may be used for creating the descriptor. You may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field.
      Returns:
      The value descriptor for the data that is made available to the workflow when the execution of the node was successful, but contains one or more soft errors.
      See Also:
    • getSuccessValueDescriptor

      default IValueDescriptor<?,? extends IValueBuilder<?>> getSuccessValueDescriptor(IValueDescriptorFactory factory)
      This default implementation returns a void value descriptor. This is appropriate when this plugin does not need to return any values. If you wish to return values (and make them available to other action via variables / placeholders), override this method. If you do, make sure you also modify execute(INodeExecutionParams) and supply the appropriate values upon execution.

      Recommended best practice is to use a IRecordValueDescriptor, with an entry for each value that may be returned. This also allows you to add additional entries later on. For example, to indicate that your action may return a status code (default value 0) and a status message (default value <empty>), you can use:

       public IValueDescriptor<?, ? extends IValueBuilder<?>> getSuccessValueDescriptor(IValueDescriptorFactory f) {
         return f.recordBuilder() //
           .requiredProperty("statusCode", f.integer(0L)) //
           .requiredProperty("statusMessage", f.string("")) //
           .build();
       

      In your execute(INodeExecutionParams) method, you then need to supply a value for the status code and message, which you can do like this:

       public INormalCompletionResult execute(INodeExecutionParams<TData> params) throws AbstractAbruptCompletionException {
         // Run business logic and get status code and message
         final Map<String, Object> result = new HashMap<>();
         result.put("statusCode", 200);
         result.put("statusMessage", "OK");
         // Create a success result with the data
         return params.normalResult().success(result).build();
       }
       

      Instead of the factory passed in to this method, you may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field. *

      Specified by:
      getSuccessValueDescriptor in interface IExecutionResultDescriptor
      Parameters:
      factory - Factory that may be used for creating the descriptor. Instead of the factory passed in to this method, you may also use ValueDescriptorFactory.getInstance() if you wish to cache the descriptor in a static or instance field.
      Returns:
      The value descriptor for that data that is made available to the workflow when the node is executed successfully.
      See Also:
    • getType

      default String getType()
      This default implementation simply returns the getName() of the plugin as the type. Make sure that the name is unique. It is recommended you hardcode the name / type, as any change will result in broken backwards compatibility. In particular we advice against using Class.getCanonicalName() etc., as a refactoring would then break your plugin.
      Specified by:
      getType in interface IWorkflowElementTypeProviding
      Returns:
      The type of the workflow element that determines how the workflow element behaves. Usually there is a registered handler for each type.
    • getUnmanagedBeans

      default Iterable<Class<? extends IPluginWorkflowNodeBean>> getUnmanagedBeans()
      Description copied from interface: IPluginGenericCustomGUI
      This must return a list of backing bean classes that control the user interface and are required by the XHTML Facelet view. A new instance of the bean will be created automatically when the view is opened. Make sure each bean has got a no-argument constructor or instantiation will fail.

      Each bean should be annotated with Named. If this annotation is not present or no name is specified, the name defaults to the simple name of the bean class, with the first character changed to lower case.

      Also, each bean needs to be annotated one of the following scopes: @RequestScoped, @ViewScoped, @SessionScoped, or @ApplicationScoped. Note that it depends on the type of plugin which scopes are actually supported (most plugins expect @ViewScoped beans). In case you do not specify a scope, an appropriate scope will be determined automatically.

      Please note that the beans are fundamentally unmanaged - functionality specific to managed CDI beans may not be supported, depending on the type of plugin. Certain features of CDI managed beans may be supported partially, depending on the type of plugin, but may work slightly differently. This includes, but is not limited to:

      • The exact timing at which PostConstruct and PreDestroy are called may differ.
      • A field marked with Inject may not work with all values allowed by the CDI specification, and may not perform certain validation passes such as the check for circular dependencies. Also, no new bean instances will be created when those beans have not yet been created as part of the current page.
      Specified by:
      getUnmanagedBeans in interface IPluginGenericCustomGUI<TData extends BaseActionProps>
      Returns:
      A list of unmanaged bean classes required by the Facelet page.
    • getVersion

      default String getVersion()
      Instead of the formcycle version, this default implementation returns the current version of this plugin. The version is read from the MANIFEST.MF file in the plugin JAR file. Make sure that the manifest contains an appropriate version entry, e.g.:
       Implementation-Version = 3.1.4
       
      We recommend that you use version numbers that conform to the format and semantics as laid out by SemVer (semantic version).
      Specified by:
      getVersion in interface IElementHandler<TData extends BaseActionProps,WorkflowNode>
      Returns:
      The version of this plugin.
      See Also: