This is a small guide for everybody involved in converting the Mini Language into Groovy.
Content
Groovy DSL
Services
Getting started
Checking Fields
Setting Fields
Groovy DSL (dynamic scripting library)
How to get Groovy support in your IDE
The following paragraph is for Eclipse users.
It is possible to get Groovy support in Eclipse by converting the loaded project to a Groovy Project. The project itself will work as before.
To do this just follow these few steps:
- Right-click on the project that has to be converted
- Click on "Configure"
- Click on "Convert to Groovy Project"
Eclipse will automatically load the file OfbizDslDescriptorForEclipse.dsld
, in which the known fields and methods used in Groovy Scripts are defined.
Known Fields
property name: 'parameters', type : 'java.util.Map'
These are the parameters given to the Groovy Script, when it is called as a service. It is equivalent to Map<String, Object> context
in the Java-Service-Definition.
property name: 'context', type: 'java.util.Map'
More parameters, which are, for example, given through a screen or another Groovy Script. This is important when the script is called through an action
segment of a screen.
property name: 'delegator', type: 'org.apache.ofbiz.entity.Delegator'
Normal instance of the Delegator
, which is used for special database access.
property name: 'dispatcher', type: 'org.apache.ofbiz.service.LocalDispatcher'
Normal instance of the LocalDispatcher
, which is used to call services and other service-like operations.
property name: 'security', type: 'org.apache.ofbiz.security.Security'
Normal instance of the Security
-Interface with which permission checks are done.
Known Methods
method name: 'runService', type: 'java.util.Map', params: [serviceName: 'String', inputMap: 'java.util.Map']
method name: 'runService', type: 'java.util.Map', params: [serviceName: 'String', inputMap: 'java.util.Map']
Helping method to call services instead of dispatcher.runSync(serviceName, inputMap)
. Also possible: run service: serviceName, with: inputMap
method name: 'makeValue', type: 'java.util.Map', params: [entityName: 'String']
Helping method to make a GenericValue
instead of delegator.makeValue(entityName)
. Creates an empty GenericValue
of the specific entity.
method name: 'findOne', type: 'java.util.Map', params: [entityName: 'String', inputMap: 'java.util.Map']
Helping method to find one GenericValue
in the database. Used instead of delegator.findOne(entityName, inputMap)
method name: 'findList', type: 'java.util.List', params: [entityName: 'String', inputMap: 'java.util.Map']
Helping method to find many GenericValue
in the database. Used instead of delegator.findList(entityName, inputMap, null, null, null, false)
method name: 'select', type: 'org.apache.ofbiz.entity.util.EntityQuery', params: [entity: 'java.util.Set']
Helping method used instead of EntityQuery.use(delegator).select(...)
method name: 'select', type: 'org.apache.ofbiz.entity.util.EntityQuery', params: [entity: 'String...']
As above.
method name: 'from', type: 'org.apache.ofbiz.entity.util.EntityQuery', params: [entity: 'java.lang.Object']
Helping method used instead of EntityQuery.use(delegator).from(...)
method name: 'success', type: 'def', params: [message: 'String']
Helping method used instead of ServiceUtil.returnSuccess(message)
method name: 'failure', type: 'java.util.Map', params: [message: 'String']
Helping method used instead of ServiceUtil.returnFailure(message)
method name: 'error', type: 'def', params: [message: 'String']
Helping method used instead of ServiceUtil.returnError(message)
method name: 'logInfo', type: 'void', params: [message: 'String']
Helping method used instead of Debug.logInfo(message, fileName)
method name: 'logWarning', type: 'void', params: [message: 'String']
Helping method used instead of Debug.logWarning(message, fileName)
method name: 'logError', type: 'void', params: [message: 'String']
Helping method used instead of Debug.logError(message, fileName)
method name: 'logVerbose', type: 'void', params: [message: 'String']
Helping method used instead of Debug.logVerbose(message, fileName)
The actual definition of the methods can be found in /framework/service/src/main/java/org/apache/ofbiz/service/engine/GroovyBaseScript.groovy
,
the variables dctx
, dispatcher
and delegator
are set in the file GroovyEngine.java
which can be found in the same location.
Services
From MiniLang to Groovy
To see additional examples and finished conversions, which may help with occurring questions, click:
There is a chance that a similar case has already been converted.
IMPORTANT: When a simple-method ends, it will automatically at least return a success-map.
All the Groovy Services have to return success
at least, too.
return success()
Getting started
MiniLang files consist of services, which, in most cases, implement services.
The get converted to Groovy like the following:
<!-- This is MiniLang --> <simple-method method-name="createProductCategory" short-description="Create an ProductCategory"> <!-- Code --> </simple-method> // This is the converted Groovy equivalent /** * Create an ProductCategory */ def createProductCategory() { // Code }
It will be useful for future developers, and everybody who has to check something in the code, to put at least the short-description
as the new Groovydoc. This will hopefully more or less explain, what the method should or shouldn't do.
If the short-description
isn't helpful enough, feel free complete it.
The structure of if
and else
in MiniLang is a little different than the one from Groovy or Java and can be a bit confusing when first seen, so here is an example:
<if-empty field="parameters.productCategoryId"> <sequenced-id sequence-name="ProductCategory" field="newEntity.productCategoryId"/> <else> <set field="newEntity.productCategoryId" from-field="parameters.productCategoryId"/> <check-id field="newEntity.productCategoryId"/> <check-errors/> </else> </if-empty>
Notice, that the else
always starts before the if
-tag is closed, but sometimes isn't indented as one would expect it.
When navigating through bigger if
-phrases, the navigation itself will be much easier through just clicking in the opening or closing if
-tag; Eclipse will automatically mark the matching opening or closing if
-tag for you.
There are two possibilities to initialize a field/variable in Groovy.
To define a field/variable with its correct typing (this should be the preferred way; it is easier to read and to review):
String fieldName = "value"
To just "define" a field/variable. The IDE you are working with may not recognize the typing, but OFBiz can work with it:
def fieldName = "value"
Checking Fields
<if-empty field="fieldName"></if-empty> // checks if fieldName is existent and/or empty if (!fieldName) {}
<if-empty field="fieldName.property"></if-empty> // fieldName has to be existent, property doesn't need to // if known, that property does exist, the ? can be left out if (!fieldName?.property) {} // CAUTION: every query like this in Groovy evaluates to a Boolean type // everything that is empty or false will turn into false: // null, [], [:], "", false -> false // if you want to check if the field really is empty if (UtilValidate.isEmpty(fieldName)) {}
<if> <condition> <or> <if-empty field="field1"/> <if-empty field="field2"/> </or> </condition> <then> <!-- Code in if --> </then> <else> <!-- Code in else --> </else> </if> if (!field1 || !field2) { // Code in if } else { // Code in else }
<if-compare-field field="product.primaryProductCategoryId" to-field="parameters.productCategoryId" operator="equals"> <!-- Code --> </if-compare-field> // this will even work, if product is not existent or null if (UtilValidate.areEqual(product?.primaryProductCategoryId, parameters.productCategoryId)) { // Code }
<if-instance-of field="parameters.categories" class="java.util.List"></if-instance-of> if (parameters.categories instanceof java.util.List) {}
Setting Fields
<set field="fieldName" value="value"/> // if fieldName is not initialized String fieldName = "value" // if fieldName is initialized fieldName = "value"
<set field="otherFieldName.property" value="value"/> <set field="otherFieldName.otherProperty" value="true" type="Boolean"/> <set field="otherFieldName.otherProperty" from-field="parameters.property/> // if otherFieldName is not yet initialized, you have to do it first // MiniLang does that automatically Map otherFieldName = [:] // empty Map // now put the values in otherFieldName = [ property: "value", otherProperty: true ] // or the less efficient way otherFieldName.property = "value" otherFieldName.otherProperty = true // it is possible to put different values in later: otherFieldName.property = parameters.property
<set field="thisFieldName" value="${groovy: []}" type="List"/> // this is easier in Groovy List thisFieldName = []
<property-to-field resource="CommonUiLabels" property="CommonGenericPermissionError" field="failMessage"/> <!-- there are different cases of this, which are not distinguished in MiniLang --> <property-to-field resource="general.properties" property="currency.uom.id.default" field="parameters.rateCurrencyUomId"/> def failMessage = UtilProperties.getMessage("CommonUiLabels", "CommonGenericPermissionError", parameters.locale) // in Groovy there can is a difference for the second case parameters.rateCurrencyUomId = UtilProperties.getPropertyValue('general.properties', 'currency.uom.id.default')
<clear-field field="product.primaryProductCategoryId"/> product.primaryProductCategoryId = null
Related articles