/** * This method gets invoked (from .NET) on all listeners registered via JDProxy's addListener method. * @param source the proxy to the object raising the event (on which addListener was called) * @param evt the name of the event being raised * @param arg a string representation of event data */ public void onEvent(JDProxy source, String evt, Object arg)
As usual when two technologies communicate with each other, it is important to understand the marshaling mechanism for arguments and return values. The approach that jdbridge takes is similar to RMI and .NET remoting in that there is marshaling by value or by reference.
All primitive types (Boolean, byte, short, char, int, long, float, double) and Strings are marshaled by value and map one-to-one between Java and .NET. No special handling is required.
Similarly, framework collections classes are mapped one-to-one (Java Lists to .NET Lists and Java Maps to .NET Dictionaries).
For general objects, if the .NET object implements the IXmlSerializable interface, it is marshaled back to Java by value. This means that a Java class with the exact same format (package, name, methods, and so on) must exist in the classpath. Otherwise it is marshaled by reference as a JDProxy. This lets you chain JDProxy calls and pass a JDProxy as an argument to another JDProxy call.
Exceptions that are raised in .NET are also propagated to Java and thrown as RuntimeExceptions with a stack trace spanning both Java and .NET code.
Example
Assume there is a .NET dll named AcmeUtils.dll in the bin directory. This dll contains the type com.acme.Calculator that has all the usual arithmetic functions: Add, Subtract, and so on. You want to invoke those functions from a JavaScript step. Here is a script that invokes them:
import com.itko.lisa.jdbridge.JDInvoker; import com.itko.lisa.jdbridge.JDProxy; JDInvoker.startCLR(); JDProxy calc = JDProxy.newInstance(Environment.LISA_HOME + "bin\\AcmeUtils.dll", "com.acme.Calculator", new Object[0]); Integer sum = (Integer) calc.invoke("Add", new Object[] { 3, 4 }); Double ratio = (Double) calc.invoke("Divide", new Object[] { 3.0, 4.0 }); Integer square = (Integer) calc.invoke("Square", new Object[] { 5 }); ...
The arguments are passed explicitly into an array of Objects because the DevTest BeanShell does not support the varargs (...) notation. If you were to code this in an extension, the syntax becomes less cumbersome.
Suppose the Calculator object exposes the OnCalculationStart and OnCalculationEnd events and you want to subscribe to those events to measure the duration of the calculation:
... JDProxy calc = JDProxy.newInstance(Environment.LISA_HOME + "bin\\AcmeUtils.dll", "com.acme.Calculator", new Object[0]); calc.addListener("OnCalculationStart", new JDProxyEventListener() { public void onEvent(JDProxy source, String evt, Object arg) { //capture timestamp here } }); calc.addListener("OnCalculationEnd", new JDProxyEventListener() { public void onEvent(JDProxy source, String evt, Object arg) { //capture timestamp here } }); Long fact = (Long) calc.invoke("Factorial", new Object[] { 30 }); ... // diff the timestamps here ...
If your project involves extensive use of .NET assemblies, it is a good idea to wrap all interactions with .NET code inside compiled extensions. In this case, the standard pattern would look like the following:
package com.acme; import com.itko.lisa.jdbridge.JDInvoker; import com.itko.lisa.jdbridge.JDProxy; import com.itko.lisa.jdbridge.JDProxyEventListener; public class Calculator { static { JDInvoker.startCLR(); } private JDProxy m_proxy = JDProxy.newInstance(Environment.LISA_HOME + "bin\\AcmeUtils.dll", "com.acme.Calculator"); public int add(int x, int y) { return ((Integer) m_proxy.invoke("Add", new Object[] { x, y })).intValue(); } ... }
This extension can be invoked from a custom JavaScript step or even the Complex Object Editor (COE).
Copyright © 2014 CA Technologies.
All rights reserved.
|
|