Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

See detailed instructions below.

Table of Contents

...

minLevel1
maxLevel3
outlinefalse
styledefault
typelist
printablefalse

Development Approach

  • Decide on implementation type - Java or Docker, depending on your expertise.

  • Define plugin.xml and properties.xml files. This should help you describe operations that will be developed and project or environment/instance level properties used by each operation. You can also define plugin operation inputs as appropriate. At times, you can just use inputs without properties if it makes sense.

  • Developer plugin implementation.

  • Unit test plugin operations.

  • Package and verify on FlexDeploy server.

...

Each plugin operation (e.g. build, deploy, start, stop) is implemented by extending AbstractPluginProvider.  

Apache HTTP Server - AbstractPluginProvider implementation - build operation

Code Block
languagejava
public class Build
  extends AbstractPluginProvider
{
  private static final String CLZ_NAM = Build.class.getName();
  private static final FlexLogger LOG = FlexLogger.getLogger(CLZ_NAM);

  public Build()
  {
    super();
  }

  @Override
  public void validate()
    throws FlexCheckedException
  {
    //TODO
  }

  @Override
  public PluginResult execute()
    throws FlexCheckedException
  {
    //TODO
  }

  @Override
  public void cleanup()
  {
    //TODO
  }
}

...

The validate method is responsible for validating that prerequisites are satisfied prior to performing the execution. As you will see later, the XML metadata identifies basic prerequisites (e.g. required fields) that will be enforced by the plugin framework.  However, there may be more advanced validation which can be implemented within this method.

Apache HTTP Server - build operation
Code Block
languagejava
  @Override
  public void validate()
    throws FlexCheckedException
  {
    String methodName = "validate";
    LOG.logInfoEntering(methodName);
    //Let's validate that if a relative path is given, it exists on the file system.
    String relativePath = getRelativePathToFiles();
    if (relativePath != "")
    {
      File file = new File(getWorkflowExecutionContext().getTempDirectory() + relativePath);
      if (!file.exists())
      {
        throw new FlexCheckedException(ApacheHttpProperties.FDAH_00001_INVALID_PATH, "The given relative path, [" + relativePath + "] was not found in the FD_TEMP_DIR, or was not a relative path.");
      }
    }
    LOG.logInfoExiting(methodName);
  }

The execute method is responsible for performing the actual execution of the plugin operation.

Apache HTTP Server - build operation

Code Block
languagejava
  @Override
  public PluginResult execute()
    throws FlexCheckedException
  {
    String methodName = "execute";
    LOG.logInfoEntering(methodName);
    String source = getWorkflowExecutionContext().getTempDirectory() + getRelativePathToFiles();
    String destination = getWorkflowExecutionContext().getArtifactsDirectory() + File.separator + ApacheHttpProperties.ARTIFACTS_ZIP;

    LOG.logInfo("Zipping {0} into an artifact at (1}.", source, destination);
    int zipped = new FlexZipUtils(source, new File(destination)).zipFolder();
    getWorkflowExecutionContext().getOutputMap().put(ApacheHttpProperties.FDAH_FILE_COUNT, new PropertyValue(zipped, PropertyValue.PropertyTypeEnum.Integer, false));
    LOG.logInfo(methodName, "Done!");

    LOG.logInfoExiting(methodName);
    return PluginResult.createPluginResult(getWorkflowExecutionContext());
}

...

The Workflow Execution Context provides information related to the current execution.  This includes properties, inputs, the endpoint working directory (and its sub-directories), and other execution related information.  The following table summarizes the working directory sub-directories and their purpose.

Directory

WorkflowExecutionContext Method

Physical Endpoint Location

Description

Working Directory

getWorkingDirectory()

<EndPoint Base Directory>/work/<Project Id>/<Workflow Request Id>

The temporary working area defined for the target endpoint.

Artifacts Directory

getArtifactsDirectory()

<Working Directory>/artifacts

Build operations which produce artifacts place those files here to have them automatically transferred back to the FlexDeploy server and version in the artifact repository. Deploy operations which consume artifacts will have those files delivered to this directory prior to execution on the endpoint.

Temp Directory

getTempDirectory()

<Working Directory>/temp

Temporary working area for a plugin operation execution.

Internal Directory

getInternalDirectroy()

<Working Directory>/internal

Internal working area for a plugin operation execution, which is not to be acessed or written to by workflow developers.

Reports Directory

getReportsDirectory()

<Working Directory>/reports

Files placed in the reports directory will be automatically be transferred back to the FlexDeploy server.

They are displayed in the execution summary.

Transfer Directory

getTransferDirectory()

<Working Directory>/transfer

Files in the transer directory are automatically transferred back to the FlexDeploy server upon completion of execution, and likewise are transferred to the endpoint for the next workflow step. This provides a mechanism for sharing files between steps in a workflow.

Test Results Directory

getTestResultsDirectory()

<Working Directory>/test-results

Testing framework plugin executions place an XML document representing the test results into this directory, and they will be automatically be transferred back to the FlexDeploy server.

See the xsd for formatting information.

View file
nameTestSuiteResults.xsd

Results are displayed in the execution results pages, insights, reporting, and other locations.

Scan Results Directory

getScanResultsDirectory()

<Working Directory>/scan-results

Scanning framework plugin executions place an XML document representing the scan results into this directory, and they will be automatically be transferred back to the FlexDeploy server.

See the xsd for formatting information.

View file
nameScanResult.xsd

Results are displayed in the execution results pages, insights, reporting, and other locations.

Object Results Directory

getObjectResultsDirectory()

<Working Directory>/object-results

Build and Deployment plugin operations for partial deployment implementations place XML documents into this directory for identifying the objects included in the build, and their execution status at deployment time.

The AbstractPluginProvider base class has a number of methods for retrieving inputs/properties by type, based on scope, and returning a default if it is not defined.

See https://flexagon.com/plugin-sdk/8.0/flexagon/fd/core/plugin/AbstractPluginProvider.html for a current list of methods that can be used to access the properties and values that you need.

Logging

Logging can be performed in plugin implementations by using the FlexDeployLogger class.  The logger is statically initialized using the following two lines of code.  Note that in this example Build would be replaced by the actual class name of the current class.

FlexDeployLogger initialization

Code Block
languagejava
  private static final String CLZ_NAM = Build.class.getName();
  private static final FlexLogger LOG = FlexLogger.getLogger(CLZ_NAM);

Log messages can be added using any of the public methods available on FlexDeployLogger.

Logging methods

See https://flexagon.com/plugin-sdk/8.0/flexagon/ff/common/core/logging/FlexLogger.html for a current list of methods that can be used.

...

An example of plugin.xml file for FlexagonADFBuilderPlugin:

Oracle ADF plugin - plugin.xml

Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
<PluginDefinition xmlns="http://flexagon.com/deploy/plugin">
  <Name>FlexagonADFBuilderPlugin</Name>
  <PluginDisplayName>Oracle ADF Builder plugin</PluginDisplayName>
  <Description>A plugin to build ADF application and projects.</Description>
  <TechnologyGroup>Build</TechnologyGroup>
  <SubTechnologyGroup>ADF</SubTechnologyGroup>
  <MaxConcurrentThreads>3</MaxConcurrentThreads>
  <Type>docker</Type>    
  <ImageName>flexdeploy/plugin-jdeveloper</ImageName>    
  <Vendor>Flexagon</Vendor>
  <Version>5.0.1.8</Version>
  <Operations>
    <Operation>
      <Name>applicationBuild</Name>
      <Description>Build JDeveloper application using Deployment Profile</Description>
      <Target>applicationBuild</Target>
      <ProducesArtifacts>true</ProducesArtifacts>
      <ConsumesArtifacts>false</ConsumesArtifacts>
      <Outputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Outputs>
      <Inputs>
        <AllowsUserDefined>false</AllowsUserDefined>
        <Input>
          <Name>FDJDEV_INP_APP_SOURCE_FOLDER</Name>
          <DisplayName>Application Source Folder</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify folder where source is checked out. (Leave it blank if you did not use any sub-folder to checkout/export source)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <!-- if not specified, use same as jws name -->
          <Name>FDJDEV_INP_APP_DEPLOY_PROFILE</Name>
          <DisplayName>Application Deployment Profile</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify application deployment profile. (Default would be name of .jws file)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <!-- if not specified, use deploy -->
          <Name>FDJDEV_INP_APP_ARTIFACT_FOLDER</Name>
          <DisplayName>Application Artifacts Folder</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify relative folder where .ear file will be generated. (Default would be deploy).</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <!-- if not specified, use false -->
          <Name>FDJDEV_INP_APP_SYNC_JDBC_RESOURCES</Name>
          <DisplayName>Synchronize JDBC Resources</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>Boolean</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify if EAR file should have generated weblogic jdbc xml files. (Default would be false, so by default EAR file will not have any weblogic jdbc xml files).</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <Name>FDJDEV_INP_OVERRIDE_FILE_PATTERN</Name>
          <DisplayName>Override File Pattern(s)</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify FileName=OverrideFileName pattern. Matching files will be replaced by override files and Property replacement logic will be executed. For example, web.xml=fd-web.xml.</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <Name>FDJDEV_INP_SAVE_ARTIFACTS_EXTENSIONS</Name>
          <DisplayName>Save Artifacts with Extension(s)</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Extensions to be copied to artifacts after build. For example ear, war, jar, mar etc. (Defaults to ear)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
          <ListData>ear,war,jar,mar,aar</ListData>
          <IsMultiselect>true</IsMultiselect>
          <DefaultValue>ear</DefaultValue>
        </Input>
      </Inputs>
      <EndPointSpecification>
        <Selection>
          <All/>
        </Selection>
        <Execution>
          <Any/>
        </Execution>
      </EndPointSpecification>
    </Operation>
    <Operation>
      <Name>projectBuild</Name>
      <Description>Build ADF project(s) using Deployment Profile</Description>
      <Target>projectBuild</Target>
      <ProducesArtifacts>true</ProducesArtifacts>
      <ConsumesArtifacts>false</ConsumesArtifacts>
      <Outputs>
        <AllowsUserDefined>false</AllowsUserDefined>          
      </Outputs>
      <Inputs>
        <AllowsUserDefined>false</AllowsUserDefined>
        <Input>
          <Name>FDJDEV_INP_APP_SOURCE_FOLDER</Name>
          <DisplayName>Application(JDev) Source Folder</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify folder where source is checked out. (Leave it blank if you did not use any sub-folder to checkout/export source)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <!-- if not specified, use * -->
          <Name>FDJDEV_INP_PROJECT_NAME</Name>
          <DisplayName>JDeveloper Project Name</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify Project name to build. (Leave it blank if you want to build all projects)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <!-- if not specified, use * -->
          <Name>FDJDEV_INP_PROJECT_DEPLOY_PROFILE</Name>
          <DisplayName>Project Deployment Profile</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify project deployment profile. (Leave it blank if you want to run all deployment profiles)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <Name>FDJDEV_INP_SAVE_ARTIFACTS_EXTENSIONS</Name>
          <DisplayName>Save Artifacts with Extension(s)</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Extensions to be copied to artifacts after build. For example ear, war, jar, mar etc. (Optional)</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
          <ListData>ear,war,jar,mar,aar</ListData>
          <IsMultiselect>true</IsMultiselect>
        </Input>
        <Input>
          <!-- if not specified, use deploy -->
          <Name>FDJDEV_INP_APP_ARTIFACT_FOLDER</Name>
          <DisplayName>Application Artifacts Folder</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify relative folder where .war or .jar files will be generated. (Default is deploy).</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
        <Input>
          <Name>FDJDEV_INP_OVERRIDE_FILE_PATTERN</Name>
          <DisplayName>Override File Pattern(s)</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <DataType>String</DataType>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>100</DisplayColumns>
          <Description>Specify FileName=OverrideFileName pattern. Matching files will be replaced by override files and Property Replacement logic will be executed. For example, web.xml=fd-web.xml.</Description>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
        </Input>
      </Inputs>
      <EndPointSpecification>
        <Selection>
          <All/>
        </Selection>
        <Execution>
          <Any/>
        </Execution>
      </EndPointSpecification>
    </Operation>
  </Operations>
</PluginDefinition>

While executing a plugin operation on a Docker host, FlexDeploy runs the following command on the target endpoint:

Docker command structure

Code Block
languagebash
docker run -rm --env-file env.list --name STEP_NAME_WF_EXEC_ID  -v ${FD_WORKING_DIR}:/fd_working_dir FULL_IMAGE_NAME:PLUGIN_VERSION FD_PLUGIN_OPERATION_TARGET

While executing a plugin operation on a Kubernetes cluster, FlexDeploy runs the following commands

Kubectl command structure

Code Block
languagebash
kubectl create -f pod.json
kubectl cp ${FD_WORKING_DIR} ${INTERNAL_POD_NAME}:/fd_working_dir
kubectl exec -it ${INTERNAL_POD_NAME} -- ${FD_PLUGIN_OPERATION_TARGET}
kubectl cp ${INTERNAL_POD_NAME}:/fd_working_dir ${FD_WORKING_DIR}
kubectl delete pod ${INTERNAL_POD_NAME}

...

In order to return outputs the container must create a properties file /fd_working_dir/${FD_RESULT_FILE_NAME} containing output values. For example:

Example of result.dck file

Code Block
languagebash
FDJDEV_OUT_EAR_FILE_NAME=application.ear
FDJDEV_OUT_IS_DOCUMENTING_BORING=YES

...

The plugin XML metadata (plugin.xml) defines information about the plugin, its operations, and its behavior.  This XML file is bundled with the plugin Java class files and is used to integrate into the FlexDeploy platform. 

plugin XML metadata schema (XSD)

Code Block
languagexml
<xsd:schema targetNamespace="http://flexagon.com/deploy/plugin" xmlns="http://flexagon.com/deploy/plugin" xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified"
            elementFormDefault="qualified">
  <xsd:element name="PluginDefinition">
    <xsd:complexType>
      <xsd:all>
        <xsd:element type="xsd:string" name="Name"/>
        <xsd:element type="xsd:string" name="PluginDisplayName" minOccurs="0" maxOccurs="1"/>
        <xsd:element type="xsd:string" name="Description"/>
        <xsd:element type="xsd:string" name="TechnologyGroup"/>
        <xsd:element type="xsd:string" name="SubTechnologyGroup"/>
        <xsd:element type="xsd:string" name="Vendor"/>
        <xsd:element type="PluginTypeEnum" name="Type" minOccurs="0" maxOccurs="1"/>
        <xsd:element type="xsd:string" name="ImageName" minOccurs="0" maxOccurs="1"/>
        <xsd:element type="xsd:int" name="MaxConcurrentThreads"/>
        <xsd:element type="xsd:string" name="Version"/>
        <xsd:element name="ResourceTypes" minOccurs="0" maxOccurs="1">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element type="xsd:string" name="Resource" maxOccurs="unbounded" minOccurs="1"/>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="Operations">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="Operation" maxOccurs="unbounded" minOccurs="1">
                <xsd:complexType>
                  <xsd:all>
                    <xsd:element type="xsd:string" name="Name"/>
                    <xsd:element type="xsd:string" name="Description"/>
                    <xsd:element type="xsd:string" name="Target"/>
                    <xsd:element name="PropertyKeys">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element type="xsd:string" name="PropertyKey" maxOccurs="unbounded" minOccurs="0"/>
                        </xsd:sequence>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element name="Inputs">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element type="xsd:boolean" name="AllowsUserDefined" maxOccurs="1" minOccurs="1"/>
                          <xsd:element name="Input" maxOccurs="unbounded" minOccurs="0">
                            <xsd:complexType>
                              <xsd:all>
                                <xsd:element type="xsd:string" name="Name" maxOccurs="1" minOccurs="1"/>
                                <xsd:element type="InputDataTypeEnum" name="DataType" maxOccurs="1" minOccurs="1"/>
                                <xsd:element type="xsd:string" name="Description" maxOccurs="1" minOccurs="1"/>
                                <xsd:element type="xsd:string" name="DisplayName" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="DefaultValue" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="IsDefaultValueExpression" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="SubDataType" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="IsRequired" maxOccurs="1" minOccurs="1"/>
                                <xsd:element type="xsd:string" name="IsEncrypted" maxOccurs="1" minOccurs="1"/>
                                <xsd:element type="xsd:long" name="MinValue" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:long" name="MaxValue" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="ListData" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:int" name="DisplayRows" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:int" name="DisplayColumns" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:int" name="LengthPrecision" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="ValidatorScript" maxOccurs="1" minOccurs="0"/>
                                <xsd:element type="xsd:string" name="IsMultiselect" maxOccurs="1" minOccurs="0"/>
                              </xsd:all>
                            </xsd:complexType>
                          </xsd:element>
                        </xsd:sequence>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element name="Outputs">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element type="xsd:boolean" name="AllowsUserDefined" maxOccurs="1" minOccurs="1"/>
                          <xsd:element type="xsd:string" name="Output" maxOccurs="unbounded" minOccurs="0"/>
                        </xsd:sequence>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element name="EndPointSpecification">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element name="Selection">
                            <xsd:complexType>
                              <xsd:choice>
                                <xsd:element type="xsd:string" name="All"/>
                                <xsd:element type="xsd:string" name="Resource"/>
                                <xsd:element type="xsd:string" name="Delegated"/>
                              </xsd:choice>
                            </xsd:complexType>
                          </xsd:element>
                          <xsd:element name="Execution">
                            <xsd:complexType>
                              <xsd:choice>
                                <xsd:element type="xsd:string" name="Any"/>
                                <xsd:element type="xsd:string" name="All"/>
                                <xsd:element type="xsd:string" name="Delegated"/>
                              </xsd:choice>
                            </xsd:complexType>
                          </xsd:element>
                        </xsd:sequence>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element type="ConsumesArtifactsEnum" name="ConsumesArtifacts"/>
                    <xsd:element type="ProducesArtifactsEnum" name="ProducesArtifacts"/>
                  </xsd:all>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:all>
    </xsd:complexType>
  </xsd:element>
  <xsd:simpleType name="ConsumesArtifactsEnum">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="true"/>
      <xsd:enumeration value="false"/>
      <xsd:enumeration value="DELEGATED"/>
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:simpleType name="ProducesArtifactsEnum">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="true"/>
      <xsd:enumeration value="false"/>
      <xsd:enumeration value="DELEGATED"/>
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:simpleType name="InputDataTypeEnum">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="Boolean"/>
      <xsd:enumeration value="Double"/>
      <xsd:enumeration value="Integer"/>
      <xsd:enumeration value="String"/>
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:simpleType name="PluginTypeEnum">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="java"/>
      <xsd:enumeration value="docker"/>
      <xsd:enumeration value="function"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:schema>

Details of various elements is described in table below.

Element

Path

Description

PluginDefinition

/

The root element for this plugin definition.

Name

/PluginDefinition

The name of the plugin, which appears on plugins page after upload.

PluginDisplayName

/PluginDefinition

The display name of the plugin, which appears on workflow page under workflow operations.

Description

/PluginDefinition

A description for this plugin, which is displayed on the plugins page after upload.

TechnologyGroup

/PluginDefinition

The name of the technology that is plugin is for, which is displayed on the plugins page after upload.

SubTechnologyGroup

/PluginDefinition

The name of the sub-technology that is plugin is for, which is displayed on the plugins page after upload.

Vendor

/PluginDefinition

The name of the vendor which authored the plugin.

Type

/PluginDefinition

Plugin type. Supported values are java and docker. Optional. Default is java.

ImageName

/PluginDefinition

Full name of Docker image

MaxConfurrentThreads

/PluginDefinition

The number of concurrent threads which are allowed to execute plugin operations on any single endpoint. Default is -1, which means unlimited.

Version

/PluginDefinition

The version of the plugin.

ResourceTypes

/PluginDefinition

A list of server/component types which are applicable for the underlying technology. This helps identify the endpoint to run a particular operation on. (e.g. for WebLogic - NodeManager, AdminServer, ManagedServer).

Resource

/PluginDefinition/ResourceTypes

A server/component types which is applicable for the underlying technology (e.g. NodeManager).

Operations

/PluginDefinition/

A list of operations supported by this plugin.

Operation

/PluginDefinition/Operations

An operation supported by this plugin.

Name

/PluginDefinition/Operations/Operation

The name of the plugin operation.

Description

/PluginDefinition/Operations/Operation

A description for the plugin operation.

Target

/PluginDefinition/Operations/Operation

For Java, the package qualified name of the Java class which implements PluginProvider or extends AbstractPluginProvider.  For Docker, the command passed to image when the container is started.

PropertyKeys

/PluginDefinition/Operations/Operation

The list of properties used by this plugin operation. See Property Definitions section for details.

PropertyKey

/PluginDefinition/Operations/Operation/PropertyKeys

A reference to a property used by this plugin operation. See Property Definitions section for details.

Inputs

/PluginDefinition/Operations/Operation

A list of inputs used by this plugin operation.

AllowsUserDefined

/PluginDefinition/Operations/Operation/Inputs

Whether or not this plugin operation allows user defined inputs. User defined inputs are useful for low-level technology plugins (e.g. shell) where the user is essentially providing the implementation via source or scripting code.

Input

/PluginDefinition/Operations/Operation/Inputs

An input definition for this plugin operation.

Name

/PluginDefinition/Operations/Operation/Inputs/Input

The name of the input. Must be alpha-numeric, and be free of whitespace or special characters (except underscore). This name may be used in shell scripts or Groovy scripts and therefore must adhere to these variable naming standards.

DataType

/PluginDefinition/Operations/Operation/Inputs/Input

The data type of the input. Must be one of Boolean, Double, Integer, String.

Description

/PluginDefinition/Operations/Operation/Inputs/Input

The description of the plugin input, which is displayed on the plugin step editor of the workflow.

DisplayName

/PluginDefinition/Operations/Operation/Inputs/Input

The display name of the plugin input, which is displayed on the plugin step editor of the workflow.

DefaultValue

/PluginDefinition/Operations/Operation/Inputs/Input

The default value for the plugin input if no value is specified by the user. This value will also be displayed by default on the plugin step editor of the workflow.

IsDefaultValueExpression

/PluginDefinition/Operations/Operation/Inputs/Input

Identifies whether the default value for this input is an expression or a literal value, if specified.

SubDataType

/PluginDefinition/Operations/Operation/Inputs/Input

An optional sub-datatype for this input. Valid options are DIRECTORY, JDBCURL, and URL. Setting the SubDataType adds additional validation when user enters value.

IsRequired

/PluginDefinition/Operations/Operation/Inputs/Input

Indicates whether this input is required or not. Required inputs are enforced by the plugin step editor and at runtime.

IsEncrypted

/PluginDefinition/Operations/Operation/Inputs/Input

Indicates whether the input is encrypted or not. Encrypted values are masked on the editor, and their values are not displayed in log files.

MinValue

/PluginDefinition/Operations/Operation/Inputs/Input

Identifies the minimum required value for this input, and is enforced by the plugin step editor and at runtime. This value is only applicable for Integer and Double input types.

MaxValue

/PluginDefinition/Operations/Operation/Inputs/Input

Identifies the maximum required value for this input, and is enforced by the plugin step editor and at runtime. This value is only applicable for Integer and Double input types.

ListData

/PluginDefinition/Operations/Operation/Inputs/Input

Provides a list of values for this input which may be selected by the user on the plugin step editor. When ListData is provided the plugin step editor will render the value using a drop down component.

DisplayRows

/PluginDefinition/Operations/Operation/Inputs/Input

Defines the height of the input field on the plugin step editor.

DisplayColumns

/PluginDefinition/Operations/Operation/Inputs/Input

Defines the length of the input field on the plugin step editor.

LengthPrecision

/PluginDefinition/Operations/Operation/Inputs/Input

Defines the required input length. Only applies to String datatype.

ValidatorScript

/PluginDefinition/Operations/Operation/Inputs/Input

An optional Groovy script which is executed to determine whether this input's value is valid. The Value bind variable is set to the current value of this input, while every other input is available by it's name. The script must return true if validation passes, and false otherwise. When returning false, the variable ValidationMessage should be set to the message to display at runtime when validation fails.

IsMultiselect

/PluginDefinition/Operations/Operation/Inputs/Input

If the ListData attribute is provided, the IsMultiselect identifies whether the user can select more than one value. As such, the input component for this input on the plugin step editor behaves accordingly.

Outputs

/PluginDefinition/Operations/Operation

A list of outputs returned by this plugin operation's execution.

AllowsUserDefined

/PluginDefinition/Operations/Operation/Outputs

Whether or not this plugin operation allows user defined outputs. User defined outputs are useful for low-level technology plugins (e.g. shell) where the user is essentially providing the implementation via source or scripting code.

Output

/PluginDefinition/Operations/Operation/Outputs

The name of the output returned from this plugin operation.

EndPointSpecification

/PluginDefinition/Operations/Operation

Defines the strategy used for determining the endpoint or endpoints on which a plugin operation will execute on.

Selection

/PluginDefinition/Operations/Operation/EndPointSpecification

Defines which endpoints will be seleted as candidates to execute on.

All

/PluginDefinition/Operations/Operation/EndPointSpecification/Selection

Indicates that all endpoints associated to the target should be selected as execution candidates.

Resource

/PluginDefinition/Operations/Operation/EndPointSpecification/Selection

Indicates that only endpoints having the given resource name should be selected as execution candidates.

Delegated

/PluginDefinition/Operations/Operation/EndPointSpecification/Selection

Indicates that the endpoint selection is delegated to the workflow developer (within the plugin step editor). In this case, the plugin step editor will present options of All and Resource.

Execution

/PluginDefinition/Operations/Operation/EndPointSpecification

Of the selected endpoint candidates, determines which one(s) to execute on.

Any

/PluginDefinition/Operations/Operation/EndPointSpecification/Execution

Indicates to execute on one of the selected endpoints (at random).

All

/PluginDefinition/Operations/Operation/EndPointSpecification/Execution

Indicates to execute on all of the selected endpoints.

Delegated

/PluginDefinition/Operations/Operation/EndPointSpecification/Execution

Indicates that execution strategy is delegated to the workflow developer (within the plugin step editor). In this case, the plugin step editor will present options of Any and All.

ConsumesArtifacts

/PluginDefinition/Operations/Operation

Identifies whether this plugin operation consumes an artifact. Valid values are true, false, DELEGATED. In the case of DELEGATED, the plugin step editor will present a checkbox for the workflow developer to determine. When a plugin operation consumes artifacts, the associated artifacts from the artifact repository are copied to the artifact directory within the working directory on the endpoint prior to execution.

ProducesArtifacts

/PluginDefinition/Operations/Operation

Identifies whether this plugin operation produces an artifact. Valid values are true, false, DELEGATED. In the case of DELEGATED, the plugin step editor will present a checkbox for the workflow developer to determine. When a plugin operation produces artifacts, the associated artifacts from the artifact directory on the endpoint are copied back to the server and stored in the artifact repository.

Properties Definition

The PropertyKeys in the plugin.xml file reference properties that are defined externally to the file itself.  This is done so that multiple plugin operations, and even multiple plugins, are allow to share properties.  The properties are defined in another XML file, conforming to a different schema, and are bundled with the plugin.  The schema, a description of its elements, and a sample XML file are provided below.

Plugin XML Properties Schema (XSD)

Code Block
languagexml
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema targetNamespace="http://flexagon.com/deploy/propertylist" xmlns="http://flexagon.com/deploy/propertylist" xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified"
            elementFormDefault="qualified">
  <xsd:element name="PropertyList">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element type="xsd:string" name="Name"/>
        <xsd:element name="Property" maxOccurs="unbounded" minOccurs="1">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element type="xsd:string" name="Name" maxOccurs="1" minOccurs="1"/>
              <xsd:element type="xsd:string" name="Description" maxOccurs="1" minOccurs="1"/>
              <xsd:element type="xsd:string" name="Scope" maxOccurs="1" minOccurs="1"/>
              <xsd:element type="xsd:string" name="DataType" maxOccurs="1" minOccurs="1"/>
              <xsd:element type="xsd:string" name="SubDataType" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:string" name="IsRequired" maxOccurs="1" minOccurs="1"/>
              <xsd:element type="xsd:string" name="IsEncrypted" maxOccurs="1" minOccurs="1"/>
              <xsd:element type="xsd:long" name="MinValue" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:long" name="MaxValue" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:string" name="ListData" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:int" name="DisplayRows" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:int" name="DisplayColumns" maxOccurs="1" minOccurs="0"/>
              <xsd:element name="Validators" maxOccurs="1" minOccurs="0">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element type="xsd:string" name="Validator" maxOccurs="5" minOccurs="0"/>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
              <xsd:element type="xsd:string" name="DisplayName" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:string" name="DefaultValue" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:string" name="IsDefaultValueExpression" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:string" name="IsAllowsVariant" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:int" name="LengthPrecision" maxOccurs="1" minOccurs="0"/>
              <xsd:element type="xsd:string" name="IsMultiselect" maxOccurs="1" minOccurs="0"/>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Details of various elements is described in table below.

Element

Path

XSD Line Number

Description

PropertyList

/

4

The root element for this properties definition.

Name

/PropertyList

7

The name of the properties definition.

Property

/PropertyList

8

A plugin property definition.

Name

/PropertyList/Property

11

The name of the property. Must be alpha-numeric, and be free of whitespace or special characters (except underscore). This name may be used in shell scripts or Groovy scripts and therefore must adhere to these variable naming standards.

Description

/PropertyList/Property

12

A description for the property.

Scope

/PropertyList/Property

13

The scope of the property. Must be one of PROJECT, ENVINST (target renamed from Environment Instance).

DataType

/PropertyList/Property

14

The data type of the property. Must be one of Boolean, Double, Integer, String.

SubDataType

/PropertyList/Property

15

An optional sub-datatype for this property when the DataType is String. Valid options are DIRECTORY, JDBCURL, and URL. Setting the SubDataType adds additional validation when user enters value.

IsRequired

/PropertyList/Property

16

Indicates whether this property is required or not. Required properties are enforced at runtime.

IsEncrypted

/PropertyList/Property

17

Indicates whether the property is encrypted or not. Encrypted values are masked when entered by the user, and their values are not displayed in log files.

MinValue

/PropertyList/Property

18

Identifies the minimum required value for this property, and is enforced at runtime. This value is only applicable for Integer and Double input types.

MaxValue

/PropertyList/Property

19

Identifies the maximum required value for this property, and is enforced at runtime. This value is only applicable for Integer and Double input types.

ListData

/PropertyList/Property

20

Provides a list of values for this property which may be selected by the user on the various properties pages. When ListData is provided the editor will render the value using a drop down component.

DisplayRows

/PropertyList/Property

21

Defines the height of the property field on the editor.

DisplayColumns

/PropertyList/Property

22

Defines the length of the property field on the editor.

Validators

/PropertyList/Property

23

RESERVED FOR FUTURE USE

Validator

/PropertyList/Property/Validators

26

RESERVED FOR FUTURE USE

DisplayName

/PropertyList/Property

30

The display name of the plugin property, which is displayed on the editor where property values are entered.

DefaultValue

/PropertyList/Property

31

The default value for the plugin property if no value is specified by the user. This value will also be displayed by default on the editor where values are entered.

IsDefaultValueExpression

/PropertyList/Property

32

Identifies whether the default value for this property is an expression or a literal value, if specified.

IsAllowsVariant

/PropertyList/Property

33

RESERVED FOR FUTURE USE

LengthPrecision

/PropertyList/Property

34

Defines the required property length. Only applies to String datatype.

IsMultiselect

/PropertyList/Property

35

If the ListData attribute is provided, the IsMultiselect identifies whether the user can select more than one value. As such, the input component for this property on the various editors behave accordingly.

Plugin Setup Scripts

A plugin is executed on an endpoint by a wrapper java program, which is launched by a wrapper shell script.  This wrapper shell script optionally sources in a setup script which is provided by the plugin.  This setup script may perform initialization activities for the plugin.  The following are examples of useful activities.

...

The following setup must be performed using the JUnit @BeforeClass annotated methed, which will create the necessary FlexDeploy folders:

JUnit - @BeforeClass

Code Block
languagejava
  @BeforeClass
  public static void prepTests()
    throws IOException
  {
    MockWorkflowExecutionContext context = new MockWorkflowExecutionContext();
    File file = new File(context.getTempDirectory() + File.separator + subFolder + File.separator + fileName1);
    file.getParentFile().mkdir();
    file.createNewFile();
  }

Next is the implementation of a test case.

JUnit - @Test

Code Block
languagejava
  @Test
  public void test()
    throws FlexCheckedException
  {
    MockWorkflowExecutionContext context = new MockWorkflowExecutionContext();
    clearFolders(context);
    AbstractPluginProvider plugin = new Build();
    plugin.setWorkflowExecutionContext(context);
    File file = new File(context.getTempDirectory() + File.separator + "TestFile1.txt");
    try
    {
      file.createNewFile();
    }
    catch (Exception e)
    {
      Assert.fail(e.getLocalizedMessage());
      e.printStackTrace();
    }
    plugin.validate();
    plugin.execute();
    Assert.assertEquals("The wrong number of files were in the zip file.", 2, plugin.getWorkflowExecutionContext().getOutputMap().get(ApacheHttpProperties.FDAH_FILE_COUNT).getValue());
    String outputPath = context.getWorkingDirectory() + File.separator + "unZipped";
    doDeploy(outputPath);
    //System.exit(6);
    File deployedFile = new File(outputPath + File.separator + fileName1);
    Assert.assertEquals("Deployed file in root folder is missing.", true, deployedFile.exists());
    deployedFile = new File(outputPath + File.separator + subFolder);
    Assert.assertEquals("subFolder is missing.", true, deployedFile.exists());
    deployedFile = new File(outputPath + File.separator + subFolder + File.separator + fileName1);
    Assert.assertEquals("File in subFolder is missing.", true, deployedFile.exists());
  }

  public void doDeploy(String outputPath)
    throws FlexCheckedException
  {
    Deploy plugin = new Deploy();
    MockWorkflowExecutionContext context = new MockWorkflowExecutionContext();

    context.addProperty(ApacheHttpProperties.FDAH_PATH_TO_DOCUMENT_ROOT, outputPath, PropertyValue.PropertyTypeEnum.String, false);
    plugin.setWorkflowExecutionContext(context);
    plugin.validate();
    plugin.execute();
  }

...

 The JAR file has the following structure:

Plugin JAR Structure

Code Block
<PluginName>-<PluginVersion>.jar
     properties
          <properties file name>.xml  (Plugin XML Properties Definition)
     lib
           adflibFlexFndCommonCore.jar (FlexDeploy library)
           adflibFlexDeployCore.jar   (FlexDeploy library)
           commons-io-2.11.0.jar   (Apache Commons IO library)
           <your plugin>.jar   (your plugin classes packaged into a JAR - containing full package structure)
     bin
           setup.sh   (optional)
           setup.bat   (optional)
     plugin.xml   (Plugin XML Metatdata)
Tip

Reminder

Remember to update the plugin Version in the plugin.xml file when packaging.

Java Plugin Reference Example

A fully functional plugin example for Apache HTTP Server, including plugin source code, is provided for reference throughout this documentation. This plugin provides four operations

...

Apache HTTP Server Plugin XML Metadata

Sample plugin.xml

Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
<PluginDefinition xmlns="http://flexagon.com/deploy/plugin">
  <Name>FlexagonApacheHttpPlugin</Name>
  <PluginDisplayName>Apache HTTP</PluginDisplayName>
  <Description>A plugin to manage Apache Http Servers and deployment.</Description>
  <TechnologyGroup>Web Servers</TechnologyGroup>
  <SubTechnologyGroup>Apache Http Server</SubTechnologyGroup>
  <Vendor>Flexagon</Vendor>
  <MaxConcurrentThreads>-1</MaxConcurrentThreads>
  <Version>0.7</Version>
  <Operations>
    <Operation>
      <Name>build</Name>
      <Description>Build an artifact to deploy to an Apache Http Server</Description>
      <Target>flexagon.fd.plugin.apache.http.operations.Build</Target>
      <PropertyKeys/>
      <Inputs>
        <AllowsUserDefined>false</AllowsUserDefined>
        <Input>
          <Name>FDAH_PATH_TO_FILES</Name>
          <DataType>String</DataType>
          <Description>Relative path to files inside the FD_TEMP_DIR to collect into a build artifact</Description>
          <DisplayName>Path to Files</DisplayName>
          <IsDefaultValueExpression>false</IsDefaultValueExpression>
          <IsRequired>false</IsRequired>
          <IsEncrypted>false</IsEncrypted>
          <DisplayRows>1</DisplayRows>
          <DisplayColumns>120</DisplayColumns>
        </Input>
      </Inputs>
      <Outputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Outputs>
      <EndPointSpecification>
        <Selection>
          <All/>
        </Selection>
        <Execution>
          <Any/>
        </Execution>
      </EndPointSpecification>
      <ConsumesArtifacts>false</ConsumesArtifacts>
      <ProducesArtifacts>true</ProducesArtifacts>
    </Operation>
    <Operation>
      <Name>deploy</Name>
      <Description>Deploy to Apache Http Server artifact</Description>
      <Target>flexagon.fd.plugin.apache.http.operations.Deploy</Target>
      <PropertyKeys>
        <PropertyKey>FDAH_PATH_TO_DOCUMENT_ROOT</PropertyKey>
      </PropertyKeys>
      <Inputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Inputs>
      <Outputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Outputs>
      <EndPointSpecification>
        <Selection>
          <All/>
        </Selection>
        <Execution>
          <Any/>
        </Execution>
      </EndPointSpecification>
      <ConsumesArtifacts>true</ConsumesArtifacts>
      <ProducesArtifacts>false</ProducesArtifacts>
    </Operation>
    <Operation>
      <Name>stopServer</Name>
      <Description>Stop an Apache Http Server</Description>
      <Target>flexagon.fd.plugin.apache.http.operations.Stop</Target>
      <PropertyKeys/>
      <Inputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Inputs>
      <Outputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Outputs>
      <EndPointSpecification>
        <Selection>
          <All/>
        </Selection>
        <Execution>
          <Any/>
        </Execution>
      </EndPointSpecification>
      <ConsumesArtifacts>false</ConsumesArtifacts>
      <ProducesArtifacts>true</ProducesArtifacts>
    </Operation>
    <Operation>
      <Name>startServer</Name>
      <Description>Start an Apache Http Server</Description>
      <Target>flexagon.fd.plugin.apache.http.operations.Start</Target>
      <PropertyKeys>
        <PropertyKey>FDAH_START_TIMEOUT</PropertyKey>
      </PropertyKeys>
      <Inputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Inputs>
      <Outputs>
        <AllowsUserDefined>false</AllowsUserDefined>
      </Outputs>
      <EndPointSpecification>
        <Selection>
          <All/>
        </Selection>
        <Execution>
          <Any/>
        </Execution>
      </EndPointSpecification>
      <ConsumesArtifacts>false</ConsumesArtifacts>
      <ProducesArtifacts>true</ProducesArtifacts>
    </Operation>
  </Operations>
</PluginDefinition>

Apache HTTP Server Properties XML

Apache HTTP Server - AHS_PROPERTY_LISTING.xml (filename is irrelevant)

Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList xmlns="http://flexagon.com/deploy/propertylist">
  <Name>FDAH_PLUGIN</Name>
  <Property>
    <Name>FDAH_PATH_TO_DOCUMENT_ROOT</Name>
    <Description>Path to the document root of the server, such as htdocs.</Description>
    <Scope>ENVINST</Scope>
    <DataType>String</DataType>
	<SubDataType>DIRECTORY</SubDataType>
    <IsEncrypted>false</IsEncrypted>
    <IsRequired>true</IsRequired>
    <Validators/>
  </Property>
    <Property>
    <Name>FDAH_START_TIMEOUT</Name>
    <Description>How many milliseconds to wait for the server to start before failing the workflow step. Defaults to 300000 (5 minutes)</Description>
    <Scope>ENVINST</Scope>
    <DataType>Integer</DataType>
    <IsEncrypted>false</IsEncrypted>
    <IsRequired>true</IsRequired>
    <Validators/>
  </Property>
</PropertyList>

...