Refer to the following: setup/HowToCreatePluginsInProM – prom
Hello World plugin
Plug in definition
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"; } }
- The helloworld method contains the logic of the plug-in. The input parameter PluginContext type is required.
- @The Plugin plug-in is represented by this annotation and acts on the method, which is the logic of the plug-in@ The properties of the Plugin, that is, the properties of the plug-in:
plug-in unit | attribute | effect | example |
@Plugin | name | Plug in name | My Hello World Plugin |
parameterLabels | Label for the input of the plug-in | Not specified in this example | |
returnLabels | Label list of objects output to the plug-in. One output object corresponds to one label | This example returns a string labeled Hello world string | |
returnTypes | Type of plug-in output object | String.class | |
userAccessible | Whether users can access | Normally set to true | |
help | Describes how to use the plug-in (optional) | Produces the string: 'Hello world' |
When the ProM framework starts, it will scan the @ Plugin annotation in all class files and register a plug-in accordingly.
- @The UITopiaViant annotation informs the ProM GUI of the existence of this plug-in. Its properties are:
plug-in unit | attribute | effect | example |
@UITopiaViant | affiliation | Author's organization | |
author | Author name | ||
Author email |
- Plug in operation effect: ① run prom with uitopia (GettingStarted) launch
Find the plug-in written above and click Start to run. When @ UITopiaViant @, ProM tools can find the corresponding plug-in. The plug-in name is the name in @ Plugin. The green plug-in list indicates that the plug-in can run. This is because our plug-in does not need input and can run naturally; If it is yellowish brown, it indicates that there is a lack of required input and therefore it cannot run.
Operation effect:
Multiple output
As mentioned earlier, the returnLabels and returnTypes properties of @ Plugin control the output of the plug-in. The output is a list, and the output label, type and order are determined by them.
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" }; } }
After executing my 2nd Hello world plug in, three objects are output (Integer does not realize visualization)
In the method, an Object [] array type is output. The type is not checked here, but the return type must be specified in returnLabels. ProM can check whether the type is normal when executing the plug-in, and throw an exception when it is incorrect (change the third element of the above array to 3, and output the following effect).
Multiple input
As mentioned earlier, the parameterLabels attribute of @ Plugin describes the label of the input of the plug-in, which is different from the output. The type and order of input are determined by the method parameters.
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; } }
We will execute the output of my 2nd Hello world plug in as the input of the plug-in in the above code. We go to All in Workspace to find the output Objects, press and hold the ctrl key on the keyboard, select three Objects, and then click the use resources button; Then find my combine worlds plug in and click Start to run
You can click each Input to modify the Input Object
The implementation effect is as follows:
Some points for attention
-
Return types and parameter types should not use generics (that is, list < string > cannot be used as parameters). The reason is that the framework cannot read these generics at run time, so it is impossible to know which plug-ins can be used as input to other plug-ins.
-
An array can be used as either a parameter type or a return type. The plug-in can specify to return string [] Class object. You can also request parameters of this type.
-
Plug ins should use interfaces in their parameter and return type definitions whenever possible. For example, a plug-in that returns a Petri netimpl object (implementing the Petri Net Interface) should declare that it returns Petri net Class object.
PluginContext parameter
The idea of PluginContext is to provide all possible interfaces to communicate with the framework, other plug-ins or users. The following describes the general features of context.
Logging
The PluginContext interface provides three logging methods:
/** * 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;(default is 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);
Each context has many log listeners associated with it. These listeners} receive each recorded message and decide how to process them. For example, the GUI ensures that it is registered as a log listener on the context of all plug-ins in the framework. By default, Debug and Test messages are closed, but users can open them.
The following is an example of using log and its output:
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"; } }
Execution Progress Indicator
The progress indicator can let the plug-in user know the progress of the task when executing the plug-in. The PluginContext provides getProgress to control the progress.
/** * 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); //Used to set the starting value of the progress void setMaximum(int value); //Set the maximum value of progress void setValue(int value); //Represents the current value of the progress void setCaption(String message); //Description of progress String getCaption(); int getValue(); //Get current progress void inc(); // It can be used for progress plus one, which is equivalent to setValue(getValue()+1) void setIndeterminate(boolean makeIndeterminate); boolean isIndeterminate(); int getMinimum(); int getMaximum(); boolean isCancelled(); void cancel(); }
The following code shows the use and effect of the progress indicator:
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; } }
Usage suggestion: when you know the number of steps (or the number of milestones) performed by the task, you can use the progress indicator to show the execution progress of the task.
Future
The future object allows the user to start the plug-in on input that is not yet available, which enables the framework to know the type of object expected when starting the plug-in execution, even if the object itself is not available. When executing a plug-in, the framework first instantiates the plug-in context of the plug-in, and then creates futures based on all expected results. Each future consists of a label and a type. The type is read from the returnTypes specified in the @ Plugin annotation, and the initial label is read from the returnLabels specified in the @ Plugin annotation.
However, during execution, the plug-in can call context Getfutureresult (int number) communicates with its future results. Here, the integer given as a parameter indicates which future is requested, that is, in the case of multiple return types, multiple futures are created.
Although technically, a plug-in may cancel its own future calculation, this is usually not desirable. Instead, communication with future should be limited to calling context getFutureResult(x). Setlabel (newlabel), in which case the label of the result will be updated.
The official explanation is really incomprehensible. Although the calculation result of output has not been determined (return output has not yet been returned), the output properties can be changed through getFutureResult, such as setLabel, contextt The number in getFutureResult (int number) indicates the output number. For example, the following example constantly changes the label of output. The last output label is different from that in the @ Plugin annotation:
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; } }
Label of executed output:
Provided object management
Typically, as long as plug-ins are invoked, their results can be used as provided objects. At first, these objects are future objects, but once the plug-in completes its execution, the future objects will be replaced by the actual objects. All objects in the framework are processed by the Provided Object Manager, which can be accessed through the context. However, plug-ins usually require only a little functionality available, that is, creating and updating provided objects.
Although the framework ensures that all results returned by plug-ins can be used as provided objects, plug-ins sometimes need more results. For example, consider the generic mining plug-in. When the plug-in is finished, it returns to the model list. However, at the same time, some models that users may be interested in are built. The framework obviously does not know these intermediate objects. Therefore, plug-ins can create their own objects.
The provided object consists of a label and an object. To create the provided object, call the createProvidedObject method of ProvidedObjectManager. This method requires not only labels and objects, but also context. This context is required so that the listener can be notified of the creation of the object provided by listener. When the provided object is created, it gets an ID that can be used to reference the object later, such as updating or deleting the object.
Human talk can be used as intermediate objects and can be created, updated and deleted. The examples given below are only the use of these methods. The specific uses will be improved in the future...
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; // Create supplied object 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 { //to update context.getProvidedObjectManager().changeProvidedObjectObject(id, s); Thread.sleep(1000); } catch (ProvidedObjectDeletedException e1) { // if the user deleted this object, // then we create it again id = 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 { //delete context.getProvidedObjectManager().deleteProvidedObject(id); } catch (ProvidedObjectDeletedException e) { // Don't care } return s; } }