# Gateway-Scoped Event Handler Script

**URL:** <https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263>\
**Category:** Module Development\
**Created:** [March 17, 2023, 9:46pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263 "2023-03-17T21:46:43Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![eric.hollering](https://avatars.discourse-cdn.com/v4/letter/e/ad7895/32.png) [@eric.hollering](https://forum.inductiveautomation.com/u/eric.hollering)\
**Post date:** [March 17, 2023, 9:46pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/1 "2023-03-17T21:46:43Z")

</div>

In my module I have a manager-style object in the Gateway scope to which we can add typed EventListeners. This works great in Java, but we have a use-case that would benefit from being able to add an event listener for a specific project (that is very specific to a customer, and so we'd like to not include anything related to it in the module itself).

We thought perhaps we could add an event listener with Python in the Gateway Startup Script, but quickly found that doing so was a little convoluted, to say the least. In the end, I wrote a wrapper class for the event that handles clean up and what-not, and it appears to "work", but unfortunately it doesn't seem to have access to most of the script engine tools we'd need, like the system namespace. So it's not good for much beyond throwing text in the wrapper log.

I was wondering whether there was a good method to add a Gateway-scoped event handler where a customer using the module could add in a custom script -- similar to the web dev module's handlers.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [March 17, 2023, 10:00pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/2 "2023-03-17T22:00:06Z")

</div>

There's lots of _possible_ ways to do this, but one of the easiest on you and your end users is to just send a message (from your module code) to a specific project's named message handler.

[https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/gateway/model/GatewayContext.html#getMessageDispatchManager()](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/gateway/model/GatewayContext.html#getMessageDispatchManager())  
[https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/common/script/message/MessageDispatchManager.html#dispatch(java.lang.String,java.lang.String,org.python.core.PyDictionary,java.util.Properties)](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/common/script/message/MessageDispatchManager.html#dispatch(java.lang.String,java.lang.String,org.python.core.PyDictionary,java.util.Properties))

> [@Calling the system.util.sendMessage function from a Module?](https://forum.inductiveautomation.com/t/calling-the-system-util-sendmessage-function-from-a-module/12063/4):
>
> What you will have to do is dispatch the message using MessageDispatchManager, and then you need to set up a message handler in a project that can call a script function in your module (example shown below). Send code (Java): MessageDispatchManager mdm = gatewayContext.getMessageDispatchManager(); PyDictionary payload = new PyDictionary(new PyObject[] { new PyString("Source"), new PyString("Gateway"), new PyString("Message"), new PyString("testMessage") }); // Limit to Gatewa…

---

<div class="post-metadata">

**Author:** ![eric.hollering](https://avatars.discourse-cdn.com/v4/letter/e/ad7895/32.png) [@eric.hollering](https://forum.inductiveautomation.com/u/eric.hollering)\
**Post date:** [March 22, 2023, 3:13pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/3 "2023-03-22T15:13:21Z")

</div>

Okay, I see how we could leverage the message handler for this, but it feels a little dirty since we'd have to know the project name and message handler name for a given implementation, which means we'd have to put that in our app configuration or let it fall into the "secret magic wire up" category.

Let's say we wanted to go the less simple, more flexible route, and define gateway scoped event handlers in the designer the same way the web dev module allows for scripting of HTTP method handlers. Are those elements available in the SDK? And if so, are there resources we can review that would help us understand how to interact with the right classes in the SDK?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [March 22, 2023, 3:38pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/4 "2023-03-22T15:38:40Z")

</div>

So, since you're talking about a project resource, there's a fair bit of process to learn before you get anywhere useful.

In the designer,

- You'll have to register your new resource type, either as a top level node on its own or maybe nested under the scripting node; see:
  - [`DesignerContext.getProjectBrowserRoot()`](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/designer/model/DesignerContext.html#getProjectBrowserRoot())
  - [`ProjectBrowserRoot.getScriptFolder`](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/designer/navtree/model/ProjectBrowserRoot.html#getScriptFolder())
  - The [getResourceX](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/designer/model/AbstractDesignerModuleHook.html#getResourceCategoryKey(com.inductiveautomation.ignition.common.project.resource.ProjectResourceId)) methods on your designer hook.
  - The common resource package; you'll want to define, at minimum, your own [`ResourceType`](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/common/project/resource/ResourceType.html) in a common scope; you'll need it for both the designer and gateway.

- Your actual editing workspace can be whatever Swing UI you dream up. If you want to allow easy editing of multiple resources in the same workspace, you probably want to extend [`TabbedResourceWorkspace`](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/designer/tabbedworkspace/TabbedResourceWorkspace.html)
- For the best script editing experience, you'll want to be compiling to an SDK version new enough to use `com.inductiveautomation.ignition.designer.gui.tools.PythonTextArea` or `com.inductiveautomation.ignition.designer.gui.tools.ExtensionFunctionPanel`, which unfortunately don't show up in our javadocs, but are still publicly accessible in the SDK.  
For a practical, but not particularly fleshed out example of all this designer side scaffolding, you can take a look at this module:  
[GitHub - paul-griffith/markdown-resources: Example of creating a custom project resource type, with editor, in Ignition](https://github.com/paul-griffith/markdown-resources)

Now that you're all set on the designer and you're creating project resources of your custom type, you have to decide what to do with them on the gateway.  
Probably the best approach would be to create your own [`ProjectLifecycleFactory`](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/gateway/project/ProjectLifecycleFactory.html), which will do the necessary work to automatically create new [`ProjectLifecycle`](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/gateway/project/ProjectLifecycle.html) instances for you as your projects change. Within the lifecycle you can react to individual resource changes.  
That will probably entail reaching out to a [ScriptManager to compile a function](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/ignition/common/script/ScriptManager.html#compileFunction(java.lang.String,java.lang.String,java.lang.String)) every time it changes (so that you only pay the overhead of compiling the Jython code once) and then executing it as needed. You should probably be serializing only the 'user' portion of the script from your designer scope, so you'll want to have a common-scoped `com.inductiveautomation.ignition.common.script.typing.ExtensionFunctionDescriptor` you can use in both the designer and gateway; in the gateway you'll call `buildDef()` on it to get the first line of the extension function you want to pass into `compileFunction()`.  
You'll also want to make sure you're using the script manager returned by `GatewayContext.getProjectManager().getProjectScriptManager(String projectName)`, so that end users are able to use project libraries and access the default project database, user source, etc.

That's _basically_ it 😉  
It's _not_ a particularly easy process (mostly because you don't have many resources to go on) but it _is_ quite doable; these APIs are much more ergonomic and consistent than the 7.9 and earlier project system, implausible as that might seem. There's also a lot I'm handwaving over here, including most of how to interact with the project system; you'll likely want to be guided by the resource workspace methods and go from there.

---

<div class="post-metadata">

**Author:** ![eric.hollering](https://avatars.discourse-cdn.com/v4/letter/e/ad7895/32.png) [@eric.hollering](https://forum.inductiveautomation.com/u/eric.hollering)\
**Post date:** [March 22, 2023, 4:03pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/5 "2023-03-22T16:03:57Z")

</div>

It sounds like there's certainly some learning curve, which was expected, but lots of good info there, so thank you!

We'll likely take some time to digest this and try some things, and perhaps have some further communication before settling on an approach, but this helps give some idea of what's involved.

---

<div class="post-metadata">

**Author:** ![eric.hollering](https://avatars.discourse-cdn.com/v4/letter/e/ad7895/32.png) [@eric.hollering](https://forum.inductiveautomation.com/u/eric.hollering)\
**Post date:** [April 19, 2023, 9:40pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/6 "2023-04-19T21:40:11Z")

</div>

For what it's worth, we settled on an approach of creating a MessageHandlerEventSubscription class that wires the message handler in a project up to our event manager. That way we can do the wire up at runtime using the Gateway events in a project.

Then we exposed a script that handles creation of the subscription, which in turn creates a corresponding EventListener object of the specified type. Subscriptions are then tracked within our EventManager class. The script that creates the subscription also takes care of removing any previous instances of the subscription and removing its EventListener.

So far it seems to work pretty well, and the listeners appear to clean up okay whenever the project is updated and/or the gateway/module is restarted.

Of course, we may revisit the potential project resource approach down the road.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 19, 2023, 10:34pm UTC](https://forum.inductiveautomation.com/t/gateway-scoped-event-handler-script/71263/7 "2023-04-19T22:34:36Z")

</div>

If I get some free time (haha) I might make a new SDK example that demonstrates all of the above. There's a few different concepts that all tie together that it would be good to demonstrate the ideal configuration for.
