Joget DX 8 Stable Released
The stable release for Joget DX 8 is now available, with a focus on UX and Governance.
Table of Contents |
---|
In this tutorial, we will be following the 在本教程中, 我们将会遵循 guideline for developing a plugin to develop our 来开发我们的 Bean Shell Hash Variable plugin插件.
...
哈希变量使用起来很方便,但有时我们想在显示一个值之前做一些条件检查。但是,哈希变量不提供条件检查的能力。
通过查看 Joget Workflow目前支持的 插件类型,我们可以开发一个 哈希变量插件,让我们编写脚本来检查条件。有相当多的Bean Shell插件作为几种插件类型的默认插件提供。我们也可以为哈希变量插件做一个。
哈希变量插件不提供用户配置界面,但要开发一个Bean Shell哈希变量插件,我们需要在某处放置我们的Bean Shell脚本。我们可以重新使用 环境变量 来存储我们的脚本。所以哈希变量语法将是一个环境变量键的前缀。
E.g. #beanshell.EnvironmentVariableKey#
但是,这可能还不够,我们可能还需要一些其他方法来传递一些变量。我们可以考虑使用一个URL查询参数语法来传递我们的变量,因为以后可以更容易解析。
E.g. #beanshell.EnvironmentVariableKey[name=Joget&email=info@joget.org&message={form.sample.message?url}]#
我们期望从这个Bean Shell Hash变量插件中得到什么?Bean Shell Hash变量插件适用于管理员/开发人员用户在构建/开发应用程序时使用。一旦使用,哈希变量将被来自Bean Shell解释器的输出返回替换。以便管理员用户在向普通用户显示内容之前可以进行条件检查。
例如,显示登录用户的欢迎消息,但在用户为匿名时不显示任何内容。
要开发Bean Shell Hash变量插件,我们可以参考 所有的Hash变量插件和Bean Shell插件的 源代码。尤其是,我们可以参考环境变量哈希变量插件如何使用变量键来检索环境变量。我们也可以参考Bean Shell Tool或者Bean Shell Form Binder插件来说明如何使用Bean Shell解释器来执行脚本。
我们可以使用StringUtil中的 getUrlParams方法 来帮助我们解析使用URL查询参数语法传入的参数。
我们需要始终准备好Joget Workflow Source Code,并按照这个指导方针建立起来。
以下教程是使用Macbook Pro和Joget源代码5.0.0版编写的。 其他平台命令请参阅 如何开发插件文章。
假设我们的文件夹目录如下所示。
Code Block |
---|
- Home
- joget
- plugins
- jw-community
-5.0.0 |
“plugins”目录是我们要创建和存储我们所有插件的文件夹,“jw-community”目录是Joget Workflow源代码存储的地方。
运行以下命令在“plugins”目录下创建一个maven项目。
Code Block | ||
---|---|---|
| ||
cd joget/plugins/
~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial beanshell_hash_variable 5.0.0 |
然后,shell脚本会要求我们输入插件的版本号,并在生成maven项目之前要求我们确认。
Code Block | ||
---|---|---|
| ||
Define value for property 'version': 1.0-SNAPSHOT: : 5.0.0
[INFO] Using property: package = org.joget.tutorial
Confirm properties configuration:
groupId: org.joget.tutorial
artifactId: beanshell_hash_variable
version: 5.0.0
package: org.joget.tutorial
Y: : y |
我们应该在终端上显示“BUILD SUCCESS”消息,在“plugins”文件夹中创建一个“beanshell_hash_variable”文件夹。
用你喜欢的IDE打开maven项目。我将使用 NetBeans。
在“org.joget.tutorial”包下创建一个“BeanShellHashVariable”类。
然后,基于 哈希变量插件文件,我们将不得不扩展 org.joget.apps.app.model.DefaultHashVariablePlugin抽象类。
让我们实现所有的抽象方法。我们将使用AppPluginUtil.getMessage方法来支持i18n,并将常量变量MESSAGE_PATH用于消息资源包目录。
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
package org.joget.tutorial;
import org.joget.apps.app.model.DefaultHashVariablePlugin;
import org.joget.apps.app.service.AppPluginUtil;
public class BeanShellHashVariable extends DefaultHashVariablePlugin {
private final static String MESSAGE_PATH = "messages/BeanShellHashVariable";
public String getName() {
return "BeanShellHashVariable";
}
public String getVersion() {
return "5.0.0";
}
public String getClassName() {
return getClass().getName();
}
public String getLabel() {
//support i18n
return AppPluginUtil.getMessage("org.joget.tutorial.BeanShellHashVariable.pluginLabel", getClassName(), MESSAGE_PATH);
}
public String getDescription() {
//support i18n
return AppPluginUtil.getMessage("org.joget.tutorial.BeanShellHashVariable.pluginDesc", getClassName(), MESSAGE_PATH);
}
public String getPropertyOptions() {
//Hash variable plugin do not support property options
return "";
}
public String getPrefix() {
return "beanshell";
}
public String processHashVariable(String variableKey) {
throw new UnsupportedOperationException("Not supported yet.");
}
} |
现在,我们来关注我们的Hash变量插件的主要方法,它是processHashVariable。我们将参考环境变量哈希变量插件的源代码来了解如何检索环境变量。然后,参考Bean Shell Form Binder的源代码,了解如何执行一个bean shell脚本。
Code Block | ||
---|---|---|
| ||
public String processHashVariable(String variableKey) {
try {
String environmentVariableKey = variableKey;
|
Hash variable is convenient to use, but sometime we want to do some condition check before displaying a value. But, Hash variable does not provide the ability for condition checking.
By looking at the Plugin Types that are currently supported by Joget Workflow, we can develop a 哈希变量插件 to allow us to write our scripting for condition checking. There are quite a number of Bean Shell plugins provided as default plugin for several plugin types. We can do one for Hash Variable plugin as well.
Hash Variable plugin does not provide interface for user to configure, but to develop a Bean Shell Hash Variable plugin, we need somewhere to put our Bean Shell script. We can reuse the Environment Variable to store our script. So the Hash Variable syntax will be a prefix with environment variable key.
E.g. #beanshell.EnvironmentVariableKey#
But, this may not be enough, we may need some other way to pass in some variable also. We can consider using a URL query parameters syntax to pass in our variables because it is easier to parse later on.
E.g. #beanshell.EnvironmentVariableKey[name=Joget&email=info@joget.org&message={form.sample.message?url}]#
What do we expected from this Bean Shell Hash variable plugin? The Bean Shell Hash Variable plugin is for admin/developer user to use when building/developing an app. Once used, the Hash Variable will be replaced by the output return from the Bean Shell interpreter. So that the admin user can do condition check before display something to normal user.
E.g. Display a welcome message for logged in user but display nothing when the user is an anonymous.
To develop Bean Shell Hash Variable plugin, we can refer to the source code of all the Hash Variable plugin and Bean Shell plugin. Especially, we can refer to the Environment Variable Hash Variable plugin on how to retrieve environment variable using a variable key. We can also refer to the Bean Shell Tool or Bean Shell Form Binder plugin on what to execute the script with Bean Shell interpreter.
We can use getUrlParams method from StringUtil to help us parse parameters passed in with URL query parameters syntax.
We need to always have our Joget Workflow Source Code ready and builded by following this guideline.
The following tutorial is prepared with a Macbook Pro and Joget Source Code version 5.0.0. Please refer to the 如何开发插件 article for other platform commands.
Let say our folder directory is as following.
Code Block |
---|
- Home
- joget
- plugins
- jw-community
-5.0.0 |
The "plugins" directory is the folder we will create and store all our plugins and the "jw-community" directory is where the Joget Workflow Source code stored.
Run the following command to create a maven project in "plugins" directory.
Code Block | ||
---|---|---|
| ||
cd joget/plugins/
~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial beanshell_hash_variable 5.0.0 |
Then, the shell script will ask us to key in a version number for the plugin and ask us for a confirmation before it generates the maven project.
Code Block | ||
---|---|---|
| ||
Define value for property 'version': 1.0-SNAPSHOT: : 5.0.0
[INFO] Using property: package = org.joget.tutorial
Confirm properties configuration:
groupId: org.joget.tutorial
artifactId: beanshell_hash_variable
version: 5.0.0
package: org.joget.tutorial
Y: : y |
We should get "BUILD SUCCESS" message shown in our terminal and a "beanshell_hash_variable" folder created in "plugins" folder.
Open the maven project with your favour IDE. I will be using NetBeans.
Create a "BeanShellHashVariable" class under "org.joget.tutorial" package.
Then, based on 哈希变量插件 document, we will have to extends org.joget.apps.app.model.DefaultHashVariablePlugin abstract class.
Let us implement all the abstract methods. We will be using AppPluginUtil.getMessage method to support i18n and using constant variable MESSAGE_PATH for message resource bundle directory.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
package org.joget.tutorial; import org.joget.apps.app.model.DefaultHashVariablePlugin; import org.joget.apps.app.service.AppPluginUtil; public class BeanShellHashVariable extends DefaultHashVariablePlugin { private final static String MESSAGE_PATH = "messages/BeanShellHashVariable"; public String getName() { return "BeanShellHashVariable"; //first } check and retrieve parameters passed publicin Stringwith getVersion() { return "5.0.0"; URL query parameters syntax wrapped in square bracket [] } public String getClassName() { return getClass().getName()queryParams = null; } public String getLabel()if (variableKey.contains("[") && variableKey.contains("]")) { //support i18n queryParams return AppPluginUtil.getMessage("org.joget.tutorial.BeanShellHashVariable.pluginLabel", getClassName(), MESSAGE_PATH); } = variableKey.substring(variableKey.indexOf("[") + 1, variableKey.indexOf("]")); public String getDescription() { environmentVariableKey = //support i18nvariableKey.substring(0, variableKey.indexOf("[")); return AppPluginUtil.getMessage("org.joget.tutorial.BeanShellHashVariable.pluginDesc", getClassName(), MESSAGE_PATH); } public String getPropertyOptions() { //Hash variable plugin do not support property options Parse the query parameters to a map Map<String, String[]> parameters return= ""null; } if (queryParams != publicnull String getPrefix&& !queryParams.isEmpty()) { return "beanshell" parameters = StringUtil.getUrlParams(queryParams); } public String processHashVariable(String variableKey) { throw new UnsupportedOperationException("Not supported yet."); } } |
Now, let's focus on the main method of our Hash Variable plugin which is processHashVariable. We will refer to the source code of Environment Variable Hash Variable plugin on how to retrieve the Environment variable. Then, refer to the source code of Bean Shell Form Binder on how to execute a bean shell script.
Code Block | ||
---|---|---|
| ||
public String processHashVariable(String variableKey) { try { //put all parameters to plugin properties getProperties().putAll(parameters); } //Retrieve the environment variable based on environmentVariableKey StringAppDefinition environmentVariableKeyappDef = variableKey (AppDefinition) getProperty("appDefinition"); if (appDef != null) { //first check and retrieve parameters passed in with URLApplicationContext queryappContext parameters syntax wrapped in square bracket [] = AppUtil.getApplicationContext(); EnvironmentVariableDao String queryParamsenvironmentVariableDao = null; if (variableKey.contains("[") && variableKey.contains("]")) {(EnvironmentVariableDao) appContext.getBean("environmentVariableDao"); queryParamsEnvironmentVariable env = variableKeyenvironmentVariableDao.substring(variableKey.indexOf("[") + 1, variableKey.indexOf("]")); loadById(environmentVariableKey, appDef); if environmentVariableKey(env != variableKey.substring(0, variableKey.indexOf("["));null) { } String script = env.getValue(); //Parse the query parameters to a map //execute the Map<String, String[]> parameters = null;script with all plugin properties if (queryParams != null && !queryParams.isEmpty()) { return executeScript(script, getProperties()); parameters} = StringUtil.getUrlParams(queryParams);else { //environment variable not found, return empty value //put all parameters to plugin properties return ""; getProperties().putAll(parameters); } } } //Retrieve the environment} variablecatch based(Exception one) environmentVariableKey{ AppDefinition//log appDefthe =exception (AppDefinition) getProperty("appDefinition");using LogUtil if (appDef != null) {LogUtil.error(getClassName(), e, "#beanshell."+variableKey+"# fail to parse."); } ApplicationContext appContext = AppUtil.getApplicationContext(); //return null to by pass the replacing EnvironmentVariableDao environmentVariableDao = (EnvironmentVariableDao) appContext.getBean("environmentVariableDao")return null; } protected String executeScript(String script, EnvironmentVariableMap envproperties) = environmentVariableDao.loadById(environmentVariableKey, appDef);throws Exception { Interpreter interpreter = new Interpreter(); if (env != null) { interpreter.setClassLoader(getClass().getClassLoader()); for (Object key : properties.keySet()) { String script = env.getValue() interpreter.set(key.toString(), properties.get(key)); } //execute the script with all plugin propertiesLogUtil.debug(getClass().getName(), "Executing script " + script); return executeScript(script, getProperties())String) interpreter.eval(script); } else {} |
我们的插件类无法解析“bsh.Interpreter”。所以,我们必须将bean shell库添加到我们的POM文件中。
Code Block | ||
---|---|---|
| ||
<!-- Change plugin specific dependencies here --> <dependency> <groupId>org.beanshell</groupId> <artifactId>bsh</artifactId> <version>2.0b4</version> </dependency> <!-- End change plugin specific dependencies //environment variable not found, return empty value return ""; } } } catch (Exception e) { //log the exception using LogUtil LogUtil.error(getClassName(), e, "#beanshell."+variableKey+"# fail to parse."); } //return null to by pass the replacing return null; } protected String executeScript(String script, Map properties) throws Exception { Interpreter interpreter = new Interpreter(); interpreter.setClassLoader(getClass().getClassLoader()); for (Object key : properties.keySet()) { interpreter.set(key.toString(), properties.get(key)); } LogUtil.debug(getClass().getName(), "Executing script " + script); return (String) interpreter.eval(script); } |
Our plugin class cannot resolve "bsh.Interpreter". So, we will have to add bean shell library to our POM file.
Code Block | ||
---|---|---|
| ||
<!-- Change plugin specific dependencies here -->
<dependency>
<groupId>org.beanshell</groupId>
<artifactId>bsh</artifactId>
<version>2.0b4</version>
</dependency>
<!-- End change plugin specific dependencies here --> |
We are using AppPluginUtil.getMessage method to display i18n value for our getLabel and getDescription method. We will have to create a message resource bundle properties file for it. Create directory "resources/messages" under "beanshell_hash_variable/src/main" directory. Then, create a "BeanShellHashVariable.properties" file in the folder.
In our properties file, we will need to add the key we have used.
Code Block | ||
---|---|---|
| ||
org.joget.tutorial.BeanShellHashVariable.pluginLabel=Bean Shell Hash Variable
org.joget.tutorial.BeanShellHashVariable.pluginDesc=Using environment variable to execute bean shell script. |
We will have to register our plugin class in Activator class to tell the Felix Framework that this is a plugin.
Code Block | ||
---|---|---|
| ||
public void start(BundleContext context) {
registrationList = new ArrayList<ServiceRegistration>();
//Register plugin here
registrationList.add(context.registerService(BeanShellHashVariable.class.getName(), new BeanShellHashVariable(), null));
} |
Let build our plugin. Once the building process is done, we will find that a "beanshell_hash_variable-5.0.0.jar" file is created under "beanshell_hash_variable/target" directory.
Then, let's upload the plugin jar to Manage Plugins. After uploading the jar file, double check that the plugin is uploaded and activated correctly.
Now, let's test our plugin.
Let assume that we have a HTML menu page in a userview that wants to display the following line to logged in user. Normally, we will use "Welcome #currentUser,username#," to display a welcome message.
But, in this use case there is a problem, which shows "Welcome ," without an username when the user is an anonymous.
Now, change the whole message to our Bean Shell Hash Variable and create an environment variable to put our script.
Change:
Code Block |
---|
Welcome #currentUser.username#, |
to the following. We will need to pass the current user's username as one of our parameters and do not forget to escape it as url.
Code Block |
---|
#beanshell.welcome[username={currentUser.username?url}]?html# |
Then, we can create an environment variable with ID "welcome" and use the following script. As we are using getUrlParams method from StringUtil to parse the parameters, all value from parameters are String array.
here --> |
我们使用AppPluginUtil.getMessage方法来显示我们的getLabel和getDescription方法的i18n值。我们将不得不为它创建一个消息资源包属性文件。在“beanshell_hash_variable / src / main”目录下创建目录“resources / messages”。然后,在该文件夹中创建一个“BeanShellHashVariable.properties”文件。
在我们的属性文件中,我们将需要添加我们已经使用的密钥。
Code Block | ||
---|---|---|
| ||
org.joget.tutorial.BeanShellHashVariable.pluginLabel=Bean Shell Hash Variable
org.joget.tutorial.BeanShellHashVariable.pluginDesc=Using environment variable to execute bean shell script. |
我们将不得不在Activator类中注册我们的插件类,告诉Felix框架这是一个插件。
Code Block | ||
---|---|---|
| ||
public void start(BundleContext context) {
registrationList = new ArrayList<ServiceRegistration>();
//Register plugin here
registrationList.add(context.registerService(BeanShellHashVariable.class.getName(), new BeanShellHashVariable(), null));
} |
让我们建立我们的插件。构建过程完成后,我们将发现在“beanshell_hash_variable / target”目录下创建了“beanshell_hash_variable-5.0.0.jar”文件
然后,让我们上传插件jar到 管理插件。上传jar文件后,再次检查插件是否正确上传并激活。
现在,让我们测试我们的插件。
假设我们在一个用户视图中有一个HTML菜单页面,它想把下面的行显示给登录用户。通常,我们将使用“欢迎#currentUser,用户名#”来显示欢迎消息。
但是,在这个用例中存在一个问题,当用户是匿名用户时,显示“Welcome”,没有用户名。
现在,将整个消息更改为我们的Bean Shell哈希变量,并创建一个环境变量来放置我们的脚本。
更改:
Code Block |
---|
Welcome #currentUser.username#, |
以下。我们将需要传递当前用户的用户名作为我们的参数之一,不要忘记把它作为url转义。
Code Block |
---|
#beanshell.welcome[username={currentUser.username?url}]?html# |
然后,我们可以创建一个ID为“welcome”的环境变量,并使用下面的脚本。由于我们使用StringUtil的 getUrlParams方法 来解析参数,所以参数中的所有值都是String数组。
Code Block | ||
---|---|---|
| ||
//all parameters passed in from Beanshell Hash Variable will converted to String array if (username != null && username.length == 1 && !username[0].isEmpty()) { return "Welcome " + username[0] + ","; } else { return ""; } |
Let go back to our HTML menu page to see the result.
When user is logged in, it shows the message correctly.
让我们回到我们的HTML菜单页面来查看结果。
当用户登录时,它显示正确的消息。
当没有用户登录时,欢迎消息不显示。When no user is logged in, the welcome message is not shown.
...
您可以从You can download the source code from beanshell_hash_variable.zip.下载源代码。
要下载现成的插件jar,请在To download the ready-to-use plugin jar, please find it in http://marketplace.joget.org/.上找到它