# GatewayHook getRPCHandler Implementation

**URL:** <https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980>\
**Category:** Module Development\
**Created:** [July 26, 2023, 11:40pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980 "2023-07-26T23:40:23Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jason\_Zhou](https://avatars.discourse-cdn.com/v4/letter/j/6bbea6/32.png) [@Jason\_Zhou](https://forum.inductiveautomation.com/u/Jason_Zhou)\
**Post date:** [July 26, 2023, 11:40pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/1 "2023-07-26T23:40:23Z")

</div>

I'm working on a module that will contain a variety of scripting functions. Because I don't want every function defined in one class, I've created 3 (for now), and am having issues "combining" them.

In the sdk-examples scripting-function example, there is only one class of functions, which can be easily returned in the getRPCHandler method.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/3/2372009a0869d30e19bfcd8ca467bd020628097d.png)

In my case, I'm having 3 separate module classes.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/4/3/43efafdd661465ee3e182a0f383aa26b4c9a084b.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/6/a6f487a844737c5b095a361939453dc593c76ac6.png)

My issue is that ultimately, getRPCHandler is only returning one object. What is the best way to "combine" these 3 module classes together, into one object to be returned? Or is an entirely different approach required at this point?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 26, 2023, 11:49pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/2 "2023-07-26T23:49:08Z")

</div>

Are you actually using RPC from Vision clients or the Designer?

While the example conflates and RPC handler with a class for scripting functions, that is a very simplistic combination, and only meaningful if you do not ever need to push messages back to specific Vision/Designer clients.

What RPC functionality do you really need? Do your scripting classes really need to be instantiated? (Can you not use static functions and just pass the class itself to register?)

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [July 26, 2023, 11:50pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/3 "2023-07-26T23:50:28Z")

</div>

This was asked/answered [here](https://forum.inductiveautomation.com/t/mimic-multiple-python-package-namespaces-in-single-module/54506) as well.

Basically, you get one RPC handler, but that _doesn't_ have to be the same object as your script modules, so multiplex it out however you want.

Also for your consideration: for years now we (IA) no longer add new scripting functions to Designer / Vision Client scope and relay them over RPC. We only add new scripting functions to Gateway scope.

---

<div class="post-metadata">

**Author:** ![Jason\_Zhou](https://avatars.discourse-cdn.com/v4/letter/j/6bbea6/32.png) [@Jason\_Zhou](https://forum.inductiveautomation.com/u/Jason_Zhou)\
**Post date:** [July 27, 2023, 12:02am UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/4 "2023-07-27T00:02:10Z")

</div>

I do not need to target specific clients. The designer, I'm just sticking as closely to the scripting function example as possible. Any coding choice I make is basically a result of that. I'm open to any suggestions.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 27, 2023, 12:04am UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/5 "2023-07-27T00:04:11Z")

</div>

> [@pturmel](#):
>
> use static functions and just pass the class itself to register

Do this. Three (or however many) classes of static functions. Private constructor (not used). Register those directly.

Omit `getRPCHandler` if you don't need it.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 27, 2023, 12:04am UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/6 "2023-07-27T00:04:44Z")

</div>

> [@Kevin.Herron](#):
>
> We only add new scripting functions to Gateway scope.

Meh. ):

---

<div class="post-metadata">

**Author:** ![Jason\_Zhou](https://avatars.discourse-cdn.com/v4/letter/j/6bbea6/32.png) [@Jason\_Zhou](https://forum.inductiveautomation.com/u/Jason_Zhou)\
**Post date:** [July 27, 2023, 4:41pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/7 "2023-07-27T16:41:25Z")

</div>

I've had this in place already, is this what you mean?

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/8/582a57a00fa2616185d9300387e45591a1b4978d.png)

---

<div class="post-metadata">

**Author:** ![Jason\_Zhou](https://avatars.discourse-cdn.com/v4/letter/j/6bbea6/32.png) [@Jason\_Zhou](https://forum.inductiveautomation.com/u/Jason_Zhou)\
**Post date:** [July 27, 2023, 4:43pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/8 "2023-07-27T16:43:28Z")

</div>

By omitting getRPCHandler, I assume you mean simply deleting the implementation of it. Which then I get this error, when attempting to run one of the module functions in the Designer script console:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/d/fd61019eddaf586c38824d0ddd77d0a31df687ba.png)

---

<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:** [July 27, 2023, 4:55pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/9 "2023-07-27T16:55:29Z")

</div>

Let's step back. What are your script modules actually doing? Do they _actually need_ any RPC, at all?  
The public SDK example leads you down that path, but you don't _need_ to use it if you can run your logic locally/self contained.

---

<div class="post-metadata">

**Author:** ![Jason\_Zhou](https://avatars.discourse-cdn.com/v4/letter/j/6bbea6/32.png) [@Jason\_Zhou](https://forum.inductiveautomation.com/u/Jason_Zhou)\
**Post date:** [July 27, 2023, 5:11pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/10 "2023-07-27T17:11:03Z")

</div>

They are just running SQL queries against our gateway DB's. I also plan on having the functions check the module's LicenseState to determine how they execute. I don't know if they need RPC, could you tell me a bit more about that? What kind of situations require an RPC?

---

<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:** [July 27, 2023, 5:29pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/11 "2023-07-27T17:29:28Z")

</div>

Well, one alternative would be to have your designer/client scoped classes rely on the existing facilities in the platform for sending SQL queries from the client. Probably not worth it, though; this is a pretty classic case for RPC.

Basically - the designer and client don't have all of the same facilities as the gateway - one of the obvious things is that the _designer_ is not actually connecting to your database(s). Instead, the designer just has to talk to the gateway, and the gateway talks to the database. This is more efficient on your database, eliminates the security risk of having every designer/client get DB credentials, etc. So the RPC mechanism built into the platform for use from clients/designers is all about "expose a limited subset of the functionality a module on the gateway has to clients/designers".

---

<div class="post-metadata">

**Author:** ![Jason\_Zhou](https://avatars.discourse-cdn.com/v4/letter/j/6bbea6/32.png) [@Jason\_Zhou](https://forum.inductiveautomation.com/u/Jason_Zhou)\
**Post date:** [July 27, 2023, 7:10pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/12 "2023-07-27T19:10:07Z")

</div>

Okay, so it seems like in this case, I do actually need the RPC then. So back to the original problem, what's the best way to "compile" all script modules together to return, in the getRPCHandler method?

---

<div class="post-metadata">

**Author:** ![Haniyeh\_Seyed\_Alinez](https://avatars.discourse-cdn.com/v4/letter/h/e99b99/32.png) [@Haniyeh\_Seyed\_Alinez](https://forum.inductiveautomation.com/u/Haniyeh_Seyed_Alinez)\
**Post date:** [August 22, 2024, 10:59pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/13 "2024-08-22T22:59:38Z")

</div>

Hello, I'm facing the same issue and found this discussion in the forum. Have you found a solution to this problem?

---

<div class="post-metadata">

**Author:** ![Benjamin\_Furlani](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/benjamin_furlani/32/66084_2.png) [@Benjamin\_Furlani](https://forum.inductiveautomation.com/u/Benjamin_Furlani)\
**Post date:** [August 23, 2024, 12:07am UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/14 "2024-08-23T00:07:14Z")

</div>

I would recommend supplying more information about your specific problem, or possibly making a new post about it with some screenshots of your code possibly and an explanation of what it is you are trying to accomplish.

---

<div class="post-metadata">

**Author:** ![Haniyeh\_Seyed\_Alinez](https://avatars.discourse-cdn.com/v4/letter/h/e99b99/32.png) [@Haniyeh\_Seyed\_Alinez](https://forum.inductiveautomation.com/u/Haniyeh_Seyed_Alinez)\
**Post date:** [August 23, 2024, 7:39pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/15 "2024-08-23T19:39:07Z")

</div>

I'm working on a module that contains multiple scripting functions. I am new to ignition module development and I am following IA's scripting function module training example available in its GitHub repository to do my development. I was able to develop a module with a single function, but am having issues combining more than one function in a single module. As an example, here is the overall structure of my module with two functions F1 and F2.

scripting-library  
|  
|  
|  
+---client  
| | build.gradle.kts  
| |  
| |  
| ---src  
| +---main  
| | ---java  
| | ---com  
| | ---inductiveautomation  
| | ---ignition  
| | ---scriptinglibrary  
| | ---client  
| | ScriptingLibraryClientHook.java  
| | ClientScriptModuleF1.java  
| | ClientScriptModuleF2.java  
| |  
+---common  
| | build.gradle.kts  
| |  
| |  
| ---src  
| +---main  
| | +---java  
| | | ---com  
| | | ---inductiveautomation  
| | | ---ignition  
| | | ---scriptinglibrary  
| | | ---common  
| | | AbstractScriptModuleF1.java  
| | | AbstractScriptModuleF2.java  
| | | InterfceClassF1.java  
| | | InterfceClassF2.java  
| | |  
| | ---resources  
| | ---com  
| | ---inductiveautomation  
| | ---ignition  
| | ---scriptinglibrary  
| | ---common  
| | AbstractScriptModuleF1.properties  
| | AbstractScriptModuleF2.properties  
| |  
+---designer  
| | build.gradle.kts  
| |  
| |  
| ---src  
| +---main  
| | ---java  
| | ---com  
| | ---inductiveautomation  
| | ---ignition  
| | ---scriptinglibrary  
| | ---designer  
| | ScriptingLibraryDesignerHook.java  
| |  
+---gateway  
| | build.gradle.kts  
| |  
| |  
| ---src  
| +---main  
| | ---java  
| | ---com  
| | ---inductiveautomation  
| | ---ignition  
| | ---scriptinglibrary  
| | ---gateway  
| | ScriptingLibraryGatewayHook.java  
| | GatewayScriptModuleF1.java  
| | GatewayScriptModuleF2.java  
| |

This is an screen capture of "ScriptingLibraryGatewayHook.java".

 ![GatewayHook](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/f/6ffc3c93cbd56fc084cfb34c1b41706340d887c3.png)

My question is how I should set up this part of the code when I have more than one scripting function in my module, especially how to use both functions in getRPCHandler (it only returns one parameter).

---

<div class="post-metadata">

**Author:** ![Benjamin\_Furlani](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/benjamin_furlani/32/66084_2.png) [@Benjamin\_Furlani](https://forum.inductiveautomation.com/u/Benjamin_Furlani)\
**Post date:** [August 23, 2024, 7:56pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/16 "2024-08-23T19:56:23Z")

</div>

Just for your knowledge it should return an object that implements an interface of your RPC

```Java
    private ApexRPCInterface rpc;

    @Override
    public void startup(ClientContext context, LicenseState activationState) throws Exception {
        logger.info(context.getDefaultDatasourceName());
        this.context = context;
        logger.info("Setup Ran");
        this.rpc = ModuleRPCFactory.create(
                "org.apex.rpm",
                ApexRPCInterface.class);
    }

```

This RPC is not going to be the script module you are trying to use. It should be something that you need to run things on the gateway.

So for example:

```Java
package org.apex.util;

import java.sql.SQLException;

import org.python.core.PyObject;

import com.inductiveautomation.ignition.common.Dataset;
import com.inductiveautomation.ignition.gateway.datasource.SRConnection;
import com.inductiveautomation.ignition.gateway.model.GatewayContext;

public class ApexRPC implements ApexRPCInterface {
    private GatewayContext gatewayContext;

    public ApexRPC(GatewayContext context) {
        this.gatewayContext = context;
    }

    @Override
    public Dataset runQuery(String query, String connectionName) throws SQLException {
        Dataset result;
        try (SRConnection con = gatewayContext.getDatasourceManager().getConnection(connectionName)) {
            result = con.runQuery(query);
        }
        return result;
    }
}

```

You can add other things to your Interface to implement on the actual RPC. This then gives the capability for you to access the gateway functionality you don't have in the client.

This is as I understand it based around some RPC models. The key is you do not/should not expose the entire gateway context to your client or designer. Instead limit it to your functional requirements.

And for those _experts_ out there if I am wrong in this I would like to know to improve what I have going on.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [August 23, 2024, 8:01pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/17 "2024-08-23T20:01:40Z")

</div>

I keep repeating this, but build an RPC handler object that multiplexes/delegates the requests. Something like this:

```java
class MultiplexingRPCHandler 
    implements ScriptingFunctionInterfaceA, ScriptingFunctionInterfaceB {

    private final ScriptingFunctionImplA a;
    private final ScriptingFunctionImplB b;

    MultiplexingRPCHandler(ScriptingFunctionImplA a, ScriptingFunctionImplA b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void functionFromA() {
        a.functionFromA();
    }

    @Override
    public void functionFromB() {
        b.functionFromB();
    }

}

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 23, 2024, 8:06pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/18 "2024-08-23T20:06:41Z")

</div>

The connection between the RPC interface and the scripting functions is an artificial constraint implied by the example, but not actually true.

What you actually need is:

- An interface in `Common` that stubs the functions _ **for which you need RPC** _. Only those functions, not anything else your scripts might do.

- An implementation of that interface in `Client` (and indirectly Designer) that delegates to the RPC invocation methods for each such function.

- An implementation of that interface in `Gateway` that actually does the work, possibly with per-client session management. This class can be just wrapper functions that delegate anywhere else in the gateway you wish.

The actual classes or class instances in which you register scripting functions _ **do not have to be the above** _. They just have to **call** the above where needed.

You can register many different scripting function classes, organized appropriately, of which **some** call RPC methods.

I recommend you **not** register your RPC classes as scripting modules.

---

<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:** [August 23, 2024, 9:06pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/19 "2024-08-23T21:06:50Z")

</div>

Following the pattern Phil describes above will also make your migration to 8.3's new RPC interface as smooth as possible. You'll still have to recompile against the new classes/APIs, but it'll be a pretty straightforward migration.

---

<div class="post-metadata">

**Author:** ![Haniyeh\_Seyed\_Alinez](https://avatars.discourse-cdn.com/v4/letter/h/e99b99/32.png) [@Haniyeh\_Seyed\_Alinez](https://forum.inductiveautomation.com/u/Haniyeh_Seyed_Alinez)\
**Post date:** [August 26, 2024, 6:13pm UTC](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980/20 "2024-08-26T18:13:02Z")

</div>

Thanks to all of you for your helpful comments.

As shown in the example Scripting Library module structure tree I provided in my previous message,

1. I have defined separate interfaces (and abstract base classes) for each scripting function F1 and F2 in Common.

The purpose of this step is to follow @pturmel's comment "An interface in Common that stubs the functions for which you need RPC. Only those functions, not anything else your scripts might do."

1. To follow @pturmel 's comment "An implementation of that interface in Client (and indirectly Designer) that delegates to the RPC invocation methods for each such function.",

The following code snippet has been developed in ClientScriptModuleF1 that makes this class delegate calls to the rpc object. The same logic has been implemented in ClientScriptModuleF2.

```python
package com.inductiveautomation.ignition.scriptinglibrary.client;

import com.inductiveautomation.ignition.client.gateway_interface.ModuleRPCFactory;

import com.inductiveautomation.ignition.scriptinglibrary.common.InterfceClassF1 ;

import com.inductiveautomation.ignition.scriptinglibrary.common.AbstractScriptModuleF1;

public class ClientScriptModuleF1 extends AbstractScriptModuleF1 {

    private final InterfceClassF1 rpc;

    public ClientScriptModuleF1() {

        rpc = ModuleRPCFactory.create(

                "com.inductiveautomation.ignition.scriptinglib.ScriptingLibrary",

                InterfceClassF1.class

        );

    }

    @Override

    protected String FunctionFromF1Impl(String param1, String param2) {

        return rpc.FunctionFromF1(param1, param2);

    }

}

```

Also, the following code snippet has been included in ScriptingLibraryClientHook and ScriptingLibraryDesignerHook

```python
@Override

    public void initializeScriptManager(ScriptManager manager) {

        super.initializeScriptManager(manager);

        manager.addScriptModule(

                "system.newlib",

                new ClientScriptModuleF1(),

                new PropertiesFileDocProvider());

        manager.addScriptModule(

                "system.newlib",

                new ClientScriptModuleF2(),

                new PropertiesFileDocProvider());

    }

}

```

1. To follow @pturmel 's third comment, I have developed GatewayScriptModuleF1 and GatewayScriptModuleF2 classes as implementation of the interfaces in Gateway that actually does the work.  
And  
To manage multiple objects in RPC handler I added the following class to Gateway scope as suggested by @Kevin.Herron:

```python
package com.inductiveautomation.ignition.scriptinglibrary.gateway;

import com.inductiveautomation.ignition.scriptinglibrary.common.InterfceClassF1;

import com.inductiveautomation.ignition.scriptinglibrary.common.InterfceClassF2;

public class MultiplexingRPCHandler implements InterfceClassF1, InterfceClassF2 {

    private final GatewayScriptModuleF1 scriptModuleF1;

    private final GatewayScriptModuleF2 scriptModuleF2;

    public MultiplexingRPCHandler(GatewayScriptModuleF1 scriptModuleF1, GatewayScriptModuleF2 scriptModuleF2) {

        this.scriptModuleF1 = scriptModuleF1;

        this.scriptModuleF2 = scriptModuleF2;

    }

    @Override

    public String FunctionFromF1(String param1, String param2) {

        return scriptModuleF1.FunctionFromF1Impl(param1, param2);

    }

    @Override

    public String FunctionFromF2(String param1, String param2) {

        return scriptModuleF2.FunctionFromF2Impl(param1, param2);

    }

}

```

Then, revised ScriptingLibraryGatewayHook as follows:

```python
public class ScriptingLibraryGatewayHook extends AbstractGatewayModuleHook {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final GatewayScriptModuleF1 scriptModuleF1 = new GatewayScriptModuleF1();

    private final GatewayScriptModuleF2 scriptModuleF2 = new GatewayScriptModuleF2();

    private final MultiplexingRPCHandler rpcHandler = new MultiplexingRPCHandler(scriptModuleF1, scriptModuleF2);

    @Override

    public void setup(GatewayContext context) {

    }

    @Override

    public void startup(LicenseState activationState) {

    }

    @Override

    public void shutdown() {

    }

    @Override

    public void initializeScriptManager(ScriptManager manager) {

        super.initializeScriptManager(manager);

        manager.addScriptModule(

                "system.newlib",

                scriptModuleF1,

                new PropertiesFileDocProvider());

        manager.addScriptModule(

                "system.newlib",

                scriptModuleF2,

                new PropertiesFileDocProvider());

    }

    @Override

    public Object getRPCHandler(ClientReqSession session, String projectName) {return rpcHandler;}

    }

```

When building and testing my developed module, it appears to work correctly. Would you mind confirming whether I am on the right track?

[Next page](https://forum.inductiveautomation.com/t/gatewayhook-getrpchandler-implementation/76980.md?page=2)
