plugin.xml

9.0插件引擎定义了主包的概念,指的是插件主要类(比如xml中描述的接入点)所在的包。所有9.0插件默认的主包为com.fr.plugin。考虑到兼容问题,8.0插件的主包默认为com.fr,但这也并不能囊括所有的老插件,所以允许在plugin.xml中添加<main-package>标签来自定义主包:

<main-package>com.personal.main</main-package>

此外,如果一个插件不允许在9.0中运行,则可修改其env-version标签:

<env-version>~8.0</env-version>

波浪号~表示"至",即该插件最高能在8.0中运行。没有波浪号的表示env-version表示最低要求的FineReport版本。

代码中添加对热部署的支持

9.0支持插件的热部署,在管理页面中,更新、删除、运行等插件操作不要求用户重启,所以需要插件支持热部署。

所有热部署相关的问题中,大部分已经被9.0插件引擎处理,剩下的就是资源释放和对象清理,需要插件支持。

那么,什么样的场景下需要插件自己释放资源、清理对象呢?

首先,对于java底层的一些持久对象,比如Timer、Executor、Socket等,插件引擎是没有办法感知并清理的。其次,如果在非FineReport的地方注册了对象,比如Swing监听,ServletContext全局属性等,插件引擎也无法感知。如果插件关闭后,这些定时器、对象等没有清理,会导致插件关闭不完全、占用系统资源等问题。

解决资源释放有两种方式:

1、Timer、Executor等使用FineReport中的接口进行定义,这样插件引擎就可以记录到当前插件有哪些需要在插件关闭后释放的对象。

Timer timer = PluginContexts.currentContext().newTimer();
Executor executor = PluginContexts.currentContext().newSingleThreadExecutor();

2、使用插件引擎定义的RecoverableTask来创建、注册,并完善清理方法

PluginContexts.currentContext().executeRecoverable(new RecoverableTask() {

    @Override
    public void execute() {
        timer = new Timer();
    }

    @Override
    public void undo() {
        timer.cancel();
    }
});
 
PluginContexts.currentContext().executeRecoverable(new RecoverableTask() {
     
    @Override
    public void execute() {
        //添加Swing监听,该方法与任务提交同步执行
        addSwingListener();
    }
     
    @Override
    public void undo() {
        //移除监听,该方法在插件stop时执行
        removeSwingListener();
    }
});

适配ClassLoader结构的改变

不同于8.0中所有插件和FineReport共用ClassLoader,9.0引擎中为每个插件提供了一个独立的ClassLoader。这点改变对于插件编写影响不大,只有一点读取资源的问题,有部分老插件直接使用的报表的类加载器来加载资源,如:

GeneralContext.class.getResourceAsStream(somePluginResourceName);

在9.0中,这种获取资源的方法只能得到FineReport的资源,而无法找到插件本身的资源。如果有这种写法,需要修改为使用自己的ClassLoader,如:

this.getClass().getClassLoader().getResourceAsStream(resourceName);

这种写法不仅可以获取到插件自身的资源,同时也能获取到FineReport的资源。