以下内容参考:setup/HowToCreatePluginsInProM – prom

可以参考视频教程:流程挖掘开发,ProM Workshop 介绍_哔哩哔哩_bilibili,开发部分只需看part1-part3,字幕是自制的,若有错误欢迎评论区纠正 ^-^

Hello World插件开发初探

插件定义

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;public class HelloWorld {@Plugin(name = "My Hello World Plugin", parameterLabels = {}, returnLabels = { "Hello world string" }, returnTypes = { String.class }, userAccessible = true, help = "Produces the string: 'Hello world'")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static String helloWorld(PluginContext context) {return "Hello World";}
}
  • helloworld方法包含了插件的逻辑,输入参数PluginContext类型是必须的。
  • @Plugin 插件通过该注解表示,作用于方法上,该方法是插件的逻辑。@Plugin的属性也即插件的属性:
插件 属性 作用 例子
@Plugin name        插件名 My Hello World Plugin
parameterLabels 插件的输入的标签 本例中没指明
returnLabels 给插件输出的对象的标签列表,一个输出对象对应一个标签 本例返回一个字符串,标签为Hello world string
returnTypes 插件输出对象的类型 String.class
userAccessible 是否用户可以访问的 一般情况设为true
help 说明如何使用该插件(可选) Produces the string: 'Hello world'

ProM框架启动时,会扫描所有class文件中的@Plugin注解,并对应注册一个插件。

  • @UITopiaViant 注解通知ProM GUI此插件的存在,其属性有:
插件 属性 作用 例子
@UITopiaViant affiliation 作者的组织
author 作者名
email 作者邮箱
  • 插件运行效果:①运行ProM with UITopia (GettingStarted).launch

找到上面写的插件,点击Start运行。当有注解@UITopiaViant ​​​​​​时,ProM tools可以找到对应的插件,插件名称是@Plugin中的name。插件列表显示绿色表示该插件可以运行,这里是因为我们这个插件不需要input,自然是可以运行的;若为黄褐色,则表示缺乏需要的输入,因此不能运行。

运行效果:

多输出

前面说了@Plugin的returnLabels和returnTypes属性控制插件的输出,输出是一个列表,输出的标签、类型和顺序由它们确定。

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;public class HelloWorld2 {@Plugin(name = "My 2nd Hello World Plug-in", parameterLabels = {}, returnLabels = {"Hello string", "Number", "Worlds string" }, returnTypes = {String.class, Integer.class, String.class }, userAccessible = true, help = "Produces three objects: 'Hello', number, 'world'")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object[] helloWorlds(PluginContext context) {return new Object[] { "Hello", new Integer(6), "Worlds" };}
}

执行My 2nd Hello World Plug-in后输出三个Object(Integer没有实现可视化)

在方法中,输出一个Object[]数组类型,在这里没有对类型进行检查,但是返回类型必须在returnLabels明确,ProM在执行插件时才能检查类型是否正常,并在不正确时抛出一个异常(将上面数组第三个元素改成3,输出以下效果)。

多输入

前面说了@Plugin的parameterLabels属性说明插件的输入的标签,不同于输出,输入的类型和顺序由方法参数确定。

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;public class HelloWorld3 {@Plugin(name = "My Combine Worlds Plug-in", parameterLabels = { "First string", "Number", "Second string" }, returnLabels = { "First string several second strings" }, returnTypes = { String.class }, userAccessible = true, help = "Produces one string consisting of the first and a number of times the third parameter.")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object helloWorlds(PluginContext context, String first, Integer number, String second) {String s = first;for (int i = 0; i < number; i++) {s += "," + second;}return s;}
}

我们将执行My 2nd Hello World Plug-in的输出作为上面代码中的插件的输入,我们到Workspace中的All找到输出的Objects,按住键盘ctrl键,选择三个Object,然后点击use resources按钮;然后找到My Combine Worlds Plug-in,点击Start运行

可以点击每一个Input修改输入Object

执行效果如下:

一些注意点

  1. 返回类型以及参数类型不应使用泛型(也就是说,List<String>不能用作参数)。原因是框架无法在运行时读取这些泛型,因此不可能知道哪些插件可以作为其他插件的输入。

  2. 数组既可以用作参数类型,也可以用作返回类型。插件可以指定返回String[].class类型的对象。此外,还可以请求此类型的参数。

  3. 插件应尽可能在其参数和返回类型定义中使用接口。例如,返回PetrinetImpl对象(实现Petrinet接口)的插件应该声明它返回Petrinet.class类型的对象。

PluginContext参数

PluginContext的思想是提供与框架、其他插件或用户通讯的所有可能的接口。下面介绍可以使用context的通用特性。

日志Logging

PluginContext接口提供三个打日志方法:

 /*** The provided String is provided to the context for information. It can* for example signal a state change of a plugin. Note that some contexts* can completely ignore this message.* * @param message*            the message to log* @param level*            the message level(Normal,Warning,Error,Test,Debug;默认是Normal)*/void log(String message, MessageLevel level);/*** Same as calling log(message, MessageLevel.NORMAL);* * @param message*            The message*/void log(String message);/*** The provided Exception is provided to the context. It signals the context* about an error in the plugin, that specifically lead to abnormal* termination. The plugin signaling the exception is no longer executing!* * @param exception*            the exception thrown*/void log(Throwable exception);

每个context都有许多与之关联的日志listeners。这些listeners 接收每个记录的消息并决定如何处理它们。例如,GUI确保它已注册为框架中所有插件context上的日志侦听器。默认情况下,Debug和Test消息处于关闭状态,但用户可以打开这些消息。

下面使用log的例子及输出:

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;
import org.processmining.framework.plugin.events.Logger.MessageLevel;public class HelloWorld4 {@Plugin(name = "My 3rd Hello World Plug-in", parameterLabels = {}, returnLabels = { "Hello world string" }, returnTypes = { String.class }, userAccessible = true, help = "Produces the string: 'Hello world'")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static String helloWorld(PluginContext context) {context.log("Started hello world plug-in", MessageLevel.DEBUG);return "Hello World";}
}

执行进度指示器Progress Indicator

进度指示器可以让插件使用者知道执行插件时的任务的进度,PluginContext提供getProgress对进度进行控制。

 /*** Returns the progress object corresponding to this context* * @return the progress object corresponding to this context*/Progress getProgress();
/*** Interface for progress indicator* * @author bfvdonge* */
public interface Progress {void setMinimum(int value); //用于设置进度的起始值void setMaximum(int value); //设置进度的最大值void setValue(int value); //表示进度的当前值void setCaption(String message); //进度的说明String getCaption();int getValue(); //获得当前进度void inc(); // 可以用于进度加一,等价于setValue(getValue()+1)void setIndeterminate(boolean makeIndeterminate);boolean isIndeterminate();int getMinimum();int getMaximum();boolean isCancelled();void cancel();
}

下面代码展示进度指示器的使用和效果:

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;public class HelloWorld5 {@Plugin(name = "My 2nd Combine Worlds Plug-in", parameterLabels = { "First string", "Number", "Second string" }, returnLabels = { "First string several second strings" }, returnTypes = { String.class }, userAccessible = true, help = "Produces one string consisting of the first and a number of times the third parameter.")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object helloWorlds(PluginContext context, String first, Integer number, String second) {context.getProgress().setMinimum(0);context.getProgress().setMaximum(number);context.getProgress().setCaption("Constructing hello worlds string");context.getProgress().setIndeterminate(false);String s = first;for (int i = 0; i < number; i++) {s += "," + second;try {Thread.sleep(1000);} catch (InterruptedException e) {// don't care}context.getProgress().inc();}return s;}
}

使用建议:在知道任务执行的步骤数(或者milestones的数量)时,可以使用进度指示器来展示任务的执行进度。

Future

Future对象允许用户在尚未可用的输入上启动插件,这使框架能够知道在开始执行插件时预期的对象类型,即使对象本身不可用。执行插件时,框架首先实例化该插件的插件上下文,然后根据所有预期结果创建futures。每个future由一个标签和一个类型组成。类型从@Plugin注释中指定的returnTypes读取,初始标签从@Plugin注释中指定的returnLabels读取。

然而,在执行过程中,插件可以通过调用context.getFutureResult(int number)与其未来的结果进行通信。这里,作为参数给出的整数表示请求哪个future,即在多个返回类型的情况下,创建多个futures。

虽然从技术上讲,插件可能会取消其自身future的计算,但这通常是不可取的。相反,与future的通信应该局限于调用context.getFutureResult(x).setLabel(newLabel),在这种情况下,结果的标签将更新。

官方的解释实在看不懂,说人话大概是尽管output的计算结果还未确定(还未return输出),可以通过getFutureResult来改变输出的属性,比如标签(setLabel)之类的,contextt.getFutureResult(int number)中的number指示第几个输出。比如下面例子不断改变output的标签,最后输出的标签与@Plugin注解中的不同:

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;public class HelloWorld6 {@Plugin(name = "My 3rd Combine Worlds Plug-in", parameterLabels = { "First string", "Number", "Second string" }, returnLabels = { "First string several second strings" }, returnTypes = { String.class }, userAccessible = true, help = "Produces one string consisting of the first and a number of times the third parameter.")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object helloWorlds(PluginContext context, String first, Integer number, String second) {context.getProgress().setMinimum(0);context.getProgress().setMaximum(number);context.getProgress().setCaption("Constructing hello worlds string");context.getProgress().setIndeterminate(false);String s = first;for (int i = 0; i < number; i++) {s += "," + second;context.getFutureResult(0).setLabel("Hello " + i + " worlds string");try {Thread.sleep(1000);} catch (InterruptedException e) {// don't care}context.getProgress().inc();}context.getFutureResult(0).setLabel("Hello " + number + " worlds string");return s;}
}

执行完的output的标签:

Provided object management

通常,只要调用插件,它们的结果就可以作为提供的对象使用。起初,这些对象是future对象,但一旦插件完成其执行,future对象就会被实际对象替换。框架中的所有对象都由Provided Object Manager(提供的对象管理器)处理,可以通过context访问对象管理器。然而,插件通常只需要一点可用的功能,即创建和更新提供的对象。

尽管框架确保插件返回的所有结果都可以作为提供的对象使用,但插件有时需要更多的结果。例如,考虑genetic mining插件。该插件完成后返回模型列表。然而,与此同时,构建了一些用户可能感兴趣的模型。框架显然不知道这些中间对象。因此,插件可以创建自己提供的对象。

提供的对象由标签和对象组成。要创建提供的对象,应调用ProvidedObjectManager的createProvidedObject方法。此方法不仅需要标签和对象,还需要上下文context。此上下文是必需的,以便可以通知侦听器listener所提供对象的创建。创建所提供的对象后,它将获得一个ID,该ID可用于以后引用该对象,例如更新或删除该对象。提供的对象是弱引用。

说人话就是可以用作于中间对象,可以创建、更新和删除,下面给出的例子也只是这几个方法的使用,具体用处以后遇到在完善。。。

package org.processmining.plugins.gettingstarted;import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;
import org.processmining.framework.providedobjects.ProvidedObjectDeletedException;
import org.processmining.framework.providedobjects.ProvidedObjectID;public class HelloWorld7 {@Plugin(name = "My 4th Combine Worlds Plug-in", parameterLabels = { "First string", "Number", "Second string" }, returnLabels = { "First string several second strings" }, returnTypes = { String.class }, userAccessible = true, help = "Produces one string consisting of the first and anumber of times the third parameter.")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object helloWorlds(PluginContext context, String first, Integer number, String second) {context.getProgress().setMinimum(0);context.getProgress().setMaximum(number);context.getProgress().setCaption("Constructing hello worlds string");context.getProgress().setIndeterminate(false);String s = first;// 创建提供的对象ProvidedObjectID id = context.getProvidedObjectManager().createProvidedObject("intermediate string", s, context);for (int i = 0; i < number; i++) {context.getFutureResult(0).setLabel("Hello " + i + " worlds string");try {//更新
context.getProvidedObjectManager().changeProvidedObjectObject(id, s);Thread.sleep(1000);} catch (ProvidedObjectDeletedException e1) {// if the user deleted this object,// then we create it againid = context.getProvidedObjectManager().createProvidedObject("intermediate string", s, context);} catch (InterruptedException e) {// don't care}s += "," + second;context.getProgress().inc();}context.getFutureResult(0).setLabel("Hello " + number + " worlds string");// The intermediate object is no longer necessary.try {//删除context.getProvidedObjectManager().deleteProvidedObject(id);} catch (ProvidedObjectDeletedException e) {// Don't care}return s;}
}

Connection management

ProM使用连接(connections)来表示和存储对象之间的关系,而连接的管理和操作通过连接管理器(connection management)进行,在代码中通过调用context.getConnectionManager()得到上下文的连接管理器。

连接connections

连接表示对象之间的联系,如Petri net对象和Marking对象的联系,一个连接是一个从标签到对象的映射,连接本身也有标签。

一旦将连接加入到连接管理器中,就会存在于ProM中,当然连接也是弱引用。通常连接不需要方法去显示表示对象间的连接,如Marking和Petri net,在marking里的库所同样在Petri net中也包含。

在org.processmining.plugins.connections包可以使用一些连接。

关于connections的作用,在教程视频中讲解得很好,可以去观看理解。

// 注册连接
Connection c = new MarkedNetConnection(petrinet, new Marking(state));
context.addConnection(c);
//等价于
context.getConnectionManager().addConnection(context, c);

查询连接

ConnectionManager的getConnections可以查询涉及给定对象的连接类型的所有连接。

 /*** Returns a collection of connections between the objects specified, such* that the type of the connection is assignable from the given* connectionType (unless the parameter equals null).* * If no connections satisfying these criteria exist and the required type* is specified and no required name is specified, then the global context* searches for all available plugins with a ConnectionObjectFactory* annotation, which can be executed in a child of the given PluginContext* and accept the given objects as input* * If such plugins exist, the first of these plugins is selected and invoked* on the given objects. The result is obtained from the plugin and a new* connection is registered of the right type. This connection is then* returned.* * @param <T>*            the type of the requested connection.* @param connectionType*            The type of the object requested. This type can be null, in*            which case all types are considered.* @param context*            The context which requests the connection. If a plugin is*            invoked to create a connection, a child context of this*            context is instantiated* @param objects*            the objects which should be connected by the requested*            connection. There might be more objects involved in the*            connection* @return A collection of connections of the requested type T. If no*         connection exists, an exception is thrown, hence the collection*         is never empty.* @throws ConnectionCannotBeObtained*             if the requested connection does not exist and cannot be*             produced in the given context.*/<T extends Connection> Collection<T> getConnections(Class<T> connectionType, PluginContext context,Object... objects) throws ConnectionCannotBeObtained;

@ConnectionFactory

@ConnectionFactory注解可作用于插件,告知框架该插件可以返回连接对象。插件不必在框架中注册连接,相反插件返回的对象必须是一个实现Connection接口的对象,并且连接管理器在ProM会处理这个连接的注册。

多线程执行

PluginContext都带有Executor,这是一个标准的java.util.concurrent.Executor。可以通过getExecutor方法,利用对应的Executor实现多线程执行

 /*** Returns an executor which can be used to execute plugins in child* contexts.* * @return*/Executor getExecutor();

插件管理

一些插件需要执行其他插件,一种方法是如果事先知道需要执行插件的所有类和方法,可以以java的方式去调用。ProM还提供了插件管理器(plugin manager)的功能,可以使用它来查询和获取指定属性的插件。

PluginContext的具体实现

UIPluginContext是具体实现例子

package org.processmining.plugins.gettingstarted;import javax.swing.JOptionPane;import org.processmining.contexts.uitopia.UIPluginContext;
import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;public class HelloWorld8 {@Plugin(name = "My 5th Combine Worlds Plug-in", parameterLabels = { "First string", "Number" }, returnLabels = { "First string several second strings" }, returnTypes = { String.class }, userAccessible = true, help = "Produces one string consisting of the first and a number of times a string given as input in a dialog.")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object helloWorlds(UIPluginContext context, String first, Integer number) {// Ask the user for his worldString w = JOptionPane.showInputDialog(null, "What's the name of your world?","Enter your world", JOptionPane.QUESTION_MESSAGE);// change your result labelcontext.getFutureResult(0).setLabel("Hello " + w + " string");// return the combined stringreturn helloWorlds(context, first, number, w);}@Plugin(name = "My 4th Hello World Plug-in", parameterLabels = {}, returnLabels = { "Hello string", "Number", "Worlds string" }, returnTypes = { String.class, Integer.class, String.class }, userAccessible = true, help = "Produces three objects: 'Hello', number, 'world'")@UITopiaVariant(affiliation = "My company", author = "My name", email = "My e-mail address")public static Object helloWorlds(PluginContext context, String first, Integer number, String second) {String s = first;for (int i = 0; i < number; i++) {s += "," + second;}return s;}
}

插件生命

当ProM框架启动时,会自动创建一个主插件上下文。此主插件上下文用于从中派生新上下文以执行插件。

当框架执行插件时,框架首先实例化一个PluginContext对象。例如,该上下文的实现类型可以是GUI上下文或命令行上下文。

复杂插件

非静态方法插件

同样可以在非静态方法上定义插件,只不过调用插件的方式不同,在执行插件时的差异体现就跟执行静态方法(类名.静态方法)和非静态方法一样(先new一个对象再执行)。

插件重载

插件支持重载,支持参数的不同。

可选输入(Optional inputs)

@Plugin注解可以加载类上,通知ProM这是一个重载插件,之后在方法上可以通过@PluginVariant注解表明插件的重载。

package org.processmining.plugins.gettingstarted;import javax.swing.JOptionPane;import org.processmining.contexts.uitopia.UIPluginContext;
import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;
import org.processmining.framework.plugin.annotations.PluginVariant;@Plugin(name = "My Overloaded Hello World Plugin", parameterLabels = { "First string", "Number", "Second string" }, returnLabels = { "Hello world string" }, returnTypes = { String.class }, userAccessible = true, help = "The plugin produces 'hello' concatenated with at least one world. If no world is given, it is requested from the user, provided that a GUI exists."
)
public class HelloWorld10 {private String getWorld(UIPluginContext context) {// Ask the user for his worldString w = JOptionPane.showInputDialog(null, "What's the name of your world?","Enter your world", JOptionPane.QUESTION_MESSAGE);// change your result labelcontext.getFutureResult(0).setLabel("Hello " + w + " string");return w;}@PluginVariant(variantLabel = "My original hello world", requiredParameterLabels = {})@UITopiaVariant(uiLabel = "My original hello world", affiliation = "My company", author = "My name", email = "My e-mail address")public String helloWorld(PluginContext context) {return "Hello World";}@PluginVariant(variantLabel = "My Hello unknown", requiredParameterLabels = {})@UITopiaVariant(uiLabel = "My Hello unknown", affiliation = "My company", author = "My name", email = "My e-mail address")public String helloUnknown(UIPluginContext context) {return "Hello " + getWorld(context);}@PluginVariant(variantLabel = "My Combine worlds", requiredParameterLabels = { 0, 1, 2 })@UITopiaVariant(uiLabel = "My Combine worlds", affiliation = "My company", author = "My name", email = "My e-mail address")public Object helloWorlds(PluginContext context, String first, Integer number, String second) {String s = first;for (int i = 0; i < number; i++) {s += "," + second;}return s;}@PluginVariant(variantLabel = "My Combine unknowns", requiredParameterLabels = { 0, 1 })@UITopiaVariant(uiLabel = "My Combine unknowns", affiliation = "My company", author = "My name", email = "My e-mail address")public Object helloWorlds(UIPluginContext context, String first, Integer number) {// return the combined string, after asking for the worldreturn helloWorlds(context, first, number, getWorld(context));}
}

每个PluginVariant的返回需要与@Plugin中的returnTypes一致。

多类型输入

输入参数的类型在多个插件重载中可以是不一样的,只是标签的名字一样。

package org.processmining.plugins.gettingstarted;import javax.swing.JOptionPane;import org.processmining.contexts.uitopia.UIPluginContext;
import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;
import org.processmining.framework.plugin.annotations.PluginVariant;@Plugin(name = "My Overloaded Hello Many Worlds", parameterLabels = { "First string", "Large Number", "Second string" }, returnLabels = { "Hello world string" }, returnTypes = { String.class }, userAccessible = true, help = "The plugin produces 'hello' concatenated with at least one world. If no world is given, it is requested from the user, provided that a GUI exists."
)
public class HelloWorld11 {private String getWorld(UIPluginContext context) {// Ask the user for his worldString w = JOptionPane.showInputDialog(null, "What's the name of your world?","Enter your world", JOptionPane.QUESTION_MESSAGE);// change your result labelcontext.getFutureResult(0).setLabel("Hello " + w + " string");return w;}@PluginVariant(variantLabel = "My Combine many worlds", requiredParameterLabels = { 0, 1, 2 })@UITopiaVariant(uiLabel = "My Combine many worlds", affiliation = "My company", author = "My name", email = "My e-mail address")public Object helloWorlds(PluginContext context, String first, Long number, String second) {String s = first;for (int i = 0; i < number; i++) {s += "," + second;}return s;}@PluginVariant(variantLabel = "My Combine few unknowns", requiredParameterLabels = { 0, 1 })@UITopiaVariant(uiLabel = "My Combine few unknowns", affiliation = "My company", author = "My name", email = "My e-mail address")public Object helloWorlds(UIPluginContext context, String first, Integer number) {// return the combined string, after asking for the worldreturn helloWorlds(context, first, Long.valueOf(number), getWorld(context));}@PluginVariant(variantLabel = "My Combine many unknowns", requiredParameterLabels = { 0, 1 })@UITopiaVariant(uiLabel = "My Combine many unknowns", affiliation = "My company", author = "My name", email = "My e-mail address")public Object helloWorlds(UIPluginContext context, String first, Long number) {// return the combined string, after asking for the worldreturn helloWorlds(context, first, number, getWorld(context));}
}

注意点

  1. 如果在特定上下文中不能执行任何一个插件变体,则框架将忽略该插件;
  2. 使用@PluginVariant注解的插件变体的方法必须返回在@Plugin注解中指定类型的对象。在执行插件之前,框架不会对此进行检查,在这种情况下,如果返回类型不匹配,将引发异常;
  3. 插件变体既可以是静态的也可以是非静态的,并且它们的任何组合都可以存在于一个插件中;
  4. 使用过多的插件变体可能会令人困惑(但有时是必要的)。通常,如果存在许多变体,那么最好的编程实践是将实际逻辑的实现推迟到(一个或多个)私有方法,并尽可能清晰明确地对方法用@PluginVariant注解;
  5. 插件变体可以在超类中定义,也就是说,可以定义一个包含许多变体的抽象类,这些变体都调用一个abstract protected方法,该方法在子类中实现。然而,@Plugin注解应该只在子类级别上使用,否则超类和子类都被视为插件。对于该构造的示例,我们指的是以下组合:
    • org.processmining.plugins.abstractplugins.AbstractImportPlugin,它定义了用于打开由不同对象指定的文件的变体,但本身不是插件;
    • org.processmining.plugins.petrinet.tpn.TpnImport,它实现了AbstractImportPlugin的抽象方法,并定义了@Plugin注解;
  6. 用@Plugin的类的方法也可以用@Plugin。在这种情况下,框架将这些方法视为单独的插件。但是,不鼓励这种构造,因为它会导致复杂的代码;
  7. @PluginVariant由子类继承。因此,任何带有@Plugin的类都可以被另一个带有@Plugin的类扩展。超类的变体也存在于子类中。这要求继承关系中的任意两个插件的结果类型相同。同样,只有在插件执行之后,才会检查这一点。

总结

这篇文章从官方教程的GettingStarted出发,入门ProM中插件的基本概念和开发,在使用ProM开发时,我们通常会对算法去开发一个插件,然后在ProM平台上去使用插件。

ProM开发指北3——ProM插件开发入门相关推荐

  1. ProM开发指北2——环境设置

    以下内容引用:GettingStarted – prom 开发环境 ProM是基于Eclipse开发的,JDK建议使用1.8版本,同时ProM使用Subclipse进行版本控制,使用Ivy进行依赖管理 ...

  2. ProM开发指北1——什么是ProM

    ProM(Process Mining framework)是一个流程挖掘算法的开源框架,是流程挖掘社区进行研究的开发框架平台,为开发者和用户提供一个易使用.易扩展的流程挖掘算法平台.开发者可以在Pr ...

  3. Mybatis注解开发指北

    Mybatis注解开发指北 目录 文章目录 Mybatis注解开发指北 @[toc] 0. Mybatis注解开发步骤 1. 导入相关配置文件 2. 配置数据库连接 3. 创建数据库对应的实体类(en ...

  4. (软件工程)GPU并行软件开发指北

    事先声明,本篇博文中不涉及任何技术,原理和代码.如果想知道GPU开发实践方法,请直接查阅OpenCL与CUDA开发手册. 在"GPU并行软件开发指北"这篇博文中,我仅仅是想谈论一下 ...

  5. Blockly开发入门指北

    Blockly开发入门指北 [腾讯文档]Blockly开发入门指北 https://docs.qq.com/doc/DRWRDUU5kR2lhaGNN 写这篇文章的目的 最近公司的项目用到了Block ...

  6. Python 简单入门指北(二)

    Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...

  7. 计算机学习入门指北——计科软工网络信安侧重图析、解读专业术语、岗位分类、未来规划

    申明:本博文偏技术向,主观性较强,其中部分理解必有偏差和误解,望指出改正! 计算机学习入门指北: 作为刚入学的计算机系学生,面对一片专业术语十分蒙.区块链?大数据?开源?数据库?嵌入式开发?前端后端? ...

  8. 【Linux入门指北】第一篇 初识Linux

    目录 前言 一.Linux操作系统的发展历史 1.Linux操作系统的诞生 2.Linux操作系统的发展 1.自由软件基金会(FSF) 2.GPL协议 3.GUN工程 二.Linux的不同发行版本 1 ...

  9. Python 简单入门指北(试读版)

    本文是我小专栏中 Python 简单入门指北 一文的前半部分,如果你能坚持读完并且觉得有一定收获,建议阅读原文,只需一杯咖啡钱就可以阅读更精彩的部分,也可以订阅小专栏或者加入我的知识星球,价格都是 6 ...

最新文章

  1. 2星|《约见投资人》:A股上市公司软文集
  2. 使用粘性布局实现tab滑动后置顶
  3. Android 仿PhotoShop调色板应用(三) 主体界面绘制
  4. 会考flash中文字变形为三角形_关于信息技术会考 Flash操作题实用模版
  5. PostgreSQL的ecpg程序的调适与运行
  6. python utf8_肿么在Python里使用UTF-8编码
  7. create 2021 | 一图读懂汽车智能化分论坛
  8. 陕西师范大学第七届程序设计竞赛 C题 iko和她的糖
  9. python join函数_Python join()函数
  10. Echarts笔记-折线图定制(Y轴百分数,鼠标移动显示百分数,显示X轴,Y轴值)
  11. 2017年上海市计算机一级题库,2017年计算机一级题库及答案
  12. 信捷plc编程100例梯形图_PLC分类组成与梯形图编程语言
  13. linux 杀掉僵尸进程 (zombie process, defunct)
  14. python贪吃蛇游戏手把手教学 第一课
  15. 无中介租房搜房工具 V1.0
  16. 构建前端项目及使用技术
  17. 网易mumu模拟器怎么清理缓存?
  18. 网络基础 — IP地址和子网掩码
  19. 多摩川绝对值编码器CPLD FPGA通信源码(VHDL格式+协议+说明书)
  20. 电商系统开发实战-用户微服务基础模块开发

热门文章

  1. matlab极性电容叫什么,有极性电容和无极性电容原理区别
  2. IDEAD中如何使用scala
  3. 操作系统篇之Linux命令操作和redis安装以及基本使用
  4. OSG 之学习五:OSG 漫游
  5. 明道云与阿里1688对接案例
  6. Jenkins搭建前后端分离项目流水线实战
  7. ping通www.baidu.com的完整过程。
  8. 高精地图:激光雷达点云与高精地图融合
  9. 憨批豪的java成长日记-MYSQL数据库
  10. html前端学习三:CSS