Exploiting the Innards of ProM and Breaking Data-Hiding for Fun and Profit

So, I’ve spent the afternoon writing horrible code.  Horrible, horrible code.

See, I have this plug-in, which executes other plug-ins.  For some time, I’ve wanted to mark those as favorite in ProM.  This means that the object shows up with a pretty yellow star and, more importantly, shows up in the Favorites.  As the Favorites tab is the default and ProM normally puts everything here itself, most users never switch to the All tab and cannot find the object created by my plug-in.  Thus, instead of this:

we would like this:

Now, ProM is designed to run in multiple modes.  The standard one that most people see is the graphical UITopia mode.  Here we have a graphical context where all of the above makes sense.  ProM also has a command line mode and a distributed mode, where we do not have the graphical context available and cannot do this.

Normally, when you register an object, you do something like this:

[java] public static <T> void publish(final PluginContext context, final String name, final T object, final Class<? super T> clazz, final boolean favorite) {
final ProvidedObjectID id = context.getProvidedObjectManager().createProvidedObject(name, object, clazz, context);[/java]

We get the ProvidedObjectManager and registers our object with the correct name and type (the type is important, as ProM only shows exact objects it knows, not any sub-types). Unfortunately, this produces the situation above: object is without a star and hidden behind the All tab along with all kinds of scary and internal objects. Nobody wants that.

Now, we do a type check on the context to check if it is a UIPluginContext, i.e., it is running in graphical mode. If it is, we have some more, very internal, methods available. We can get access to the GlobalContext, which provides access to all the innards of ProM. As such:

[java]if (context instanceof UIPluginContext) {
final GlobalContext gcontext = ( (UIPluginContext) context).getGlobalContext();
[/java]

Now, a GlobalContext in itself is a bit boring, but if the GlobalContext is a UIContext (which it is in the graphical case), we have found the proverbial pot of Britney CDs at the end of the rainbow. The thing is that the UIContext has access to a Resource Manager, which is used for handling resources. The resource manager can be used to register a new resource, which in turn can be marked as a favorite. Lets hijack that son of Aguilera:

[java] if (gcontext instanceof UIContext) {
final UIContext uicontext = (UIContext) gcontext;
final ResourceType resType = uicontext.getResourceManager().getResourceTypeFor(clazz);
if (resType != null) {
final List<Collection<ProMPOResource>> lst = Collections.emptyList();
ProMPOResource res = new ProMPOResource(uicontext, null, resType, id, lst);
res = uicontext.getResourceManager().addResource(id, res);
res.setFavorite(favorite);
[/java]

We have now set our resource as favorite and it shows up appropriately in the Favorites tab.

But we are not completely done yet… We would also like to show the resource if possible. This is quite easy: the UIContext has a View Manager, which we can use to get all possible views for a type of resource instantiate one, and register it. We add this:

[java] if (favorite) {
final List<ViewType> viewTypes = uicontext.getViewManager().getViewTypes(res);
if (viewTypes.size() > 0) {
final ViewType viewType = viewTypes.get(0);
final View view = viewType.createView(res);
uicontext.getViewManager().addView(view);
[/java]

We simply, if the object is marked as favorite, get all views of the appropriate type, and, if there are any, pick the first and create a view from it. Finally we add it to the view manager. Simple.

Again, we could call it a day at the office and head for the pub. We are not going to do that, because it wold be much more elegant to have the view also shown to the user; currently it is simply available and can be shown manually. Now, this is really dependent on the internals of the GUI, so it is a bit more difficult…

It turns out, however, that ProM has a UITopia Controller, which has access to the main view. Using this, we can switch to the views view ((Wonderful name…)) and select the newly created one as the one shown as such:

[java] final UITopiaController controller = …;
controller.getMainView().showViewsView();
controller.getMainView().getViewsView().showFullScreen(view);
[/java]

Now, of course, the problem is, how do we get a hold of the controller? Well, the UIContext has it. Looking in the UIContext class, we see:

[java] public class UIContext extends AbstractGlobalContext implements FrameworkHub<ProMAction, ProMTask, ProMResource<?>, ProMPOResource> {

private UITopiaController controller;
[/java]

We just use the accessor method on UIContext and Bob’s your uncle… Problem number next: The controller is not accessible. Nor is it (to the best of my knowledge) accessible from anywhere I have access to…

This, of course is fixable. Using reflection, we have access to private fields, so we “just” write some wonderful code like this:

[java] final Field controllerField = uicontext.getClass().getDeclaredField(“controller”);
controllerField.setAccessible(true);
final UITopiaController controller = (UITopiaController) controllerField.get(uicontext);
[/java]

Yes, the solution is to find David Parnas, hit him with something heavy, tear out his heart, and then completely shit all over data encapsulation. We “simply” get the field descriptor for the controller, modify the access rights, and get the value. Simple.

Tying all of this together in a single piece of code yields the below method. It is ugly as an Aguilera song, but works. It even gracefully degrades ((Much unlike Aguilera who has hit rock bottom but still ungracefully degrades herself by singing her own songs instead of Britney’s.)), i.e., if we are running on the command line, the object is just registered, and if we cannot access the private field, we just ignore it and neglect to raise the view to the front. In addition to the code explained above, we also actually provide a value for the action in line 37 (the second parameter) which we obtain from the context using the same strategy used to get the controller.

[java] /**
* Will publish object as a provided object with the class specified by
* clazz. If the context is a UIPluginContext and favorite is true, it will
* be marked as a favorite object as well. Finally, if it is marked as
* favorite, it will also be show and ProM will switch to the Views tab.
* Encapsulation is for scared little boys/girls who still believe in the
* invisible pink unicorn (IPU).
*
* @param <T>
* @param context
* @param name
* @param object
* @param clazz
* @param favorite
*/
public static <T> void publish(final PluginContext context, final String name, final T object,
final Class<? super T> clazz, final boolean favorite) {
final ProvidedObjectID id = context.getProvidedObjectManager().createProvidedObject(name, object, clazz,
context);
// Do not look at the rest of this method. Boudewijn was busy and I just decided to do what is necessary.
// It works. It probably breaks if somebody changes anything. Here or elsewhere.
if (context instanceof UIPluginContext) {
final GlobalContext gcontext = ((UIPluginContext) context).getGlobalContext();
if (gcontext instanceof UIContext) {
final UIContext uicontext = (UIContext) gcontext;
final ResourceType resType = uicontext.getResourceManager().getResourceTypeFor(clazz);
if (resType != null) {
ProMTask task = null;
try {
final Field taskField = context.getClass().getDeclaredField(“task”);
taskField.setAccessible(true);
task = (ProMTask) taskField.get(context);
} catch (final Exception _) {
// Guess it wasn’t meant to be, then…
}
final List<Collection<ProMPOResource>> lst = Collections.emptyList();
ProMPOResource res = new ProMPOResource(uicontext, task == null?null:task.getAction(), resType, id, lst);
res = uicontext.getResourceManager().addResource(id, res);
res.setFavorite(favorite);
if (favorite) {
final List<ViewType> viewTypes = uicontext.getViewManager().getViewTypes(res);
if (viewTypes.size() > 0) {
final ViewType viewType = viewTypes.get(0);
final View view = viewType.createView(res);
uicontext.getViewManager().addView(view);
try {
final Field controllerField = uicontext.getClass().getDeclaredField(“controller”);
controllerField.setAccessible(true);
final UITopiaController controller = (UITopiaController) controllerField.get(uicontext);
controller.getMainView().showViewsView();
controller.getMainView().getViewsView().showFullScreen(view);
} catch (final Exception _) {
// Not a huge surprise, really…
}
}
}
}
}
}
}
[/java]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.