Custom Step Types
From Skyway Wiki
Contents
|
Custom Step Framework
The custom step framework provides a mechanism for extending the functional capabilities of Skyway actions by allowing developers to define their own custom steps. Although a brief overview is provided, this document assumes a general familiarity with Skyway actions, steps and the Action Diagram Editor.
Overview
First, let’s define exactly what a step is in the Skyway vernacular. In a broad sense, a step is the mechanism by which Skyway allows a user to model behavior. A step can be fine grained enough to model actual programming constructs or course grained enough to model common tasks. For instance, the Decision step provides a mechanism for a user to model the "if - then - else" statement familiar to many developers while the Send Email step models the task of sending an email.
A step can be connected to another step (or steps) to model the flow of application logic. Beginning at the step designated as the start step, each step is executed and based on the semantics of that particular step, the next connected step will be executed. The collection of steps and their connections are contained within a Skyway Action.
The Skyway visual workspace ships with many steps that allow a user to model application behavior. However, Skyway recognizes that we have not thought of everything. Hence, it was necessary to allow users to define their own custom steps to extend the modeling capabilities of Skyway actions.
It is important to note that designing a custom step is not always the best approach. WHEN TO DEVELOP A CUSTOM STEP VERSUS JAVA SERVICE
The following table describes the components necessary to define a custom step.
| Component | Description |
|---|---|
| Graphical User Interface | The graphical user interface component defines how a user will interact with the custom step when building an application. This consists of a palette entry for selecting the custom step, a canvas for placement and connection of the custom step and a property sheet which allows the user to enter information that will be stored in the model. |
| Model | The model component defines the data that is needed to configure a step. This data is stored as part of the containing action. |
| Generation | The generation component accesses the information stored in the model to generate code that can be executed at runtime. |
Custom Steps from UI
An eclipse plugin is required to define one or more custom steps. This section will walk you through the creation of the Send Email step as if we were creating it as a custom step.
The easiest way to create a plugin is to use the Plugin Development Environment (PDE). The PDE provides wizards to help create standard plugins and Skyway has added a few wizards to help in the creation of custom steps. These instructions assume that you have started the Skyway Visual Workspace and that you are in the Skyway Modeling Perspective.
From the eclipse menu, select File>New>Other. Open up the Plug-In Development folder, select the Plug-in Project item and click on the Next button.
Enter a project name in the “Project Name” field. By convention, plug-in project names are similar to package names so we will call our project com.mycompany.steps. For our purposes, all the default values are fine, so click on the Next button.
The next wizard allows you to enter information that will be used to generate the plug-in. We will accept the default values, so click on the Next button.
The next wizard allows you to select a template to aid in the creation of a particular type of plugin. Select the “Skyway Custom Step” option in the left pane (make sure that the “Create a plug-in using one of the templates” checkbox is checked) and click on the Next button.
The next wizard is the Step Registry wizard contributed by Skyway to help create custom steps. You can click on any of the “Show Steps” radio buttons to filter the steps that are displayed. By default, the “Local Only” option is selected which only displays steps created for the current plug-in. Since this is a brand new plug-in, the only item displayed will be the Step Registry item. You can click on the “All” radio button to view all steps defined in any installed plugins. The “Skyway Only” option will display steps defined by Skyway and can be useful if you want to see how some of the Skyway steps were defined.
Now lets create our “custom” version of the Send Email step. Click on the “Add” button to get started. You will be prompted to enter the Step Name dialog. Enter “Send Email” in the provided text field and click on the “OK” button.
The Step Registry wizard generates default values for all required fields.
The default values should be sufficient in most cases, but feel free to make changes as needed. The “Title” field contains the value that will be displayed in the palette for this step. This value will also be used as the text in your canvas node when the step is dropped onto the canvas. The “Step Id” field uniquely identifies this step. Although not a requirement, the step id is prefixed with your plug-in id. This provides a namespace for your step and helps avoid collisions with steps defined by other plug-ins. The “Small Image” and “Large Image” fields contain references to the images that will be displayed in the palette as well as on the canvas for this step. Next we will need to define a model for our step. The model will store information that the step needs to perform its function. Click on the “Define…” button to open the “Create Model Interface” dialog.
Now we can define the fields that make up our Send Email step. We want the user to be able to enter a destination email address, an origination email address, a subject and a body. Select the “com.mycompany.steps.sendemail.model.SendEmailStep” item and click on the “Add” button to add a field. You will see a field with a default name of Field0 and a default type of String. Change the “Field Name” to “toAddress”.
Follow the same steps to create a fromAddress field, a subject field and a body field.
For our simple example, we will just have fields of type String. You can, however, click on the “Select…” button to select a different type for the field. Click on the finish button to accept this model interface definition. Once a model has been defined, a default property section will be defined based on the fields added to your model. We will accept the defaults for now.
Next we need to define where our step should go on the Palette. Click on the “Next” button to display the “Palette Registry” wizard. The Palette Registry wizard displays the layout of the steps within the palette. The Palette Registry wizard allows you to define palette groups and palette tools. Palette containers are used to group steps together and palette tools are used to add a step to the canvas. We will simply add our custom step to the Services group. First select the “Group [Services]” item and click on the “Add Tool” button. This will create an empty palette tool. Select our custom step from the “Step” drop list. This will automatically populate the Id field which is required. The default value is the same as the step id which should be sufficient in most cases.
The other fields only need to be populated if you wish to override the values defined in the step definition. We won’t make any changes here, so click on the “Finish” button to create your plug-in project with your custom step definition.
On the left of the workbench, in the Package Explorer, is an overview of some of the things the wizard created. Most of the items are generated for all plug-in projects and are not very interesting.
The most interesting things here are the src folder, which contains the source code for our plug-in and the plugin.xml file -- the plug-in's manifest file. We will take a look at plugin.xml first.
plugin.xml
An Eclipse plugin is required to define one or more steps. A step definition is made up of several different extension points that define the different components that make up a step including the gui, model and runtime. We will use the Send Email step as an example to see the different extension points used to define a step.
Step Registry extension point (com.skyway.core.stepRegistry)
First lets look at the step registry extension point for the Send Email step which defines a step descriptor.
<extension point="com.skyway.core.stepRegistry">
<step id="com.skyway.core.steps.sendemail">
<diagram>
<smallImage location="/icons/steps/small/SendEmail.gif"/>
<largeImage location="/icons/steps/large/SendEmail.gif"/>
<tool description="Create new Send Email Step" label="Send Email"/>
<propertiesSheet showAppearanceTab="true" showGeneralTab="true"
showGenericTab="false">
<propertiesTab
description="Provide header information"
id="com.skyway.core.steps.sendemail.tab.header">
<propertiesSection id="com.skyway.core.steps.sendemail.section.header" />
</propertiesTab>
<propertiesTab
description="Construct a message using text and available variables"
id="com.skyway.core.steps.sendemail.tab.body">
<propertiesSection id="com.skyway.core.steps.sendemail.section.body" />
</propertiesTab>
<propertiesTab
description="Select attachment(s)"
id="com.skyway.core.steps.sendemail.tab.attachments">
<propertiesSection id="com.skyway.core.steps.sendemail.section.attachments" />
</propertiesTab>
</propertiesSheet>
</diagram>
<model class="com.skyway.core.steps.sendemail.model.SendEmailStep"/>
<runtime templateClass="com.skyway.deploy.templates.SendEmailTemplate" />
</step>
</extension>
Custom Step Definition
Custom steps are defined as extension points. For this reason, all custom steps must be defined within a plugin project. The following table describes the main extension points used to define a custom step:
| Extension Point | Description |
|---|---|
| org.skyway.core.stepRegistry | The org.skyway.core.stepRegistry extension point is used to provide the metadata needed to define a custom step. Each step has a variety of metadata associated with it (labels, images, validations, property sheet, etc). |
| org.skyway.core.paletteRegistry | The org.skyway.core.paletteRegistry extension point is used to provide the metadata about the entries that will be displayed in the step portion of the action palette on the action diagram. The step developer can define palette containers and palette tools. |
| org.eclipse.ui.views.properties.tabbed.propertyTabs | The org.eclipse.ui.views.properties.tabbed.propertyTabs extension point is used to define the tabs that will appear in the property sheet. This is a standard eclipse extension point for defining property tabs. |
| org.eclipse.ui.views.properties.tabbed.propertySections | The org.eclipse.ui.views.properties.tabbed.propertySections extension point is used to define the property sections that will appear in a property tab. This is a standard eclipse extension point for defining property sections. |
| org.eclipse.emf.ecore.generated_package | The org.eclipse.emf.ecore.generated_package extension point is used to register your model with EMF. This is a standard EMF (Eclipse Modeling Framework) extension point. |
| org.skyway.core.actionResourceHandler | The org.skyway.core.actionResourceHandler extension point is used to define a resource handler that will be called during resource operations (beforeLoad, afterLoad, beforeSave, afterSave) that occur on an action resource. |
Step Registry
The step registry is responsible for loading all of the meta-data about steps that the custom step framework needs in order to work with it. The definition of a custom step resides in the plugin.xml file and contains all of the meta-data about a step.
A custom step is defined in the stepRegistry extension point in your plugin.xml. For this reason, you are required to create your custom steps in a plugin project. Each step definition .
The step registry is loaded whenever an action is loaded into memory. This can occur when a user explicitly opens the action diagram editor as well as when any other core object that references an action is loaded – for example, an operation has a default action defined so opening an operation will also cause the step registry to be loaded. In addition, building can also cause the step registry to be loaded if an action is one of the objects being built. However, the step registry is loaded only once and is very light weight.
The step registry loads all of the contributing elements into memory and creates a descriptor that supplies all of the meta data contained in the plugin.xml file. Model objects are created as needed, meaning that only the model objects that exist in the action being loaded will be created.
Palette Registry
The palette registry is responsible for loading all of the meta-data about the entries that will be displayed in the palette on the action diagram.
The paletteRegistry extension point in your plugin.xml defines the structure and position of the entries in your palette. There are two kinds of palette entries, a palette tool and a palette container. A palette tool can be used to add a step to the canvas. A palette container provides a means of organizing your palette tools. It can contain one or more palette tools as well as other palette containers. A palette container must contain at least one palette tool, either directly or indirectly.
Custom Step
A custom step definition is required to have a unique id within the plugin that it is defined. All step ids will be prefixed with the id of the plugin. The fully qualified id of the step includes the plugin id and should be used wherever a step id is required. A name for the step must also be provided. By default, the step name will be used at design time for the palette tool title as well as to derive the palette tool description. Additionally, when a step is created, the step instance will be initialized with the name of the step.
Standard Step
A standard step cannot contain children steps. The primary model object for a standard step must be an extension of a Step. See the section on custom step models for more information about the primary model object for a step.
Container Step
A container step can contain children steps. The primary model object for a standard step must be an extension of a ContainerStep. See the section on custom step models for more information about the primary model object for a step.
A container step requires a start step to determine which contained step should be executed first. See the section on models for more information about the ContainerStep interface.
plugin.xml
Defining the Custom Step
To define a custom step, add a <step> element as a child of the step registry extension point. The <step> element allows you to define the id and name of the step as well as a label and description.
Element Name: step Parent Element(s): extension point for the step registry Child Elements: model, diagram, generation, validations, runtime
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| id | The unique id for the custom step. | N/A | N/A |
| name | The name of the custom step. | N/A | N/A |
| stepType | The type of the custom step. | Container | Standard |
| label | The label for the step. This will be used by the palette tool associated wth this step unless it is overridden y the palette tool definition. If no value is provided, the step name is used. | N/A | N/A |
| description | A description for the step. This will be used as the description for the palette tool mapped to this step unless it is overridden by the palette tool definition. If no value is provided, the following message is displayed “Create new [Step Name] Step”. | N/A | N/A |
This example shows the step definition for the Send Email step.
<step id="sendemail" name="Send Email" >
Custom Step Model
The first thing you need to determine when you design your custom step is the data that your custom step will need to perform the desired functionality. This data will be stored in one or more model objects. Most of the data will need to be acquired from the user at design time. The information provided by the user will be stored when the action is saved and will be available when the source code for the custom step is generated.
Each custom step is required to have a single primary model object. Additionally, the step designer may define helper model objects to create a model object graph. Helper model objects can be useful when you need to store a list of model objects as well as when multiple custom steps make use of the same data.
The step designer has several options for defining their model object graph, each with its own set of requirements. Your model object graph can mix and match the different types of model options.
Dynamic Model
A dynamic model object is implemented as a java interface. The java interface should contain java bean properties (getter and setter methods) for its data elements. The java bean properties are required to follow the standard java bean naming convention. This is the simplest option and should be sufficient in most cases. In fact, all of the Skyway Core Steps are defined using a dynamic model.
To define a primary dynamic model object, create an interface that extends one of the following two interfaces (or a subinterface of these two interfaces):
| Interface | Description |
|---|---|
| org.skyway.core.model.action.step.Step | Extend this interface when the custom step will not contain other steps. |
| org.skyway.core.model.action.step.ContainerStep | Extend this interface when the custom step will contain other steps |
To define a helper dynamic model object, create an interface that extends the following interface (or a subinterface of this interface):
org.eclipse.emf.ecore.EObject
Add getter and setter methods for each property you would like to define following the standard java bean naming convention. Two annotations are also supported for your properties:
| Annotation | Description |
|---|---|
| @Containment(Boolean) | Default is true. |
| @ResolveProxies(Boolean) | Default is true. |
Working With Dynamic Model Objects
When you define your model objects using the dynamic model option, there will not be a concrete class for each defined model object. Instead, the custom step architecture makes use of the EMF dynamic features which is similar to introspection in Java. Each dynamic model object is created and managed as an instance of one of the following three classes:
| Class | Description |
|---|---|
| org.skyway.core.model.util.DynamicStep | For steps that don’t contain other steps. |
| org.skyway.core.model.util.DynamicContainerStep | For steps that do contain other steps. |
| org.skyway.core.model.util.DynamicEObject | For helper model objects. |
These classes are identical with the exception of their superclass. They each implement the IDynamicEObject interface. The sole purpose of this interface is for easy conversion from a proxy to the actual instance. Most of the time, the custom step framework is operating on the instances themselves. However, these classes are hard to use when a step developer needs to access the data stored in them because all access is through the EMF introspection facility. So in cases where the custom step developer may be accessing the model object, they will be working with a proxy. The proxy makes it possible for the user to use the getters and setters defined in their model interface instead of the through introspection. Of course, the step developer is free to use introspection if they wish.
There are two primary areas where a user might be accessing the model object. In their property sections and in their JET template. In these situations the user will be working with the proxy and not the actual instance. They do not have to do anything, except possibly cast the proxy to their interface type. If the step developer finds himself in a situation where he does not have the proxy, but instead has the actual instance, they can easily get to the proxy by casting the actual instance to an IDynamicEObject and calling the getProxy method. Additionally, the user may get to the actual instance by casting the proxy to an IDynamicEObjectProxy and calling the getDelegate method.
Example:Creating Dynamic Model Objects
Since a dynamic model object does not have its own concrete class, you need to create instances of dynamic model objects through your StepPackage. There are several create methods available. You can create a new dynamic model object by passing in your model interface name, your model interface class or the EClass created for your model object.
This example shows a simplified version of the Send Email primary model object. The SendEmailStep model object stores the information necessary to send an email.
public interface SendEmailStep extends Step {
@Containment(true)
public VariableText getBody();
public void setBody(VariableText value);
@Containment(true)
public VariableText getTo();
public void setTo(VariableText value);
@Containment(true)
public VariableText getFrom();
public void setFrom(VariableText value);
@Containment(true)
public VariableText getSubject();
public void setSubject(VariableText value);
@Containment(true)
public List<EmailAttachment> getAttachments();
public void setAttachments(List<EmailAttachment> attachments);
}
The SendEmailStep model object is defined as a java interface that extends the Step interface. This is what makes it a dynamic model object. The SendEmailStep defines several java bean properties body, to, from, subject and attachments. The getters and setters are required to follow the standard java bean naming conventions.
The SendEmailStep makes use of the VariableText model object which is defined in the Skyway core model. The VariableText model object allows a user to store data as a mix of text and Skyway variables. The variables must be in the following format: ${variableName}.
The SendEmailStep also makes use of a dynamic helper model object EmailAttachment. The EmailAttachment model object stores information necessary to send an attachment with an email.
public interface EmailAttachment extends EObject {
public String getAttachmentType();
public void setAttachmentType(String attachmentType);
public String getSourceLocation();
public void setSourceLocation(String sourceLocation);
public String getDestinationLocation();
public void setDestinationLocation(String destinationLocation);
@Containment(true)
public VariablePath getVariableLocation();
public void setVariableLocation(VariablePath variableLocation);
}
The EmailAttachment model object is defined as a java interface that extends the EObject interface. Again we see the definition of several java bean properties following the standard java bean naming conventions.
The EmailAttachement model object makes use of the VariablePath model object which is defined in the Skyway core model. The VariablePath model object stores path information representing a single Skyway variable.
Concrete Model
A concrete model object is implemented as a java class. The java class should contain java bean properties (getter and setter methods) for its data elements. The java bean properties are required to follow the standard java bean naming convention. Since the model object is a java class, you will need to define an implementation for the getter and setter methods. In most cases you will also need to define instance attributes to store the properties.
One of the benefits of the concrete model is that you will be working with a concrete class instead of a generic implantation class or a proxy to your model object interface. Since your model object is defined as a java class, you can define additional methods in addition to your getter and setter methods. This also provides a degree of control over what happens when a property is set or retrieved.
To define a primary concrete model object, create a class that extends one of the following two classes (or a subclass of these two classes): org.skyway.core.model.util.ConcreteStep – extend this class when the custom step will not contain other steps org.skyway.core.model.util.ConcreteContainerStep – extend this class when the custom step will contain other steps
To define a helper concrete model object, create a class that extends the following interface (or a subinterface of this interface): org.skyway.core.model.util.ConcreteEObject
Add getter and setter methods for each property you would like to define following the standard java bean naming convention. You will also need to define an instance attribute in which to store the property. A concrete model supports the same annotations as a dynamic model.
Example: Creating Concrete Model Objects
This example shows a simplified version of the Send Email primary model object defined as a concrete model object.
public class SendEmailStep extends ConcreteStep {
private List<EmailAttachment> attachments;
private VariableText body;
private VariableText to;
private VariableText from;
private VariableText subject;
@Containment(true)
public VariableText getBody(){return body;}
public void setBody(VariableText value){body = value;}
@Containment(true)
public VariableText getTo(){return to;}
public void setTo(VariableText value){to = value;}
@Containment(true)
public VariableText getFrom(){return from;}
public void setFrom(VariableText value){from = value;}
@Containment(true)
public VariableText getSubject(){return subject;}
public void setSubject(VariableText value){subject = value;}
@Containment(true)
public List<EmailAttachment> getAttachments(){return attachments;}
public void setAttachments(List<EmailAttachment> attachments){this.attachments = attachments;}
}
The SendEmailStep model object is defined as a java class that extends the ConcreteStep class. This is what makes it a concrete model object. The only other difference from the dynamic model definition is that an instance attribute is defined for each property and the getter and setter methods are implemented to get and set the corresponding property.
Custom Model
A custom model object is defined by extending the Skyway ECore model. This option provides the greatest flexibility. However, it does require a thorough understanding of EMF and is therefore the most complex option. The process of extending the Skyway ECore model is outside the scope of this document.
To define a primary custom model object, extend one of the following two EClasses (or both), depending on your requirements:
| Interface | Description |
|---|---|
| model.action.step.Step | Extend this EClass when the custom step will not contain other steps. |
| model.action.step.ContainerStep | Extend this EClass when the custom step will contain other steps. |
To define a helper custom model object, create any EClass since they all extend the EObject EClass.
plugin.xml
Defining the Primary Model Object
To define your primary model object, add a <model> element as a child of the <step> element. The <model> element has a single attribute “class” which should contain the fully qualified name of the interface or class that defines your primary model object. Helper model objects do not need to be defined in the plugin.xml. They are derived from your primary model object definition.
Element Name: model Parent Element(s): step Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| class | The fully qualified name of the interface or class that defines your primary model object. | N/A | N/A |
Custom Step Graphical User Interface
This section describes the GUI attributes for Custom Steps.
Action Palette
The action palette provides a mechanism for a user to interact with the canvas. The palette is made up of several palette containers each containing one or more palette tools or other palette containers. The palette is presented to the user as a hierarchical set of palette groups each with palette tools. The palette is broken up into three separate sections.
General Section
The general section contains some general purpose tools not specific to custom steps. These are not defined by the custom step framework but instead are part of the action diagram editor.
Connector Section
The connector section contains a single Connector tool that allows the user to connect one canvas node to another.
In the figure above we see the Connector tool. To create a connection from one canvas node to another using this tool follow these steps:
- Select the Connector tool
- Place your mouse over the source canvas node
- Click and hold your mouse button
- Drag your mouse over the target canvas node
- Release the mouse button
Note that you can also release the mouse button without being over a target canvas node. You will then be prompted with a popup menu to create a new canvas node or connect to an existing one.
Step Section
The step section is where all of your custom steps will be placed. The step section can be considered the root palette container.
In the figure above we see four palette containers. The parent of each of these palette containers is root.
Palette Container
A palette container provides a mechanism for organizing other palette entries. A palette container can be one of the following types:
Palette Group
A palette group displays its child palette entries as a list that is not collapsible. This type of palette container can contain palette tools as well as other palette containers. Use this type of palette container when you have a very small number of palette entries that you want to be displayed at all times or when you want to have other palette containers as children. Space is a concern with this option.
In the figure above we see a palette group with several child palette tools.
Palette Drawer
A palette drawer can be collapsed and expanded to hide and show its child palette entries. This type of palette container can only contain non-container palette entries. Use this type of palette container when you have a medium number of palette entries and it is acceptable to have your palette children collapsed at times. Space is less a concern with the palette drawer, but if you have a lot of children, they will all be displayed when the drawer is expanded. The initial state can be specified when you define your palette drawer.
In the figure above we see a palette drawer in its collapsed state. By clicking on the palette drawer we can expand it to show its child palette entries.
In the figure above we see a palette drawer in its expanded state. Clicking on the palette drawer will collapse the drawer once again. Clicking on the push pin image to the right of the palette drawer title will pin this drawer open. If a palette drawer is not pinned, opening other palette drawers will force any expanded drawers to be collapsed.
Palette Stack
A palette stack is similar to a drop down toolbar button. It has a current entry that can be selected and used just like any other palette tool. Additionally, you can select the drop down icon on the right to display a popup menu of all child palette entries. Selecting an entry from the drop down causes it to become the current entry and also makes it active. This type of palette container can only contain palette tools. Use this type of palette container when you have many palette entries as it will never use more space than that required by the current entry.
In the figure above we see a palette stack with the Invoke Operation tool as its current entry.
In the figure above we see a palette stack with the current entry activated.
In the figure above we see a palette stack with its popup menu displayed showing all children palette entries. The checkmark on the left indicates the current entry.
Palette Tool
A palette tool provides a mechanism for placing a custom step on the canvas. Simply select the palette tool and click on the canvas at the location where you would like the step to be placed. This will create a node on the canvas representing the selected custom step. You can then configure the step using the properties sheet as well as using the Connector tool to connect the canvas node to other canvas nodes.
In the figure above we see five palette tools contained by the Services palette container.
plugin.xml
Defining a Palette Container
To define a palette container, add a <paletteContainer> element as a child of the paletteRegistry extension point. The title and description values defined on the step are used if not specified here.
Element Name: paletteContainer Parent Element(s): extension point for the palette registry Child Elements: smallImage, largeImage
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| id | The unique id of the palette container. | N/A | N/A |
| title | The title of the palette container.This will only display a title when the type attribute equals drawer. | N/A | N/A |
| description | The description of the palette container. This attribute is for descriptive purposes only and will not display on the palette. | N/A | N/A |
| type | The type of palette container. | drawer | stack | group |
| parent | The id of the palette container that should contain this palette entry. Use root to specify the root palette container. | N/A | root |
| initiallyCollapsed | Set to true if you would like a palette container of type drawer to be displayed collapsed initially. This attribute is ignored unless the palette container type is drawer. | N/A | true |
| position | Numeric value greater than 0 that indicates where a palette entry is positioned within a palette container relative to sibling palette entries that also specify a position. Use decimal values to insert between two consecutive integer positions. For example, if you would like your palette entry to be positioned between two other palette entries with respective position values of 4 and 5, set your position attribute to 4.5. If no position is specified, it attempts to display the palette entries in the order they appear in the plugin.xml after any palette entries that have a valid position. Though not required, it is generally a good practice to provide a position because if no position is provided and other plugins contribute palette entries without a position, the ordering is undefined. Entering a value that is non numeric or less than or equal to 0 will be treated as if no position was provided. | N/A | N/A |
Example: This is the definition of the Services palette container for the Skyway Core Steps.
<paletteContainer
id="org.skyway.group.services"
initiallyCollapsed="true"
parent="root"
position="1"
title="Services"
type="drawer"
visible="true">
</paletteContainer>
Defining a Palette Tool
To define a palette tool, add a <paletteTool> element as a child of the paletteRegistry extension point. The title and description values defined on the step are used if not specified here.
Element Name: paletteTool Parent Element(s): extension point for the palette registry Child Elements: smallImage, largeImage
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| id | The unique id of the palette tool. | N/A | N/A |
| stepId | The id of the step descriptor mapped to this palette tool. | N/A | N/A |
| title | The title of the palette tool. This will not display a title when the palette layout is set to “Icons Only” | N/A | N/A |
| description | The description of the palette tool. This text will be displayed as a tool tip unless the palette layout is set to “Details” in which case this text will be displayed as part of the palette tool. | N/A | N/A |
| parent | The id of the palette container that should contain this palette entry. Use root to specify the root palette container. | N/A | root |
| position | Numeric value greater than 0 that indicates where a palette entry is positioned within a palette container relative to sibling palette entries that also specify a position. Use decimal values to insert between two consecutive integer positions. For example, if you would like your palette entry to be positioned between two other palette entries with respective position values of 4 and 5, set your position attribute to 4.5. If no position is specified, it attempts to display the palette entries in the order they appear in the plugin.xml after any palette entries that have a valid position. Though not required, it is generally a good practice to provide a position because if no position is provided and other plugins contribute palette entries without a position, the ordering is undefined. Entering a value that is non numeric or less than or equal to 0 will be treated as if no position was provided. | N/A | N/A |
Example: This is the definition of the Send Email palette tool. Notice that its parent is the Services palette container.
<paletteTool
id="org.skyway.core.steps.sendemail"
parent="org.skyway.group.services"
position="4"
stepId="org.skyway.core.steps.sendemail"
visible="true">
</paletteTool>
Defining Palette Images
A small image can be defined as child elements of both the <paletteContainer> and <paletteTool> elements. The small image is displayed when the “Use Large Icons” option for the palette is not selected. For palette container entries, the small image is always displayed. The small image is displayed by default. When no small image is specified, the value for the small image is taken from the step definition.
Element Name: smallImage Parent Element(s): paletteContainer, paletteTool Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| location | The path to the small image to display on the palette. | N/A | N/A |
A large image can be defined as a child element of the <paletteTool> element. The large image is displayed when the “Use Large Icons” option for the palette is selected. When no large image is specified, the value for the large image is taken from the step definition.
Element Name: largeImage Parent Element(s): paletteTool Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| location | The path to the large image to display on the palette. | N/A | N/A |
Action Canvas
The action canvas provides a visual representation of your action logic. A step is represented by a node on the canvas. Each step can be connected to another step (or steps) to model the flow of the application logic. Beginning at the step designated as the start step, each step is executed and based on the semantics of that particular step, the next step connected step will be executed. Additionally, you can add notes to your canvas. Although they do not represent any functionality they are a good way to provide documentation right on the canvas. Many of the visual aspects of the action canvas can be tailored to your liking. This includes showing a ruler, showing a grid, grid spacing, fonts, etc. However, these are user preferences and are not part of the step definition.
Canvas Node
The definition of the canvas node allows you to customize how the node will appear on the canvas. This includes the image that will be displayed as well as how the label will appear under the canvas node. Additionally, you can define a tooltip that will be displayed when the user hovers over the node.
- Define a canvas node if you want to
- Customize the content of the label that appears under the canvas node
- Add a tooltip
- Initialize the model in some way.
If a canvas node is not defined, default values will be used.
In the figure above on the left we see a Send Email step displaying a tooltip.
A canvas node appears a little differently when the primary model object extends the ContainerStep.
The figure above shows the Iterator step, which is a container step, with its Log Message step set as the start step. Just like an action, a container step requires one of the steps to be marked as the start step. When the container step is executed, it looks for its start step and starts executing from there. When the container step is done executing its contained steps, it executes the next step as normal.
Start Step
The start step for an action indicates the step that will be executed first when an action is invoked. After the start step is executed, the flow of the logic then proceeds to any connected step based on the semantics of the step that just completed. This process continues until one of the following occurs:
- A Stop step is encountered
- An exception is thrown and not caught
- There are no more steps to execute
In the figure above to the left, we see that the Send Email step has been marked as the start step. This is evident by the Start image (green circle with a white arrow pointing right) in the upper left hand corner of the canvas node. Additionally, the start step is surrounded by a blue border. Contrast this with a Send Email step that is not marked as the start step on the right. The border color and width can be configured through the workspace preferences..
When a step is added to the canvas it will automatically become the start step if no other step exists on the canvas. The start step can be changed by right clicking on a step and either selecting or deselecting the “Start Step” menu toggle
Initializers
Initializers are a means of initializing your model object.
plugin.xml
Canvas Node Definition
To define a canvas node, add a <node> element as a child of the <diagram> element. The node element itself has no defined attributes.
Element Name: node Parent Element(s): diagram Child Elements: label, initializer, tooltip
This element has not defined attributes.
Canvas Node Label Definition
The node element can have a single <label> child element. The <label> element determines what will be displayed in the text under the canvas node.
Element Name: label Parent Element(s): node Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| viewText | The text to display under the canvas node. This can contain variables from your model object or static text or both. Any variable must be enclosed by the ${variableName} syntax. The variable name should be a fully qualified attribute starting from your primary model object. The default value is the name of the step. | N/A | ${name} |
This example shows how you would display the step description after the step name:
<label viewText=”${name} - ${description} />
Tooltip Definition
The node element can have a single <tooltip> child element. The <tooltip> element determines what will be displayed when the user hovers over the canvas node.
Element Name: tooltip Parent Element(s): node Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| viewText | The text to display in the tooltip. This can contain variables from your model object or static text or both. Any variable must be enclosed by the ${variableName} syntax. The variable name should be a fully qualified attribute starting from your primary model object. There is no default value. Newlines are significant and each line is trimmed before display. | N/A | ${name} |
This example shows the tooltip definition from the figure above:
<toolTip>
To: ${to}
From: ${from}
Subject: ${subject}
Body: ${body.text}
</toolTip>
Initializer Definition
The node element can have 0 or more <initializer> child elements. The <initializer> element itself can contain 0 or more <initializer> child elements. The <initializer> element performs initialization of your model when a node is created. An implicit initializer is always performed before initializers explicitly defined. The implicit initializer sets the name attribute of the primary model object the the value of the step title. You can override this behavior by specifying an initializer that sets the name attribute to an alternative value.
Nested initialization uses the context of its parent initialization. For this reason, when using nested initialization, your feature should be within the context of the parent initialization.
There are three types of initialization:
- Assignment – simply assigns the provided value to the specified feature
- Create – creates an instance that is the type of the feature and uses that value in the assignment
- User – this should be specified when you are providing an implementation of the IInitializer interface.All attributes except className are ignored.
Element Name: initializer Parent Element(s): node, initializer Child Elements: initializer
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| Type | The type of initialization to perform. | create | user | N/A |
| Feature | The name of the property to perform the initialization on. | N/A | N/A |
| Value | The value to initialize the property to. | N/A | N/A |
| className | The name of a class that implements the IInitializer interface. The IInitializer interface has a single method initialize that takes a Step as its parameter. Cast this to your model interface and perform any required initialization. | N/A | N/A |
Properties
Now that you have defined a model to store data necessary to execute your custom step, you will need an interface for the user to enter the required information to configure the step. This is accomplished by using the standard eclipse Properties view. The properties view is comprised of a property sheet, property tabs and property sections.
Property Sheet
Each open action diagram editor has its own property sheet. We use the standard eclipse tabbed property sheet which allows the step developer to create multiple property tabs, each with property sections.
The property sheet encompasses your entire properties view. It consists of a title bar at the top, a list of tabs to the left and the body of the currently selected tab to the right of the list of tabs.
Property Tab
Each step can have zero or more property tabs. These will appear as tab folder on the left side of your properties view. To the right of the tab folders will be displayed the actual UI for the selected tab. A property tab does not really define much besides the label that appears in the tab and the description that appears at the top of the tab UI. It also has some organizational attributes that help position the tab in the desired sequence.
Property Section
Each step can have zero or more property sections. Property sections refer to actual classes that implement a section of the UI displayed in the body area of the currently selected tab. Although a property section is associated with a single tab, it is a concrete class so the same class can be specified as different property sections for different tabs.
Each property section must extend the org.eclipse.ui.views.properties.tabbed.AbstractPropertySection class at a minimum. However, extending the skyway developed org.skyway.action.ui.sheet.StepPropertySection provides many benefits that make it easier to work with the Custom Step Framework. It provides several helper methods to easily implement databinding as well as to create controls that work with Skyway objects.
plugin.xml
Property Sheet Definition
To define a property sheet, add a <propertiesSheet> element as a child of the <diagram> element.
Element Name: propertiesSheet Parent Element(s): diagram Child Elements: label, propertiesTab, image
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| helpContextId | Provide a help context id. If a value is specified, the help icon will be displayed in the upper right corner of the property sheet title. | N/A | N/A |
| showGeneralTab | True to show the general tab and false to hide the general tab. | N/A | true |
| showGenericTab | True to show the generic tab and false to hide the generic tab. | N/A | false |
| showAppearanceTab | True to show the appearance tab and false to hide the appearance tab. | N/A | true |
| showIgnoreExceptionsFlag | True to show the “Ignore exceptions for this step and continue to the next step” checkbox on the general tab and false to hide it. | N/A | true |
Property Sheet Label Definition
The <propertiesSheet> element can have a single <label> child element. The <label> element determines what will be displayed in the property sheet title.
Element Name: label Parent Element(s): node Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| viewText | The text to display in the property sheet title. This can contain variables from your model object or static text or both. Any variable must be enclosed by the ${variableName} syntax. The variable name should be a fully qualified attribute starting from your primary model object. The default value is the name of the step. | N/A | N/A |
Property Sheet Image Definition
The <propertiesSheet> element can have a single <image> child element. The <image> element determines the image to display on the left hand side of the property sheet title bar. When no image is specified, the value for the image is taken from the step definition.
Element Name: image Parent Element(s): propertiesSheet Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| location | The path to the image to display on the left hand side of the property sheet title. | N/A | N/A |
Property Tab Definition
The <propertiesSheet> element can have zero or more <propertiesTab> child elements. For each <propertiesTab> element you will see a tab added to the list of tabs on the left hand side of the property sheet. Each tab in the property sheet makes use of the standard eclipse tabbed property sheet. The <propertiesTab> element defined here merely points to a <propertyTab> element in the org.eclipse.ui.views.properties.tabbed.propertyTabs exrtension point.
Element Name: propertiesTab Parent Element(s): propertiesSheet Child Elements: propertiesSection
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| id | References the id attribute of a <propertyTab> element in the org.eclipse.ui.views.properties.tabbed.propertyTabs extension point. | N/A | N/A |
| description | The description will be displayed as a title within your property tab. The description attribute only supports static text. | N/A | N/A |
Property Section Definition
The <propertiesTab> element can have zero or more <propertiesSection> child elements. Each <propertiesSection> element contributes a section to its parent <propertiesTab> element. Each section in the property sheet makes use of the standard eclipse tabbed property sheet. The <propertiesSection> element defined here merely references a <propertySection> element in the org.eclipse.ui.views.properties.tabbed.propertySections extension point.
Element Name: propertiesTab Parent Element(s): propertiesSheet Child Elements: propertiesSection
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| id | References the id attribute of a <propertySection> element in the org.eclipse.ui.views.properties.tabbed.propertySections extension point. | N/A | N/A |
Custom Step Validations
The custom step validation architecture consists of a thin layer on top of the EMF Validation Framework. One of the benefits of this thin layer is that we can hide some of the complexities of the EMF Validation Framework from the custom step developer. This makes it easier to add validations without in depth knowledge of the EMF Validation Framework. Additionally, it allows us to provide a simple interface for creating validations in the Step Registry Wizard.
The custom step validation architecture does not preclude the step developer from creating constraint providers, categories and constraints directly in the EMF Validation Framework provided extension points. It simply makes it easier to create validations with less ramp up time.
Virtually all custom step types will require the user to provide some information at design time to configure the step. In most cases, this information will need to be validated to ensure proper execution of the step at runtime. When you define a custom step type, you can also define validations for that step. The validation can be defined in one of the following three languages: Java, Groovy or OCL.
Java Validations
A Java validation is implementated as a subclass of the AbstractModelConstraint class. It implements the validate() method accepting an IValidationContext and returning an IStatus reporting the validation result.
The ValidationContext object provides a variety of information about the current validation operation, including:
- the target of validation, the element to be validated
- the eventType, in the case of live validation
- the feature and its new value, in the case of live validation
- the currentConstraintId, indicating the constraint that is being invoked. This allows a single Java class to implement any number of (probably related) constraints, and switch on the ID to determine what to check.
In addition to this contextual information, the validation context provides a variety of other services to the constraint. Using the get/putCurrentConstraintData(), a constraint can cache some information that will help it to optimize the validation of multiple objects by persisting data between invocations on different objects in the same validation operation. This cache can take the form of any object.
Another time-saving measure is the skipCurrentConstraintFor() method, which allows a constraint to indicate that it has already validated some other objects while checking the current target, so that it does not need to be invoked on them later. For example, a constraint that checks for dependency cycles in a graph will, if it does or does not find a cycle, have indirectly validated a number of other objects in doing so.
In the following example, our java validation is checking to make sure that the loggingPolicy attribute of the object being validated is not null:
public IStatus validate(IValidationContext ctx) {
if (((LogMessageStep)ctx.getTarget()).getLogCategory() == null) {
IStatus failureStatus = ctx.createFailureStatus(new Object[] { ((LogMessageStep)ctx.getTarget()).getDisplayString() });
return failureStatus;
} else {
return ctx.createSuccessStatus();
}
}
Groovy Validations
A Groovy validation consists of a Groovy expression that will be evaluated when validation occurs. The Groovy expression has access to two predefined context variables. The "validationContext" variable contains a reference to the ValidationContext which is the same object described in the Java Validations section above. The "target" variable will contain a reference to the object being validated.
You can think of your groovy expression as an assertion. In order for the constraint to pass, the result of the expression must evaluate to true. Alternatively, a result value that can be coerced to the string "true" (case insensitive) by accessing the result objects toString() method will also pass. In addition, you have the option of returning a ValidationStatus object. In that case, the status is passed to the validation framework unchanged.
In this example, our groovy expression is checking to make sure that the loggingPolicy attribute of object being validated is not null:
target.loggingPolicy != null
In this example, our groovy expression is returning a ValidationStatus object which will be forwarded to the validation framework:
TODO - provide an example
OCL Validations
OCL constraints are outside the scope of this document. Please see the EMF Validation Framework documentation for more information about OCL constraints and how to use them.
Validation Categories
Each validation is defined in the context of a validation category. Validation categories are hierarchical and serve primarily as a means of grouping validations. A validation category can also be defined as mandatory which prevents a user from disabling it.
All custom step validations are part of the “Custom Step Validation Rules” category. Each custom step then defines its own category to further organize the validations defined for that step. The default category name for a custom step is “$stepName Validations” and the default category description is “Validations for the $stepName Step”. Lets take a look at how the validation categories and validations are displayed. Select the Window -> Preferences menu item and select the Model Validation -> Constraints preferences panel.
What we see in the Constraint Categories list is the “Custom Step Validation Rules” category which is the parent, by default, of all custom step categories. Below that, we see the specific validation category for each step. When selected, the category displays all of its validations in the “Select constraints to enable” list. The name of the validation is displayed in this list.
plugin.xml
Defining a Validation Category
To define a validation category, add a <validationCategory> element as a child of the <step> element. It is only necessary to define the attributes of the <validationCategory> element if you wish to override the default values, which are sufficient in most cases.
Element Name: validationCategory Parent Element(s): step Child Element(s): validation
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| categoryID | The id for the category. If no category id is provided, a default category is created. | N/A | N/A |
| categoryName | The name of the category. The category name is displayed on the Constraints preference page. Specify this attribute if you want to override the default custom step validation category name. | N/A | N/A |
| categoryDescription | A description of the category. The description is displayed on the Constraints preference page. Specify a value for this attribute if you want to override the default custom step validation category description. | N/A | N/A |
| mandatory | Boolean value indicating whether the validations in this category are mandatory. The validations in a mandatory category cannot be disabled by the user in the Constraints preference page. | false | false |
Defining a Validation
To define a validation, add a <validation> element as a child of the <validationCategory> element.
Element Name: validation Parent Element(s): validations Child Elements: NONE
| Attribute Name | Description | Supported Values | Default |
|---|---|---|---|
| language (Required) | The implementation language of your validation. | Groovy | OCL | N/A |
| name (Required) | The name of the validation. The validation name is displayed on the Constraints preference page. | N/A | N/A |
| description | The description of the validation. The description is displayed on the Constraints preference page as part of the description field. If not specified, the description is the same as the validation name. | N/A | N/A |
| id | The id for the validation. The id is displayed on the Constraints preference page as part of the description field. If not specified, the id is generated from the validation name. | N/A | N/A |
| severity | The type of validation failure. If not specified, the severity is ERROR. The severity effects how a validation is displayed on the Action Diagram Editor. | WARNING | INFO | ERROR |
| modelClassName | The fully qualified name of the class to be validated. If not specified, the model class for the step is used. | N/A | N/A |
| validationClassName (Java Only) | The fully qualified name of the class that performs the validation. The class must be a subclass of the AbstractModelConstraint class. This is required when the language attribute is Java. | N/A | N/A |
| expression (Groovy and OCL Only) | A groovy expression or an OCL constraint expression. This is required when the language attribute is Groovy or OCL. | N/A | N/A |
| message | The message that will be displayed to the user. If no message is provided, the message "No message provided." will be displayed. | N/A | N/A |
The following example shows the definition of a groovy validation defined as part of the default custom step validation category:
<validation
language="Groovy"
name="Log Category Validation"
message="No log category specified"
severity="WARNING"
expression="target.logCategory != null && target.logCategory.text.length() > 0"
/>
The following example shows the same validation definition defined as a java validation.
<validation
language="Java"
name="Log Category Validation"
message="No log category specified"
severity="WARNING"
validationClassName="org.skyway.core.steps.logmessage.validation.LogCategoryValidation"
/>
Action Diagram Editor
This section describes how to work with validations in the Action Diagram Editor.
For each validation that fails, you will see a marker in your problems view. In the example below, we see that we have one validation failure indicating the we have not provided a valid value for the Logging policy.
You can double click on the marker in your problems view, and the step that has the problem will be selected on the canvas and the properties sheet for that step will be displayed. In the example below, notice that the "Log Message" step is selected (has a black border).
Any steps that have a validation failure will also be decorated with the marker image as appropriate for the type of validation failure. You can place your mouse over the validation marker to see a tool tip describing all validation failures for that step.
Additionally, a validation marker will appear in the top right hand corner of your canvas. You can place your mouse over the validation marker to see a tool tip describing all validation failures for all steps in the action.
Users can enable and/or disable Constraints from thier preferences dialog. Open your preferences dialog by selecting the Window -> Preferences... menu item. Select the Model Validation -> Constraints preference page.
Step Registry Wizard
The Step Registry Wizard provides two panels for users to configure validations for a step, the Validations panel and the Validation panel.
- Validations Panel
The Validations panel provides a user interface for a step developer to enter category information. If this panel is left blank, the validations for this step will be part of the Skyway Custom Step category. None of the fields on this panel are required and should only be modified if the default behavior is not satisfactory. One reason to provide a different category here would be if you want the validations for this step to be grouped separately from Skyway Custom Step category validations in the Model Validations -> Constraints preference page. Another reason to change the default behavior would be if you want your validations to be mandatory.
The following example shows a Validations panel that uses the default category:
- Validation Panel
The following example shows
plugin.xml
Each step can have a <validations> element as a child of the <step> element. For each validation you wish to define, create a <validation> element as a child of the <validations> element.
| Element Name: validations Parent Element(s): step Child Element(s): validation | |||
|---|---|---|---|
| Attribute Name | Description | Supported Values | Default |
| categoryId | The id for the category. If no category id is provided, the constraints are placed in the custom step category and all other attributes for this element are ignored. | ||
| categoryName | The name of the category. The category name is displayed on the Constraints preference page. This attribute is ignored unless a categoryId is provided. If a categoryId is provided, and no value is provided for the categoryName, the categoryId will be used as the categoryName. | ||
| categoryDescription | A description of the category. The description is displayed on the Constraints preference page. This attribute is ignored unless a categoryId is provided. If a categoryId is provided, and no value is provided for the categoryDescription, the categoryId will be used as the categoryDescription. | ||
| mandatory | Boolean value indicating that the constraints in this category are mandatory. The constraints in the category cannot be disabled by the user in the Constraints preference page when the mandatory flag is set to true. This attribute is ignored unless a categoryId is provided. | true | false | false |
| Element Name: validation Parent Element(s): validations Child Elements: NONE | |||
|---|---|---|---|
| Attribute Name | Description | Supported Values | Default |
| language (Required) |
The implementation language of your validation. | Java | Groovy | OCL | |
| name | The name of the validation. The validation name is displayed on the Constraints preference page. | ||
| description | The description of the validation. The description is displayed on the Constraints preference page as part of the description field. | ||
| id | The id for the validation. The id is displayed on the Constraints preference page as part of the description field. | ||
| status | The type of validation failure. | ERROR | WARNING | INFO | ERROR |
| targetClassName | The fully qualified name of the class to be validated. If no value is provided, the model class for the step is used. | ||
| validationClassName (Java Only) |
The fully qualified name of the class that performs the validation. The class must be a subclass of the AbstractModelConstraint class. This is required when the language attribute is Java. | ||
| expression (Groovy and OCL only) |
A groovy expression or an OCL constraint expression. This is required when the language attribute is Groovy or OCL. | ||
| message | The message that will be displayed to the user. If no message is provided, the message "No message provided." will be displayed. | ||
The following example shows the definition of a groovy validation defined as part of the default custom step validation category:
<step> .... other step elements .... <validations> <validation language="Groovy" name="validation.logmessage.loggingPolicy" message="Logging policy must be one of the following values (Enabled | Disabled | Dynamic)" modelClassName="com.skyway.core.steps.logmessage.model.LogMessageStep" expression="target.loggingPolicy != null" > </validation> </validations> </step>
The following example shows the definition of a java validation:
<step> .... other step elements .... <validations> <validation language="Java" name="validation.logmessage.loggingPolicy" message="Logging policy must be one of the following values (Enabled | Disabled | Dynamic)" modelClassName="com.skyway.core.steps.logmessage.model.LogMessageStep" validationClassName="com.skyway.core.steps.logmessage.validation.LoggingPolicyValidation" > </validation> </validations> </step>
Generation
Now its time to make your step actually perform the function it was designed for. At this point you have defined a model to store the information your custom step will need at runtime. You have defined and implemented the GUI necessary for the user to provide this information. You have provided validations to ensure that the information entered by the user is valid. Now we need to take the model information and generate some code to perform the desired function.
An action is generated into a java class that will be executed at runtime. Each step that is reachable from the designated start step for the action will become a method in the generated action class. A step is reachable if a path from the start step will directly or indirectly lead to that step. Note that if no start step is specified, the action will not have any generated step methods.
The custom step framework uses JET (Java Emitter Templates) to generate the code that will be executed at runtime. JET is part of the EMF (Eclipse Modeling Framework) which the custom step framework uses to define its model objects.
JET (Java Emitter Templates) Overview
JET templates are text files that use a simplified JSP (Java Server Pages) syntax. When a JET template is saved it is transformed into a java source file called the java implementation class. The JET template can specify the package and class name of the java implementation class. The java implementation class has a generate method which is called to generate the actual source code. It is important to note that the java implementation class does not contain your generated source code. It contains the source code that will generate your source code when the generate method is invoked.
JET Syntax
JET templates support the following syntactical elements:
| Syntax Element | Syntax | Description |
|---|---|---|
| Comment | <%-- … --%> | All text between these characters are ignored |
| Directive | <%@ … %> | Controls the generation of the java implementation class. |
| Java Declaration | <%! … %> | Used to declare Java methods or fields. |
| Java Expression | <%= … %> | Emits the results of the contained Java expression. Java expressions do not contain semicolons. |
| Java | <% … %> | Contains sections of Java statements. The Java statements are emitted directly into the generate() method of the java implementation class. |
| Tag | <tagname [attributes]/>
<tagname [attributes]> … content … </tagname> | XML elements accepting attributes and possibly content. Tags typically emit some text or perform some function. |
Implicit Java Objects
The following Java objects are accessible from Java expressions and scriptlets:
| Object Name | Description | Java Type |
|---|---|---|
| context | Provides access to template invocation information. Supports setting and getting of variables that can be used elsewhere in the template. | org.eclipse.jet.JET2Context |
| out | The writer where the template output is written when the generate() method is called on the java implementation class. | org.eclipse.jet.JET2Writer |
Custom Step JET Templates
As mentioned earlier, the implementation of a custom step is a method within the runtime action class. The custom step template is responsible for generating the method body only. The method signature and enclosing braces are generated by the action that contains the custom step.
Configuration
The plugin where your custom step is defined must have the JET nature applied and must be configured with the JET Template Builder. This is done automatically when you create your custom step plugin through the Step Registry Wizard. If you are adding a custom step to an existing plugin or if you created the plugin manually your plugin project may not be a JET project. You can convert your plugin project to a JET project by following these steps.
Right click on your plugin project in the package explorer and select New>Other…>Java Emitter Templates>Convert Projects to JET Projects.
After clicking the Next button, select your project and click Finish.
By default, custom step templates will be created in a templates directory under the project root. When you save a custom step template, the JET Template Builder will generate the java implementation class in the jet2java source directory also under the project root. The package name and class name of the java implementation class are specified within your custom step template.
At design time, when a build occurs, the JET template is provided your model objects and is executed to produce the code that makes up the runtime portion of your step.
Accessing Model Objects
A custom step template has access to the following variables:
| Variable Name | Description | Variable Type |
|---|---|---|
| step | An instance of the primary model object defined by your custom step. | The primary model object defined by the custom step. |
| model | An instance of the action that contains the custom step. | org.skyway.core.model.action.Action |
| pkg | The package name of the generated runtime action class. | string |
| stepMethod | The generated method name of the custom step. | string |
| methodNameFactory | A factory class used to generate step method names. | string |
Tags
XPath expressions can be used to navigate your model object graph within a tag attribute. An XPath expression is a series of path elements separated by forward slashes. The paths are evaluated from left to right to navigate the object graph of the model. XPath expressions can begin with a variable which is the name of the variable preceded by a $.
Java Access
JET templates may contain Java code when surrounded by the Java Declaration, Java Expression or Java Scriptlet syntax elements. The java code has access to all variables defined within the JET Context.
Getting a Variable
You can get a variable using the following syntax:
context.getVariable(“variableName”);
You will need to cast the result to the appropriate type.
Setting a Variable
You can set a variable using the following syntax:
context.setVariable(“variableName” variableInstance);
Any variables that you set into the context can be accessed from within a tag by preceding the variable with a $.
Skyway Custom Step Tags
This section provides details on some of the more common JET tags used by custom steps.
<sw:javaType>
The javaType tag is used to emit the runtime java type of the selected model object.
| Attribute | Description | Required |
|---|---|---|
| select | Provide an xpath expression to select a model object for this tag to operate upon. | true |
| package | When true, the emitted runtime class name will contain the package name. | N/A |
| emitCollectionPolicy | Determines how to omit a model object that is a collection. This attribute is ignored if the selected object is not a collection.
concrete – emit a concrete collection class name abstract – emit a collection interface name omit – do not emit the collection declaration | N/A |
| var | The name of a variable to store the result of this tag. | N/A |
| emit | When true, the result of this tag will be written. This is usually used in conjunction with the var attribute to emit the results of this tag to a variable instead of as output. | N/A |
<sw:declarationName>
The declarationName tag is used to emit an appropriate declaration name for the selected model object.
| Attribute | Description | Required |
|---|---|---|
| select | Provide an xpath expression to select a model object for this tag. | true |
| factory | The variable name of a class that implements the INameFactory interface. Create an instance that implements this interface and set it into the context giving it a variable name before using this attribute. | N/A |
| capitalize | Emit a capitalized declaration name. | N/A |
| uncapitalize | Emit an uncapitalized declaration name. | N/A |
| var | The name of a variable to store the result of this tag. | N/A |
| emit | When true, the result of this tag will be written. This is usually used in conjunction with the var attribute to emit the results of this tag to a variable instead of as output. | N/A |
<sw:getVariable>
The getVariable tag emits code capable of getting a variable at runtime.
| Attribute | Description | Required |
|---|---|---|
| select | Provide an xpath expression to select a model object for this tag to operate upon. | true |
<sw:setVariable>
The setVariable tag emits code capable of setting a variable at runtime.
| Attribute | Description | Required |
|---|---|---|
| select | Provide an xpath expression to select a model object for this tag to operate upon. | true |
| parameter | The code to emit that provides the set variable function with the variable to set at runtime. If you do not provide a parameter attribute the string “);” will be appended to the setVariable call. This is useful when the parameter needs to come from the evaluation of another tag. | N/A |
<sw:variableText>
The variableText tag emits the contents of a VariableText model object.
| Attribute | Description | Required |
|---|---|---|
| select | Provide an xpath expression to select a model object for this tag to operate upon. The selected model object must be a VariableText model object. | true |
| output | Determines how the contents of the VariableText should be output.
formatted – (default) replaces variable references with code to get their value encoded – encode the contents – works in conjunction with the encoding attribute raw – emit the contents without any processing | N/A |
| encoding | Determines how the contents of the VariableText should be encoded if the output attribute is set to encoded.
java – (default) encodes variable references for use with the java MessageFormat class groovy – removes the ${} syntax around variable references slf4j – encodes variable references for use with slf4j jpql – encodes variable references for use with a jpql query sql – encodes variable references for use with a sql query | N/A |
| defaultValue | The value to emit if the VariableText is empty or null. | N/A |
<sw:insert>
The insert tag is used to emit the processed contents of the insert tag to an anchored position elsewhere in the template file. This is useful when your custom step needs to emit code outside of its method body. You can define methods, variables, etc to be used by your custom step.
| Attribute | Description | Required |
|---|---|---|
| anchorId | The id of the anchor where the contents of this tag will be inserted. | true |
| id | Provide a unique id to prevent your emitted code from being overwritten by another step emitting to the same anchor. | N/A |
Use this tag sparingly as it increases the possibility of name collisions.
The action defines the following anchor ids:
| Anchor | Description |
|---|---|
| actionVariables | Location near the top of the runtime action class typically used to declare variables for the action. |
| beforeStepMethods | Content will be inserted before any of the step methods are defined. |
| beforeStepMethod_{$stepMethod} | Content will be inserted before the comment of the step with a method name equal to the $stepMethod variable. |
| annotationsStepMethod_{$stepMethod} | Content will be inserted directly before the method signature of the step with a method name equal to the $stepMethod variable. |
| afterStepMethod_{$stepMethod} | Content will be inserted directly after the closing brace of the step with a method name equal to the $stepMethod variable. |
| afterStepMethods | Content will be inserted after all of the steps are defined. |
<sw:javaString>
The javaString tag is used to emit contents that can be stored between “” in java code. It escapes any characters that would make a literal string invalid.
| Attribute | Description | Required |
|---|---|---|
| select | Provide an xpath expression to select a model object for this tag to operate upon. | true |
| var | The name of a variable to store the result of this tag. | N/A |
| emit | When true, the result of this tag will be written. This is usually used in conjunction with the var attribute to emit the results of this tag to a variable instead of as output. | N/A |
Writing a Custom Step Template
The best way to explain how to write a custom step template is to go through an example. Below is the template for the Groovy step. The template has comments added explaining the different syntactical constructs used and how they affect the generated code.
<%--
The jet directive instructing the JET Template Builder to generate the java implementation class for this template in package org.skyway.core.steps.groovy.template with a class name of GroovyTemplate.java.
--%>
<%@ jet
package="org.skyway.core.steps.groovy.templates"
class="GroovyTemplate"
%>
<%--
Three taglib directive instructing the JET Template Builder to include the standard JET javaTags and controlTags tag
libraries as well as the skywayCodeGenTags tag library.
--%>
<%@taglib prefix="java" id="org.eclipse.jet.javaTags"%>
<%@taglib prefix="c" id="org.eclipse.jet.controlTags"%>
<%@taglib prefix="sw" id="org.skyway.integration.java.skywayCodeGenTags"%>
<%--
The standard JET control tag <c:include> generates the template specified by the template attribute and places
its results at this location. The templates/ExpressionCache.jet template can be used to cache groovy scripts and is
shown below this example. Notice the c: before the tag name. This indicates which tag library this tag comes from.
This allows tags with the same name but from different libraries to be used in the same template.
--%>
<c:include template="templates/ExpressionCache.jet" />
<%--
The next line contains a mix of java runtime code with the <sw:javaString> tag. This ensures that no matter what the
user entered as a step name, it can be output as a java string literal.
--%>
_getLogger().debug("Groovy: <sw:javaString select="$step/@name" />");
<%--
Here we see the use of the standard core JET tags <c:choose>, <c:when> and <c:otherwise>. The <c:choose> tag evaluates
the first <c:when> tag whose test xpath expression evaluates to true and evaluates the <c:otherwise> tag if none of the <c:when> tags were evaluated.
--%>
<c:choose>
<%--
This <c:when> tag will be evaluated if the script property of the groovy custom step has a value. Notice the use of the implicit variable $step which refers to the primary model object of the custom step being generated. The xpath expression navigates from the groovy custom step, to its script property defined in the primary model object with a getScript() and setScript() method of type StatementBlock.
--%>
<c:when test="$step/script">
<%--
Again we see the <sw:javaString> tag that safeguards java strings. There is a great possibility that a groovy script will contain newlines. Without the <sw:javaString> tag this would cause compile errors. This line of code is extracting the groovy script entered by the user and assigning it to the expressionText string.
--%>
String expressionText = "<sw:javaString select="$step/script/@text" />";
<%--
Here we encounter the standard java JET tag <java:import>. Use this tag to surround any class name with a full package and JET will handle the conversion from a fully qualified class name to a simple class name with an import statement added at the top of the generated class. The line of code that will be generated declares a variable named expression of type Expression. The Expression class is used to evaluate groovy code. The expression variable is assigned the value returned from the _getExpression() method. The _getExpression() method is generated by the <c:include> tag at the top of this template. Notice the parameter being passed to the _getExpression() method uses the $stepMethod variable. The $stepMethod variable contains the name of the method generated for this step which is unique so we can use it as the key for our cached script. The rest of the template is java code that will be generated as is into the action runtime class. It evaluates the groovy expression, catches any exceptions and throws a SkywayRuntimeException.
--%>
<java:import>org.skyway.core.expr.Expression</java:import> expression = _getExpression("<sw:javaString select="$stepMethod" />", expressionText);
if (expression != null && !expression.isEmpty()){
try{
expression.evaluate();
}catch(Exception x){
throw new <java:import>org.skyway.execution.exception.SkywayRuntimeException</java:import>("Error evaluating expression: " + "<sw:javaString select="$step/script/@text" />", x);
}
}
</c:when>
<%--
The <c:otherwise> tag will be evaluated if the script property of the groovy custom step has no value. We just write out a debug message.
--%>
<c:otherwise>
_getLogger().debug("No Script Specified.");
</c:otherwise>
</c:choose>
The next example looks at the ExpressionCache.jet template. This can be included in any custom step that wants to cache groovy expressions that it evaluates.
<%-- The jet directive instructing the JET Template Builder to generate the java implementation class for this template in package org.skyway.deploy.templates with a class name of ExpressionCache.java. --%> <%@ jet package="org.skyway.deploy.templates" class="ExpressionCacheTemplate" %> <%-- Three taglib directive instructing the JET Template Builder to include the standard JET javaTags and controlTags tag libraries as well as the skywayCodeGenTags tag library. --%> <%@taglib prefix="java" id="org.eclipse.jet.javaTags"%> <%@taglib prefix="c" id="org.eclipse.jet.controlTags"%> <%@taglib prefix="sw" id="org.skyway.integration.java.skywayCodeGenTags"%> <%-- The skyway code gen tag <sw:insert> indicates that we want to insert its contents at the predefined actionVariables anchor located in the runtime action class. The id attribute differentiates this content from other content that might be inserted at this anchor location. It also ensures that no matter how many custom steps include this template, there will only be a single _expressionMap variable declared because other invocations will overwrite this one. The source code generated from this tag will declare a Map that is used to cache instances of the Expression class used to evaluate groovy expressions. --%> <sw:insert anchorId="actionVariables" id="_expressionMap"> /** * Expression cache * @generated */ private <java:import>java.util.Map</java:import><String, Expression> _expressionMap = new <java:import>java.util.HashMap</java:import><String, Expression>(); </sw:insert> <%-- Here we see another skyway code gen tag <sw:insert> indic




















