Written By: David E. Jones, jonesde@ofbiz.org
Table of Contents
- Introduction
- The Simple Map Processor Mini-Language
- Simple Map Processor Overview
- Make In String Operations
- Process Field Operations
- Simple Map Processors Example
- The Simple Method Mini-Language
- Simple Method Overview
- Special Context Access Syntax
- Call Operations
- Java Call Operations
- Control and Error Handling Operations
- Event Specific Operations
- Service Specific Operations
- Method Environment Operations
- Entity Engine Misc. Operations
- Entity Engine Find Operations
- Entity Engine Value Operations
- Entity Engine List Operations
- Entity Engine Transaction Operations
- Conditional (If) Operations
- Other Operations
- Simple Methods Example
Introduction
The Mini-Language concept in Open For Business is similar to the Gang of Four Interpreter pattern, or the Mark Grand Little Language pattern. This is also the central theme of the Building Parsers with Java book by Steven John Metsker which the OFBiz Rule Engine is based on. The idea is to create simple languages that simplify complex or frequently performed tasks.
In business software there are things that are done hundreds or thousands of times in a single application that a Mini-Language can simplify to the point of cutting implementation and maintenance times not by just 50%, but often as much as 70-90%. Yes, certain tasks will take only 10% of the effort and knowledge to perform. This makes it easier people not familiar with the software to manipulate existing or build new functionality.
Mini-Languages tend to have instructions that are more like method calls in a general purpose language. They are meant to solve a specific problem in a specific context, and are generally worthless or need to be modified for other contexts or problems.
Often this idea is implemented using a free form (BNF) or english-like syntax. In Open For Business the Mini-Languages are expressed as XML files to simplify the learning and manipulation of the syntax, in addition to making the Mini-Languages easier to write and extend.
The XML schema (XSD) for the simple-map-processor and simple-method XML files is in the distribution in framework/minilang/dtd/simple-methods.xsd or on the website at [<span style="color: #ff0000"><strong>to be modified</strong></span> http://docs.ofbiz.org/pages/]. These are combined into a single file to make it easy to use inlined simple-map-processors inside a simple-method.
To specify the XMLschema for a simple-methods or simple-map-processors XML file use the following:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="[to be modified www.ofbiz.org/dtds/simple-methods.xsd]">
The Simple Map Processor Mini-Language
- Simple Map Processor Overview
- Make In String Operations
- Process Field Operations
- Simple Map Processors Example
Simple Map Processor Overview
The Simple Map Processor Mini-Language performes two primary tasks: validation and conversion. It does this in a context of moving values from one Map to another. The input map will commonly contain Strings, but can contain other object types like Integer, Long, Float, Double, java.sql.Date, Time, and Timestamp.
NOTE: The reference information for the simple-map-processor has been moved to annotations in
[to be modified
www.ofbiz.org/dtds/simple-methods.xsd]
We hope to recommended soon a tool that will use XSL/Transform to render the documentation directly from the XSD file in Browsers.
If you are interested by this issue please take a look at https://issues.apache.org/jira/browse/OFBIZ-571
Simple Map Processors Example
<simple-map-processors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="[to be modified www.ofbiz.org/dtds/simple-methods.xsd]"> <simple-map-processor name="update"> <make-in-string field="estimatedStartDate"> <in-field field="estimatedStartYear"><constant>-</constant> <in-field field="estimatedStartMonth"><constant>-</constant> <in-field field="estimatedStartDay"><constant>T</constant> <in-field field="estimatedStartHour"><constant>:</constant> <in-field field="estimatedStartMinute"><constant>:</constant> <in-field field="estimatedStartSecond"> </make-in-string> <process field="workEffortId"><copy replace="false"/></process> <process field="scopeEnumId"><copy/></process> <process field="currentStatusId"> <copy/> <not-empty> <fail-message message="Status is missing."/> </not-empty> </process> <process field="priority"> <convert type="Long"> <fail-message message="Priority is not a valid whole number."/> </convert> </process> <process field="estimatedStartDate"> <compare-field operator="less" field="estimatedCompletionDate" type="Timestamp" format="yyyy-MM-dd'T'HH:mm:ss"> <fail-message message="Estimated Start date/time must be BEFORE End date/time."/> </compare-field> <convert type="Timestamp" format="yyyy-MM-dd'T'HH:mm:ss"> <fail-message message="Estimated Start Date is not a valid Date-Time."/> </convert> </process> <process field="estimatedCompletionDate"> <convert type="Timestamp"> <fail-message message="Estimated Completion Date is not a valid Date-Time."/> </convert> </process> <process field="estimatedMilliSeconds"> <convert type="Double"> <fail-message message="Estimated Milli-seconds is not a valid number."/> </convert> </process> </simple-map-processor> <simple-map-processor name="delete"> <process field="workEffortId"> <copy/> <not-empty> <fail-message message="Work Effort ID is missing."/> </not-empty> </process> </simple-map-processor> </simple-map-processors>
The Simple Method Mini-Language
- Simple Method Overview
- Special Context Access Syntax
- Call Operations
- Java Call Operations
- Control and Error Handling Operations
- Event Specific Operations
- Service Specific Operations
- Method Environment Operations
- Entity Engine Misc. Operations
- Entity Engine Find Operations
- Entity Engine Value Operations
- Entity Engine List Operations
- Entity Engine Transaction Operations
- Conditional (If) Operations
- Other Operations
- Simple Methods Example
Simple Method Overview
The Simple Method Mini-Language is a simple way to implement an event that is invoked by the Control Servlet or a service that is invoked by the Service Engine. A Simple Method can be invoked through the static methods on the SimpleMethod class, or as an event through an entry in the controller configuration XML file like the following:
<event type="simple" path="org/ofbiz/commonapp/workeffort/workeffort/WorkEffortSimpleEvents.xml" invoke="update"/>
or as a service through an entry in a services.xml file like the following:
<service name="createPartyRole" engine="simple" location="org/ofbiz/commonapp/party/party/PartyRoleServices.xml" invoke="createPartyRole" auth="true"> <description>Create a Party Role (add a Role to a Party)</description> <attribute name="partyId" type="String" mode="IN" optional="true"/> <attribute name="roleTypeId" type="String" mode="IN" optional="false"/> </service>
The path or location for a Simple Method is the classpath and filename of the XML file.
In this Mini-Language you can invoke Simple Map Processors, Services and bsh scripts, perform entity related operations, and create messages to return to the caller. Specific operations can be enclosed in if blocks to execute conditionally and values or fields can be copied around in the maps, lists and method environment.
There are a number of tags which can be used to get and set attributes to/from a request or session object when called as an event or to set attributes in the result when called as a service. These operations are only applied when applicable. In other words if you include an env-to-request operation it will only be invoked when the simple-method is called as an event and an env-to-result operation will only be invoked when the simple-method is called as a service. Everything else is the same when called as an event or a service which makes it easy to write flexible logic that can be mounted/applied in various ways.
There are a number of objects that exist in the method environment when a simple-method starts or that are used as it executes to keep track of certain information. Some will exist when called as an event or a service, these are marked in the XSD. Each name can be overridden using an attribute on the simple-method tag. The defaults are listed below in the XSD.
NOTE: The reference information for simple-method has been moved to annotations in
[to be modified
www.ofbiz.org/dtds/simple-methods.xsd]
We hope to recommended soon a tool that will use XSL/Transform to render the documentation directly from the XSD file in Browsers.
If you are interested by this issue please take a look at https://issues.apache.org/jira/browse/OFBIZ-571
Special Context Access Syntax
In strings and field names a special syntax is supported to flexibly access Map member, List elements and to insert environment values into string constants.
The ${} (dollar-sign-curly-brace) syntax can be used to insert an environment variable value in pretty much any string constant in a simple-method file. Not only can it be used to reference top-level envrionment variables, the syntax elements described below can be used to access values in sub-structures.
You can use the "." (dot) syntax to access Map members. For example if you specify the attribute field-name="product.productName"it will reference the productName member of the productMap. This would be the same as specifying map-name="product" field-name="productName". Note that this is, of course, more flexible than a field-name/map-name combination because the Map structure can be multiple levels deep. For example if you have use the attributefield-name="products.widget.productName" it will reference the productName in the widget Map which is in the products Map.
The "[]" (square-brace) syntax can be used to access list elements. For example you can specify the attribute field-name="products[0].productName"and it will reference the productName of the first (position zero) element in the products List. To make this more useful you can pull a list index from the environment using something like field-name="products[${currentIndex}].productName".
There are two extensions to the [] syntax that can be used when refering to an environment location that is the target of an operation. If you do not include a number between the square braces the value will be put at the end of the list. If you put a "+" (plus sign) in front of the number between the square braces (ie: [+2]) it will insert the value before that position in the list instead of replacing the value at that location. For example, specifying [+0] would insert the value at the beginning of the list.
In fact, you can use the ${} syntax to substitute any string or other value at any location in a field-name or other string constant. So, you could even reference a Map member named in some other environment variable. For example you could use field-name="products[${currentIndex}].productName".
Okay, enough of the general stuff, you may find in the XSD file descriptions of the available operations. Here is simply a categorized list of them.
Call Operations
call-map-processor
call-service
call-service-asynch
call-bsh
call-simple-method
Java Call Operations
create-object
call-object-method
call-class-method
Control and Error Handling Operations
check-errors
add-error
return
Event Specific Operations
field-to-request
field-to-session
request-to-field
request-parameters-to-list
session-to-field
webapp-property-to-field
Service Specific Operations
field-to-result
Method Environment Operations
map-to-map
field-to-list
order-map-list (not documented in XSD yet)
set (not documented in XSD yet)
string-append (not documented in XSD yet)
string-to-list (not documented in XSD yet)
to-string
clear-field
[All operations in red below have been be replaced by set operation]
field-to-field (deprecated, do not use)
env-to-env (deprecated, do not use)
env-to-field (deprecated, do not use)
field-to-env (deprecated, do not use)
string-to-field (deprecated, do not use)
Control Operations
iterate
first-from-list
Entity Engine Misc. Operations
now-timestamp-to-env
now-date-to-env
sequenced-id-to-env
set-current-user-login
Entity Engine Find Operations
find-by-primary-key
find-by-and
filter-list-by-and
filter-list-by-date
Entity Engine Value Operations
make-value
clone-value
create-value
store-value
remove-value
remove-by-and
clear-cache-line
clear-entity-caches
set-pk-fields
set-nonpk-fields
Entity Engine List Operations
store-list
remove-list
Entity Engine Transaction Operations
transaction-begin
transaction-commit
transaction-rollback
Conditional (If) Operations
if
if-validate-method
if-compare
if-compare-field
if-regexp
if-empty
if-not-empty
if-has-permission
check-permission
check-id
Other Operations
property-to-field
The property-to-field tag puts the inlined string value in the specified field.
Attribute Name |
Required? |
Description |
resource |
Y |
The resource location of the properties file. |
property |
Y |
The property whose value will be put in the field. |
default |
N |
The default value to use if the specified property is empty. |
map-name |
N |
The name of the map in the method environment. If not specified the field-name will be used to get the field from the method environment. |
field-name |
Y |
The name (key) of the map field to use. |
log
The log tag logs a message used the OFBiz Debug class, which uses Log4J to log to the console, a file, or some other location. The message is a concatenation of the message attribute and then all of the field and string sub-element values in the order they are specified.
Attribute Name |
Required? |
Description |
level |
Y |
The logging/debug level to use. Must be one of the following: verbose | timing | info | important | warning | error | fatal | always. These are the standard OFBiz logging levels. |
message |
N |
A shortcut for simple messages. If used along with field and/or string sub-elements the inline string in the message will come first. |
Sub-Element Name |
How Many |
Description |
field |
0 to many |
Inserts the value of the field into the message where specified. |
string |
0 to many |
Inserts the value of the inline string into the message where specified. |
calculate
The calculate tag performs the specified calculation and puts the result in an object in the field of the specified map (see the calculate element attribute descriptions above). The type of the object can be specified with thetype attribute, but defaults to Double.
The calculate tag can contain calcop andnumber tags, and thecalcop tag can also contain these two tags to enable nested calculations.
Each calcop tag has three attributes: operator, map-name and field-name. Only the operator is required. The operator specifies the operation to perform on the given field and nested calcops and numbers. It must be one of the following: get | add | subtract | multiply | divide | negative.
Regardless of the operator the action is very similar. It translates to a formula like the following: (V1 operator V2 operator V3). In other words a final result is calculated by applying the operator to the values in the order they are specified. If a field-name (and optionally map-name) is specifies on the calcop tag that field will be used as the first value, otherwise the first nested calcop or number will be the first value.
The get operator is just an alias for add. It adds all of the values under it. Likewise the negative operator is almost an alias for subtract, with the exception that the first value is negated instead of left positive. For convenience the calculate tag itself acts like an add, in other words the calcops and numbers under it are all added together.
Attribute Name |
Required? |
Description |
map-name |
N |
The name of the map in the method environment. If not specified the field-name will be used to get the field from the method environment. |
field-name |
Y |
The name (key) of the map field to use. |
type |
N |
The object type to put into this field. Can be: Double | Float | Long | Integer. The default is Double. |
Sub-Element Name |
How Many |
Description |
calcop |
0 to many |
This tag is used to apply an operator in the calculation. It can have calcop and number tags nested under it, making it also act like a parenthesis. It has three attributes: operator, map-name, and field-name. These are described below. |
number |
0 to many |
This is used to put a numeric constant (a number) into the calculation. It has one attribute: value. This must be a properly formatted number or an error will result. |
Here is an example of an XML snippet that performs the calculation a=b+(((c+x+2)-d)/e), or in Reverse Polish Notation (a little bit closer to the resulting XML, and the notation used in the Rule Engine) a=(b,/(((c,x,2),-d),e)).
Here is the XML:
<calculate field-name="a"> <calcop operator="get" field-name="b"/> <calcop operator="divide"> <calcop operator="multiply"> <calcop operator="add" field-name="c"> <calcop operator="get" field-name="x"/> <number value="2"/> </calcop> <calcop operator="negative" field-name="d"/> </calcop> <calcop operator="get" field-name="e"/> </calcop> </calculate>
Simple Methods Example
<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="[<span style="color: #ff0000"><strong>to be modified</strong></span> http://docs.ofbiz.org/pages/]"><simple-method method-name="createProduct" short-description="Create an Product">
<check-permission permission="CATALOG" action="_CREATE"><fail-message message="Security Error: to run createProduct you must have the CATALOG_CREATE or CATALOG_ADMIN permission"/></check-permission>
<check-id field-name="productId" map-name="parameters"/>
<check-errors/>
<make-value value-name="newEntity" entity-name="Product"/>
<set-nonpk-fields map-name="parameters" value-name="newEntity"/>
<set-pk-fields map-name="parameters" value-name="newEntity"/>
<now-timestamp-to-env env-name="newEntity.lastModifiedDate"/>
<now-timestamp-to-env env-name="newEntity.createdDate"/>
<set from-field="userLogin.userLoginId" field="newEntity.lastModifiedByUserLogin"/>
<set from-field="userLogin.userLoginId" field="newEntity.createdByUserLogin"/>
<create-value value-name="newEntity"/>
<!- induce keywords if autoCreateKeywords is emtpy or Y->
<if-empty field-name="autoCreateKeywords" map-name="newEntity">
<call-bsh><![CDATA[org.ofbiz.commonapp.product.product.KeywordSearch.induceKeywords(newEntity);]]></call-bsh>
<else>
<if-compare field-name="autoCreateKeywords" map-name="newEntity" operator="equals" value="Y">
<call-bsh><![CDATA[org.ofbiz.commonapp.product.product.KeywordSearch.induceKeywords(newEntity);]]></call-bsh>
</if-compare>
</else>
</if-empty>
</simple-method>
<simple-method event-name="create" short-description="Create Work Effort">
<call-map-processor xml-resource="org/ofbiz/commonapp/workeffort/workeffort/WorkEffortMapProcessors.xml"
processor-name="update" in-map-name="parameters" out-map-name="context"/>
<check-errors/>
<call-service service-name="createWorkEffort" in-map-name="context">
<default-message>Work Effort successfully created.</default-message>
<result-to-request result-name="workEffortId"/></service>
</simple-method>
<simple-method event-name="update" short-description="Update Work Effort">
<call-map-processor xml-resource="org/ofbiz/commonapp/workeffort/workeffort/WorkEffortMapProcessors.xml"
processor-name="update" in-map-name="parameters" out-map-name="context"/>
<check-errors/>
<call-service service-name="updateWorkEffort" in-map-name="context">
<default-message>Work Effort successfully updated.</default-message></service>
</simple-method>
</simple-methods>