在本教程中,我们将遵循开发插件的  指南  来开发我们的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