Joget DX Preview Release Available for Download

Check out Joget DX, the next generation successor to Joget Workflow for faster, simpler digital transformation.

Page tree
Skip to end of metadata
Go to start of metadata

 

在本教程中,我们将遵循开发插件的  指南  来开发我们的Amazon S3 Datalist Binder插件。 有关更多详细信息步骤,请参阅第一个教程  如何开发一个Bean Shell哈希变量插件

1.什么问题?

我们要检索Amazon S3中的文件信息。

2.如何解决问题?

我们可以为此开发一个Datalist Binder插件用户界面菜单插件在本教程中,我们将开发一个  Datalist Binder插件  来检索文件信息并使用列表设计器填充它

Datalist Binder不适合用于此目的的插件类型,因为Amazon S3客户端API无法获取文件的总数,也无法支持排序,分页和过滤等数据列表操作。我们这样写是为了学习的目的,不鼓励生产使用,因为它会有性能问题。

更好的方法是开发一个用户视图菜单,该菜单可以将文件显示为树结构,并在扩展树时加载额外的文件。

3.你的插件需要什么输入?

要开发Amazon S3 Datalist Binder插件,我们需要提供如下输入:

  1. Amazon S3 API访问密钥
  2. Amazon S3 API的秘密
  3. 亚马逊S3地区
  4. Amazon S3 Bucket
  5. 检索文件列表的文件夹/前缀(可选)

我们将在这里做一些不同的事情,因为一些输入将被放置在属性文件中,并在需要时从属性文件中检索。请参考如何在  文件上传表单元素与Amazon S3集成

4.你的插件的输出和预期结果是什么?

一个数据列表,它将根据配置列出Amazon S3存储桶中的文件。

5.是否有任何资源/ API可以重复使用?

我们将使用适用于Java的  AWS开发工具包

6.准备你的开发环境

我们需要始终准备好我们的Joget工作流程源代码,并按照这个指导方针建立起来  。 

下面的教程是用Macbook Pro编写的,Joget源代码是5.0.1版本。 其他平台命令请参阅  如何开发插件文章。

假设我们的文件夹目录如下所示。 

- Home
  - joget
    - plugins
    - jw-community
      -5.0.1

“插件”目录是我们将创建和存储我们所有插件的文件夹,“jw-community”目录是Joget Workflow源代码的存储位置。

运行以下命令在“plugins”目录下创建一个maven项目。

cd joget/plugins/
~/joget/jw-community/5.0.1/wflow-plugin-archetype/create-plugin.sh org.joget amazon_s3_datalist_binder 5.0.1

然后,shell脚本会要求我们输入插件的版本号,并在生成maven项目之前要求我们确认。

Define value for property 'version':  1.0-SNAPSHOT: : 5.0.0
[INFO] Using property: package = org.joget
Confirm properties configuration:
groupId: org.joget
artifactId: amazon_s3_datalist_binder
version: 5.0.0
package: org.joget
Y: : y

我们应该在终端上显示“BUILD SUCCESS”消息,并在“plugins”文件夹中创建一个“amazon_s3_datalist_binder”文件夹。

用你最喜欢的IDE打开maven项目。我将使用  NetBeans。  

7.只需编码!

a.扩展插件类型的抽象类

在“org.joget”包下创建一个“AmazonS3DatalistBinder”类。然后,使用org.joget.apps.datalist.model.DataListBinderDefault  抽象类来扩展  该类。请参阅  Datalist活页夹插件我们还需要实现  org.joget.plugin.base.PluginWebSupport  接口类,以在插件属性页面中提供Ajax验证。请参考  Web服务插件

b.实现所有的抽象方法

像往常一样,我们必须执行所有的抽象方法。我们将使用AppPluginUtil.getMessage方法来支持i18n,并使用常量变量MESSAGE_PATH作为消息资源包目录。

Implementation of all basic abstract methods
package org.joget;
  
import org.joget.apps.app.service.AppPluginUtil;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.datalist.model.DataListBinderDefault;
import org.joget.plugin.base.PluginWebSupport;
 
public class AmazonS3DatalistBinder extends DataListBinderDefault implements PluginWebSupport {
    private final static String MESSAGE_PATH = "message/AmazonS3DatalistBinder";
     
    @Override
    public String getName() {
        return "Amazon S3 Datalist Binder";
    }
    @Override
    public String getVersion() {
        return "5.0.0";
    }
     
    @Override
    public String getClassName() {
        return getClass().getName();
    }
     
    @Override
    public String getLabel() {
        //support i18n
        return AppPluginUtil.getMessage("org.joget.AmazonS3DatalistBinder.pluginLabel", getClassName(), MESSAGE_PATH);
    }
    @Override
    public String getDescription() {
        //support i18n
        return AppPluginUtil.getMessage("org.joget.AmazonS3DatalistBinder.pluginDesc", getClassName(), MESSAGE_PATH);
    }
     
    @Override
    public String getPropertyOptions() {
        return AppUtil.readPluginResource(getClass().getName(), "/properties/amazonS3DatalistBinder.json", null, true, MESSAGE_PATH);
    }
 
}

现在,我们必须为管理员用户创建一个UI,为我们的插件提供输入。在getPropertyOptions方法中,我们已经指定了我们的  插件属性选项和配置  定义文件位于“/properties/amazonS3DatalistBinder.json”。让我们在“amazon_s3_datalist_binder / src / main”目录下创建一个目录“resources / properties”。创建目录后,在“properties”文件夹中创建一个名为“amazonS3DatalistBinder.json”的文件。

在属性定义选项文件中,我们需要提供如下的选项。请注意,我们可以在我们的属性选项中使用“@@ message.key @@”语法来支持i18n。如前所述,某些属性将放入一个属性文件中,因此我们的插件属性选项和配置  定义文件中只存在一个  属性我们将通过AJAX验证来验证属性文件是否存在并能够连接到Amazon S3服务。

[{
    title : '@@AmazonS3DatalistBinder.config@@',
    properties : [{
        name : 'folder',
        label : '@@AmazonS3DatalistBinder.folder@@',
        type : 'textfield'
    }],
    validators : [{ 
        type : 'AJAX',
        url : '[CONTEXT_PATH]/web/json/app[APP_PATH]/plugin/org.joget.AmazonS3DatalistBinder/service?action=validate'
    }]
}]

其他属性将放在  awsS3.properties  文件中。这个属性文件将需要放在您的wflow文件夹中。

完成收集输入的属性选项后,我们可以使用getColumns,getPrimaryKeyColumnName,getData和getDataTotalRowCount方法来处理插件的主要方法。

protected static AmazonS3 s3;
protected static Properties properties;
protected static DataListColumn[] columns;
protected List<Map<String, Object>> cacheData;
 
protected static String getPropertiesPath() {
    return SetupManager.getBaseSharedDirectory() + "awsS3.properties";
}
 
public static AmazonS3 getClient() throws Exception {
    if (s3 == null) {
        FileInputStream fis = null;
        try {
            properties = new Properties();
            fis = new FileInputStream(new File(getPropertiesPath()));
            properties.load(fis);
             
            BasicAWSCredentials awsCreds = new BasicAWSCredentials(properties.getProperty("access_key_id"), properties.getProperty("secret_access_key"));
            s3 = new AmazonS3Client(awsCreds);
         
         
            Region region = Region.getRegion(Regions.fromName(properties.getProperty("region")));
            s3.setRegion(region);
             
            if (!s3.doesBucketExist(properties.getProperty("bucket"))) {
                Bucket bucket = s3.createBucket(properties.getProperty("bucket"));
                if (bucket == null) {
                    throw new RuntimeException(AppPluginUtil.getMessage("AmazonS3DatalistBinder.bucketFilToCreate", AmazonS3DatalistBinder.class.getName(), MESSAGE_PATH));
                }
            }
        } catch (Exception e) {
            LogUtil.error(AmazonS3DatalistBinder.class.getName(), e, "");
            s3 = null;
             
            if (e instanceof FileNotFoundException) {
                throw new RuntimeException(AppPluginUtil.getMessage("AmazonS3DatalistBinder.configureationFileIsMissing", AmazonS3DatalistBinder.class.getName(), MESSAGE_PATH));
            } else {
                throw e;
            }
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (Exception e) {}
        }
    }
    return s3;
}
 
protected List<Map<String, Object>> getCacheData() {
    if (cacheData == null) {
        cacheData = new ArrayList<Map<String, Object>>();
        try {
            AmazonS3 client = getClient();
            String prefix = getPropertyString("folder");
            if (prefix.isEmpty()) {
                prefix = null;
            }
            ObjectListing listing = client.listObjects(properties.getProperty("bucket"), prefix);
            boolean cont;
            do {
                cont = false;
                 
                List<S3ObjectSummary> summaries = listing.getObjectSummaries();
                for (S3ObjectSummary s : summaries) {
                    Map<String, Object> obj = new HashMap<String, Object>();
                    String key = s.getKey();
                    int pos = key.lastIndexOf("/");
                    obj.put("key", key);
                    obj.put("path", (pos > 0)?(key.substring(0, pos-1)):"");
                    obj.put("filename", (pos > 0)?(key.substring(pos+1)):key);
                    obj.put("owner", s.getOwner().getDisplayName());
                    obj.put("md5", s.getETag());
                    obj.put("size", s.getSize());
                    obj.put("storageClass", s.getStorageClass());
                    obj.put("lastModified", s.getLastModified());
                     
                    cacheData.add(obj);
                }
                 
                if (listing.isTruncated()) {
                    cont = true;
                    listing = client.listNextBatchOfObjects(listing);
                }
            } while (cont);
        } catch (Exception e) {}
    }
    return cacheData;
}
 
public DataListColumn[] getColumns() {
    if (columns == null) {
        Collection<DataListColumn> list = new ArrayList<DataListColumn>();
        list.add(new DataListColumn("key", AppPluginUtil.getMessage("AmazonS3DatalistBinder.key", getClassName(), MESSAGE_PATH), true));
        list.add(new DataListColumn("path", AppPluginUtil.getMessage("AmazonS3DatalistBinder.path", getClassName(), MESSAGE_PATH), true));
        list.add(new DataListColumn("filename", AppPluginUtil.getMessage("AmazonS3DatalistBinder.filename", getClassName(), MESSAGE_PATH), true));
        list.add(new DataListColumn("owner", AppPluginUtil.getMessage("AmazonS3DatalistBinder.owner", getClassName(), MESSAGE_PATH), true));
        list.add(new DataListColumn("md5", AppPluginUtil.getMessage("AmazonS3DatalistBinder.md5", getClassName(), MESSAGE_PATH), false));
        list.add(new DataListColumn("size", AppPluginUtil.getMessage("AmazonS3DatalistBinder.size", getClassName(), MESSAGE_PATH), true));
        list.add(new DataListColumn("storageClass", AppPluginUtil.getMessage("AmazonS3DatalistBinder.storageClass", getClassName(), MESSAGE_PATH), true));
        list.add(new DataListColumn("lastModified", AppPluginUtil.getMessage("AmazonS3DatalistBinder.lastModified", getClassName(), MESSAGE_PATH), true));
         
        columns = list.toArray(new DataListColumn[]{});
    }
    return columns;
}
 
public String getPrimaryKeyColumnName() {
    return "key";
}
 
public DataListCollection getData(DataList dataList, Map properties, DataListFilterQueryObject[] filterQueryObjects, String sort, Boolean desc, Integer start, Integer rows) {
    //TODO: handle filterQueryObjects
     
    List list = PagingUtils.sortAndPage(getCacheData(), sort, desc, start, rows);
    DataListCollection data = new DataListCollection();
    data.addAll(list);
     
    return data;
}
public int getDataTotalRowCount(DataList dataList, Map properties, DataListFilterQueryObject[] filterQueryObjects) {
    //TODO: handle filterQueryObjects
     
    return getCacheData().size();
}

在我们的插件属性中,我们有一个AJAX验证来测试属性文件和连接到Amazon S3客户端。让我们实现webService方法来提供一个用于验证的API。

 public void webService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 boolean isAdmin = WorkflowUtil.isCurrentUserInRole(WorkflowUserManager.ROLE_ADMIN);
 if (!isAdmin) {
 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
 return;
 }
 
 String action = request.getParameter("action");
 if ("validate".equals(action)) {
 String message = "";
 boolean success = true;
 try {
 AmazonS3DatalistBinder.getClient();
 } catch (Exception e) {
 LogUtil.error(this.getClassName(), e, "");
 success = false;
 message = StringUtil.escapeString(e.getMessage(), StringUtil.TYPE_JAVASCIPT, null);
 }
 try {
 JSONObject jsonObject = new JSONObject();
 jsonObject.accumulate("status", (success?"success":"fail"));
 JSONArray messageArr = new JSONArray();
 messageArr.put(message);
 jsonObject.put("message", messageArr);
 jsonObject.write(response.getWriter());
 } catch (Exception e) {
 //ignore
 }
 } else {
 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
 }
}

c.管理你的插件的依赖库

我们需要在我们的POM文件中包含“jsp-api”和“aws-java-sdk-s3”库。

<!-- Change plugin specific dependencies here -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-s3</artifactId>
    <version>1.10.56</version>
</dependency>
<!-- End change plugin specific dependencies here -->

d。让你的插件国际化(国际化)

我们在getLabel和getDescription方法中使用i18n消息密钥。我们将在我们的属性选项定义中使用i18n消息密钥。然后,我们将需要为我们的插件创建一个消息资源包属性文件。

在“amazon_s3_datalist_binder / src / main”目录下创建一个目录“resources / message”。然后,在该文件夹中创建一个“AmazonS3DatalistBinder.properties”文件。在属性文件中,添加所有消息密钥及其标签,如下所示。

org.joget.AmazonS3DatalistBinder.pluginLabel=Amazon S3 Datalist Binder
org.joget.AmazonS3DatalistBinder.pluginDesc=Used to retrieve the available files in Amazon S3.
AmazonS3DatalistBinder.config=Configure Amazon S3 Datalist Binder
AmazonS3DatalistBinder.configureationFileIsMissing=AWS S3 configuration file is missing.
AmazonS3DatalistBinder.bucketFilToCreate=AWS Bucket fail to create.
AmazonS3DatalistBinder.key=Key
AmazonS3DatalistBinder.path=Path
AmazonS3DatalistBinder.filename=File Name
AmazonS3DatalistBinder.owner=Owner
AmazonS3DatalistBinder.md5=MD5 Hash
AmazonS3DatalistBinder.size=Size
AmazonS3DatalistBinder.storageClass=Storage Class
AmazonS3DatalistBinder.lastModified=Last Modified
AmazonS3DatalistBinder.folder=Folder

e. 注册你的插件到Felix框架

接下来,我们将需要在Activator类(在同一个类包中自动生成)中注册我们的插件类,以告诉Felix框架这是一个插件。

 public void start(BundleContext context) {
 registrationList = new ArrayList<ServiceRegistration>();
 //Register plugin here
 registrationList.add(context.registerService(AmazonS3DatalistBinder.class.getName(), new AmazonS3DatalistBinder(), null));
}

f.建立它并测试

让我们建立我们的插件。构建过程完成后,我们将在“amazon_s3_datalist_binder / target”目录下找到一个“amazon_s3_datalist_binder-5.0.0.jar”文件。

然后,让我们上传插件jar到  管理插件上传jar文件后,再次检查插件是否正确上传并激活。

检查Amazon S3 Datalist Binder插件可在  列表设计器中找到

选择并配置Amazon S3 Datalist活页夹。

按下确定。它的awsS3.properties  属性文件丢失或无效。将显示错误消息。

设计数据表。

检查结果。

8.再进一步,分享或出售

您可以从amazon_s3_datalist_binder_src.zip下载源代码  

 

  • No labels