Joget DX 8 Stable Released
The stable release for Joget DX 8 is now available, with a focus on UX and Governance.
在本教程中,我们将遵循开发插件 来开发我们的JDBC选项活页夹插件的 指导原则。请参阅第一个教程 如何开发Bean Shell Hash变量 以获取更多详细信息步骤。
有时,我们可能需要编写一些自定义查询来填充多选项字段的选项。
Joget Workflow提供了一个名为Form Options Binder Plugin的插件类型 。我们将开发一个支持JDBC连接和自定义查询。
要开发一个JDBC选项绑定器,我们将需要JDBC连接设置以及自定义查询来填充选项。
查询还应该支持在使用AJAX时注入依赖关系值的语法。
例:
返回的JDBC结果的第一列将是选项的值,第二列是选项的标签。当不使用AJAX级联下拉列表时,会有另一个可选的第三列进行分组。
我们可以参考其他可用的Form Options Binder插件的实现。可以使用AppUtil.getApplicationContext().getBean("setupDataSource") 来检索Joget的默认数据源。
我们需要始终准备好我们的Joget工作流源代码,并遵循 这个指导原则。
以下教程是使用Macbook Pro和Joget源代码5.0.0版编写的。请参阅 为其他平台命令开发插件文章的 指南。
假设我们的文件夹目录如下所示。
- Home - joget - plugins - jw-community -5.0.0
“插件”目录是我们将创建和存储我们所有插件的文件夹,“jw-community”目录是Joget Workflow源代码的存储位置。
运行以下命令在“plugins”目录下创建一个maven项目。
cd joget/plugins/ ~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial jdbc_options_binder 5.0.0
然后,shell脚本会要求我们输入插件的版本号,并在生成maven项目之前请求确认。
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: jdbc_options_binder version: 5.0.0 package: org.joget.tutorial Y: : y
我们应该在终端上显示“BUILD SUCCESS”消息,在“plugins”文件夹中创建一个“jdbc_options_binder”文件夹。
用你喜欢的IDE打开maven项目。我将使用 NetBeans。
在“org.joget.tutorial”包下创建一个“JdbcOptionsBinder”类。然后,使用org.joget.apps.form.model.FormBinder 抽象类来扩展 该类。
为了使其作为Form Options Binder工作,我们需要实现org.joget.apps.form.model.FormLoadOptionsBinder接口。我们也想支持AJAX Cascading Drop-Down List ,所以我们还需要实现 org.joget.apps.form.model.FormAjaxOptionsBinder 接口。
请参阅 Form Options Binder Plugin.
像往常一样,我们必须执行所有的抽象方法。我们将使用AppPluginUtil.getMessage方法来支持i18n,并将常量变量MESSAGE_PATH用于消息资源包目录。
然后,我们必须为管理员用户创建一个UI,为我们的插件提供输入。在getPropertyOptions方法中,我们已经指定我们的 插件属性选项 定义文件位于“/properties/jdbcOptionsBinder.json”。让我们在“jdbc_options_binder / src / main”目录下创建一个“resources / properties”目录。创建目录后,在“properties”文件夹中创建一个名为“jdbcOptionsBinder.json”的文件。
在属性定义选项文件中,我们需要提供如下的选项。请注意,我们将在我们的属性选项中使用“ @@message.key@@”语法来支持i18n。
[{ title : '@@form.jdbcOptionsBinder.config@@', properties : [{ name : 'jdbcDatasource', label : '@@form.jdbcOptionsBinder.datasource@@', type : 'selectbox', options : [{ value : 'custom', label : '@@form.jdbcOptionsBinder.customDatasource@@' },{ value : 'default', label : '@@form.jdbcOptionsBinder.defaultDatasource@@' }], value : 'default' },{ name : 'jdbcDriver', label : '@@form.jdbcOptionsBinder.driver@@', description : '@@form.jdbcOptionsBinder.driver.desc@@', type : 'textfield', value : 'com.mysql.jdbc.Driver', control_field: 'jdbcDatasource', control_value: 'custom', control_use_regex: 'false', required : 'true' },{ name : 'jdbcUrl', label : '@@form.jdbcOptionsBinder.url@@', type : 'textfield', value : 'jdbc:mysql://localhost/jwdb?characterEncoding=UTF8', control_field: 'jdbcDatasource', control_value: 'custom', control_use_regex: 'false', required : 'true' },{ name : 'jdbcUser', label : '@@form.jdbcOptionsBinder.username@@', type : 'textfield', control_field: 'jdbcDatasource', control_value: 'custom', control_use_regex: 'false', value : 'root', required : 'true' },{ name : 'jdbcPassword', label : '@@form.jdbcOptionsBinder.password@@', type : 'password', control_field: 'jdbcDatasource', control_value: 'custom', control_use_regex: 'false', value : '' },{ name : 'useAjax', label : '@@form.jdbcOptionsBinder.useAjax@@', type : 'checkbox', options : [{ value : 'true', label : '' }] },{ name : 'addEmpty', label : '@@form.jdbcOptionsBinder.addEmpty@@', type : 'checkbox', options : [{ value : 'true', label : '' }] },{ name : 'emptyLabel', label : '@@form.jdbcOptionsBinder.emptyLabel@@', type : 'textfield', control_field: 'addEmpty', control_value: 'true', control_use_regex: 'false', value : '' },{ name : 'sql', label : '@@form.jdbcOptionsBinder.sql@@', description : '@@form.jdbcOptionsBinder.sql.desc@@', type : 'codeeditor', mode : 'sql', required : 'true' }], buttons : [{ name : 'testConnection', label : '@@form.jdbcOptionsBinder.testConnection@@', ajax_url : '[CONTEXT_PATH]/web/json/app[APP_PATH]/plugin/org.joget.tutorial.JdbcOptionsBinder/service?action=testConnection', fields : ['jdbcDriver', 'jdbcUrl', 'jdbcUser', 'jdbcPassword'], control_field: 'jdbcDatasource', control_value: 'custom', control_use_regex: 'false' }] }]
在“属性选项”中,我们添加了一个按钮,用于在使用自定义数据源时测试连接。这个按钮会调用一个JSON API来做测试。所以,我们的插件将需要实现 org.joget.plugin.base.PluginWebSupport 接口,使其成为一个 Web服务插件。让我们按如下方式实现webService方法来测试JDBC连接。
/** * JSON API for test connection button * @param request * @param response * @throws ServletException * @throws IOException */ public void webService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Limit the API for admin usage only boolean isAdmin = WorkflowUtil.isCurrentUserInRole(WorkflowUserManager.ROLE_ADMIN); if (!isAdmin) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } String action = request.getParameter("action"); if ("testConnection".equals(action)) { String message = ""; Connection conn = null; try { AppDefinition appDef = AppUtil.getCurrentAppDefinition(); String jdbcDriver = AppUtil.processHashVariable(request.getParameter("jdbcDriver"), null, null, null, appDef); String jdbcUrl = AppUtil.processHashVariable(request.getParameter("jdbcUrl"), null, null, null, appDef); String jdbcUser = AppUtil.processHashVariable(request.getParameter("jdbcUser"), null, null, null, appDef); String jdbcPassword = AppUtil.processHashVariable(SecurityUtil.decrypt(request.getParameter("jdbcPassword")), null, null, null, appDef); Properties dsProps = new Properties(); dsProps.put("driverClassName", jdbcDriver); dsProps.put("url", jdbcUrl); dsProps.put("username", jdbcUser); dsProps.put("password", jdbcPassword); DataSource ds = BasicDataSourceFactory.createDataSource(dsProps); conn = ds.getConnection(); message = AppPluginUtil.getMessage("form.jdbcOptionsBinder.connectionOk", getClassName(), MESSAGE_PATH); } catch (Exception e) { LogUtil.error(getClassName(), e, "Test Connection error"); message = AppPluginUtil.getMessage("form.jdbcOptionsBinder.connectionFail", getClassName(), MESSAGE_PATH) + "\n" + e.getMessage(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (Exception e) { LogUtil.error(DynamicDataSourceManager.class.getName(), e, ""); } } try { JSONObject jsonObject = new JSONObject(); jsonObject.accumulate("message", message); jsonObject.write(response.getWriter()); } catch (Exception e) { //ignore } } else { response.setStatus(HttpServletResponse.SC_NO_CONTENT); } }
一旦我们完成了用于收集输入的属性选项和用于测试连接的Web服务,我们可以使用loadAjaxOptions方法的插件的主要方法。
public FormRowSet loadAjaxOptions(String[] dependencyValues) { FormRowSet rows = new FormRowSet(); rows.setMultiRow(true); //add empty option based on setting if ("true".equals(getPropertyString("addEmpty"))) { FormRow empty = new FormRow(); empty.setProperty(FormUtil.PROPERTY_LABEL, getPropertyString("emptyLabel")); empty.setProperty(FormUtil.PROPERTY_VALUE, ""); rows.add(empty); } //Check the sql. If require dependency value and dependency value is not exist, return empty result. String sql = getPropertyString("sql"); if ((dependencyValues == null || dependencyValues.length == 0) && sql.contains("?")) { return rows; } Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { DataSource ds = createDataSource(); con = ds.getConnection(); //support for multiple dependency values if (sql.contains("?") && dependencyValues != null && dependencyValues.length > 1) { String mark = "?"; for (int i = 1; i < dependencyValues.length; i++) { mark += ", ?"; } sql = sql.replace("?", mark); } pstmt = con.prepareStatement(sql); //set query parameters if (sql.contains("?") && dependencyValues != null && dependencyValues.length > 0) { for (int i = 0; i < dependencyValues.length; i++) { pstmt.setObject(i + 1, dependencyValues[i]); } } rs = pstmt.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int columnsNumber = rsmd.getColumnCount(); // Set retrieved result to Form Row Set while (rs.next()) { FormRow row = new FormRow(); String value = rs.getString(1); String label = rs.getString(2); row.setProperty(FormUtil.PROPERTY_VALUE, (value != null)?value:""); row.setProperty(FormUtil.PROPERTY_LABEL, (label != null)?label:""); if (columnsNumber > 2) { String grouping = rs.getString(3); row.setProperty(FormUtil.PROPERTY_GROUPING, grouping); } rows.add(row); } } catch (Exception e) { LogUtil.error(getClassName(), e, ""); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } if (con != null) { con.close(); } } catch (Exception e) { LogUtil.error(getClassName(), e, ""); } } return rows; } /** * To creates data source based on setting * @return * @throws Exception */ protected DataSource createDataSource() throws Exception { DataSource ds = null; String datasource = getPropertyString("jdbcDatasource"); if ("default".equals(datasource)) { // use current datasource ds = (DataSource)AppUtil.getApplicationContext().getBean("setupDataSource"); } else { // use custom datasource Properties dsProps = new Properties(); dsProps.put("driverClassName", getPropertyString("jdbcDriver")); dsProps.put("url", getPropertyString("jdbcUrl")); dsProps.put("username", getPropertyString("jdbcUser")); dsProps.put("password", getPropertyString("jdbcPassword")); ds = BasicDataSourceFactory.createDataSource(dsProps); } return ds; }
我们的插件使用dbcp,javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse类,所以我们需要将jsp-api和commons-dbcp库添加到我们的POM文件中。
<!-- Change plugin specific dependencies here --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.3</version> </dependency> <!-- End change plugin specific dependencies here -->
我们在getLabel和getDescription方法中使用i18n消息密钥。我们还在我们的属性选项定义中使用了i18n消息密钥。所以,我们需要为我们的插件创建一个消息资源包属性文件。
在“jdbc_options_binder / src / main”目录下创建目录“resources / messages”。然后,在该文件夹中创建一个“JdbcOptionsBinder.properties”文件。在属性文件中,让我们添加所有的消息键和标签如下。
org.joget.tutorial.JdbcOptionsBinder.pluginLabel=JDBC Binder org.joget.tutorial.JdbcOptionsBinder.pluginDesc=Used to load field's options using JDBC form.jdbcOptionsBinder.config=Configure JDBC Binder form.jdbcOptionsBinder.datasource=Datasource form.jdbcOptionsBinder.customDatasource=Custom Datasource form.jdbcOptionsBinder.defaultDatasource=Default Datasource form.jdbcOptionsBinder.driver=Custom JDBC Driver form.jdbcOptionsBinder.driver.desc=Eg. com.mysql.jdbc.Driver (MySQL), oracle.jdbc.driver.OracleDriver (Oracle), com.microsoft.sqlserver.jdbc.SQLServerDriver (Microsoft SQL Server) form.jdbcOptionsBinder.url=Custom JDBC URL form.jdbcOptionsBinder.username=Custom JDBC Username form.jdbcOptionsBinder.password=Custom JDBC Password form.jdbcOptionsBinder.useAjax=Use AJAX for cascade options? form.jdbcOptionsBinder.addEmpty=Add Empty Option? form.jdbcOptionsBinder.emptyLabel=Empty Option Label form.jdbcOptionsBinder.sql=SQL SELECT Query form.jdbcOptionsBinder.sql.desc=Use question mark (?) in your query to represent dependency values when using AJAX form.jdbcOptionsBinder.testConnection=Test Connection form.jdbcOptionsBinder.connectionOk=Database connected form.jdbcOptionsBinder.connectionFail=Not able to establish connection.
我们将需要在Activator类(在同一个类包中自动生成)中注册我们的插件类,告诉Felix框架这是一个插件。
public void start(BundleContext context) { registrationList = new ArrayList<ServiceRegistration>(); //Register plugin here registrationList.add(context.registerService(JdbcOptionsBinder.class.getName(), new JdbcOptionsBinder(), null)); }
让我们建立我们的插件。一旦构建过程完成,我们将发现在“jdbc_options_binder / target”目录下创建了一个“jdbc_options_binder-5.0.0.jar”文件。
然后,让我们上传插件jar到 管理插件。上传jar文件后,再次检查插件是否正确上传并激活。
然后,让我们 在表单中创建一个 AJAX级联下拉列表来测试它。我们来创建我们的测试表单,如下所示。
然后,配置我们的选择框和JDBC绑定。
在查询中,我们将使用以下查询来获取基于组ID的用户列表。
select distinct username, firstName, groupId from dir_user u join dir_user_group g on u.username=g.userId where groupId in (?) group by username;
将依赖关系配置为“group”。然后,测试结果。
用户选择框选项根据组选择框的选定值进行更改。
现在,让我们将查询更改为以下内容,以便在不使用AJAX的情况下测试级联下拉列表。
select distinct username, firstName, groupId from dir_user u join dir_user_group g on u.username=g.userId group by username;
请记住取消勾选“ 使用AJAX进行级联选项? ”选项,以使其不使用AJAX。
是的,它也可以工作。然后,我们可以测试自定义配置和测试连接按钮。
您可以从jdbc_options_binder_src.zip下载源代码 。
要下载现成的插件jar,请在http://marketplace.joget.org/上找到它 。