JavaNetBridge for OAA2
Overview
Code Generation
DotNetProxy Agent
.NET API
Callbacks
COM Interoperability
Notes
Overview
JavaNetBridge is used to bridge .NET and Java code for the OAA2 Java libraries. JavaNetBridge consists of a minimal set of runtime components which are used for data marshaling and infrastructure, and at the core of JavaNetBridge are source code generators. The generators are used to generate Java proxy code, Java stubs and csharp stubs for bi-directional communication between .NET and Java. JavaNetBridge is used to generate the source code for the
Back To TopdotnetproxyagentandOaa2JavaNet.dll(the .NET OAA2 assembly). This document provides an overview of the basic concepts behind JavaNetBridge and how it is used to access OAA2 from .NET. For other topics such as building dotnetproxyagent, running the generators and developing with Oaa2JavaNet, see index.html.Code Generation
The first step in bridging the Java code to .NET is to generate the java stub and proxy code. The java generator is used for this purpose. The java generator accepts input classes and/or jar files. Then, using reflection the generator retrieves the APIs for all the classes and proceeds to generate proxy and stub code. This generator is run when building the OAA2 agent "dotnetproxyagent" located in the directory "src/dotnetproxyagent". See building-dotnet.html for instructions on how to build the dotnetproxyagent agent (which runs the generator).
The generator generates Java source which contain proxy methods. A proxy method is a wrapper method for a call to a Java object method or Java constructor. The wrapper method accepts and returns primitive types only, with Objects identified by an id. A local object database is kept where ids are mapped to objects and vice-versa, and reference counts are maintained on the objects. In this way, every method call only requires Java primitive types (int, short, byte, long, float, String, int[], short[], ..., int[][], short[][], ...). Furthermore, every proxy method has a unique id associated with it. A socket API is used so that a client can connect to the Java Proxy, and invoke proxy methods by the method id, sending and reading data types through the socket connection.
The csharp generator generates the csharp source code. The csharp source code mirrors the generated Java code. The csharp generator is executed when building the dotnet library for oaalib, in the directory "src/oaalib/dotnet". The current generated source files are also contained in the zip file "src/oaalib/dotnet/Source/csharp/generated_source.zip" (this is done as a matter of convenience so that the source can be examined without having to build the dotnet library). The csharp generator also adds the generated files to a pre-configured .csproj .NET project file, which is used to build the .NET assembly Oaa2JavaNet.dll. All this is done when building the dotnet oaalib library. See building-dotnet.html for instructions on building the .NET OAA2 library.
Back To TopDotNetProxy Agent
The generated csharp code contains the required socket APIs and data marshaling code to communicate properly with the Java Proxy. The Java Proxy code is executed as an application, which accepts socket connections and acts as the proxy between .NET and Java. This is the application which processes requests to invoke proxy methods and handles basic data marshaling between .NET and Java. It is known as the dotnetproxyagent. This agent must be run before executing an application which uses
Oaa2JavaNet.dll.See running-dotnetproxyagent for instructions on how to build and run the agent. Note also that this agent is capable of handling mutliple socket connections from different .NET applications. Each application which connects to the agent is assigned a unique object database and a new instance of the Java Proxy handling code is created. In this way, the dotnetproxyagent can be used with multiple .NET applications simultaneously.
Back To Top.NET API
JavaNetBridge was designed so that almost all of the OAA2 Java library could be generated (98% or more). JavaNetBridge is also used to generate
java.lang.*classes as well as various collection classes located in thejava.utilpackage.The .NET APIS are a mirror image of the Java APIs with some exceptions. Some protected Java classes are made "public abstract" in csharp in order to avoid inconsistent accesibility errors. This is due to fundamental differences between the csharp and java languages. Also, methods and constructs which have parameters or return types that are arrays of ant non-primitive type are not supported. These methods and constructors are ommitted when the source code is generated.
For example, consider the class
com.sri.oaa2.icl.IclTerm. The following are snippets of the source code:The csharp equivalent for these code snippets is (note that there are many more methods generated than this; the code is also snipped here):public abstract class IclTerm implements Cloneable, AST, OaaPrologVocabTokenTypes, Serializable { // Code snipped... public abstract int hashCode(); public abstract boolean equals(Object o); public boolean equals(IclTerm t) { // Snipped... } // Code snipped... public abstract IclTerm getTerm(int i) throws UnsupportedOperationException; public abstract int size(); // Code snipped... }Notice that the csharp code has the same package name with the prefixnamespace jnb.com.sri.oaa2.icl { public abstract class IclTerm : JavaObject, jnb.com.sri.oaa2.icl.OaaPrologVocabTokenTypes { public new const String JAVA_CLASS_NAME = "com.sri.oaa2.icl.IclTerm"; public static IclTerm newIclTerm(JavaObject obj) { return new IclTerm_StubImpl(obj); } public static IclTerm newIclTerm(JavaObjectId id) { return new IclTerm_StubImpl(id, true); } public IclTerm(JavaObject objToCast) : base(objToCast.getObjId(), true) { } public IclTerm(JavaObjectId objId, bool addRef) : base(objId, addRef) { } public abstract override int hashCode(); public abstract override bool equals(JavaObject arg0); public bool equals(jnb.com.sri.oaa2.icl.IclTerm arg0) { return Oaa2JavaInterop.getSingleton().IclTerm_4_equals(getObjId(), arg0 != null ? arg0.getObjId() : null); } public abstract int size(); public abstract jnb.com.sri.oaa2.icl.IclTerm getTerm(int arg0); } }jnbadded. Also note that the class inherits fromJavaObject, found inJavaObject.cs. The generator generates the csharp code forJavaObject,JavaSystem,Runtimeand many other classes in thejnb.java.langpackage.The class which does the work of data marshaling and method calling is
Oaa2JavaInterop, located atSource/csharp/generated/com/sri/sedc/javanetbridge/Oaa2JavaInterop.cs. When csharp methods and constructors are invoked, they always delegate to this generated class which mirrors the generated Java Proxy code.Every generated csharp class has a const static String member named
JAVA_CLASS_NAMEwhich is the name of the Java class from which it is derived. Furthermore, two methods are provided namednew(shortName), where(shortName)is the name of the class. For example, the methods namednewIclTermabove can be used to cast any Java object to an IclTerm. Note also that theJavaObjectIdcan be supplied as well when casting. This id uniquely identifies an object and can be retrieved from any JavaObject. To better illustrate this, note the declaration of a JavaObject class in csharp (found in Source/csharp/generated/java/lang/Object.cs):Every JavaObject ultimately inherits from the classpublic class JavaObject : JavaRefObjectImplJavaRefObjectImpl. The following is the implementation of this class, which is declared in the generated source codeSource/csharp/generated/com/sri/sedc/javanetbridge/Oaa2JavaInterop.cs:Note that the primary purpose of this class is handle object referencing and store the JavaObjectId. The definition of the JavaObjectId class is located at Source/csharp/com/sri/sedc/javanetbridge/JavaObjectId.cs. JavaObjectId contains the id and class name of an object. So, the methodpublic abstract class JavaRefObjectImpl : JavaRefObjectAbstractImpl, JavaRefObject { private JavaObjectId objId = null; protected JavaRefObjectImpl() { } ~JavaRefObjectImpl() { releaseRef(); } public JavaRefObjectImpl(JavaObjectId objId, bool addRef) { setObjectId(objId, addRef); } protected void setObjectId(JavaObjectId objId, bool addToRef) { releaseRef(); this.objId = objId; if (addToRef) addRef(); } protected void addRef() { if (objId != null) { Oaa2JavaInterop.getSingleton().getJavaNetBridgeBean().addRef(getObjId().getId()); } } protected void releaseRef() { if (objId != null) { Oaa2JavaInterop.getSingleton().getJavaNetBridgeBean().releaseRef(getObjId().getId()); } } public override JavaObjectId getObjId() { return objId; } }getObjIdcan be used to obtain the id and class name of any JavaObject. Also, note that reflection is supported with a limited API. So, it is possible to call thegetClass()method on a JavaObject which returns a .NET instance ofjnb.java.lang.Class.Note that stubs are implemented for interfaces and abstract classes, so that those interfaces and classes can be used in .NET as actual interface definitions and abstract class definitions, while the stubs are the concrete implementations. For example, the class
Back To TopOAAEventListenerhas a generated stub which isOAAEventListener_StubImpl. Also, the abstract classIclTermTraverserhas a stub namedIclTermTraverser_StubImpl. These stubs are provided in the event that a Java method returns an abstract interface or class for which there is not a suitable csharp class. It is worth noting here because it is a feature of JavaNetBridge which allows more dynamic type resolution. For example, a stub can be created in order to cast a java object to an interface which it implements.Callbacks
As mentioned previously, callback stubs are generated for interfaces. The callback stub is a class that another .NET class can inherit from. Once a .NET class inherits from the callback stub, it becomes a JavaObject which implements the callback interface, and can thus be passed to any method which requires that interface. For example, the following is the definition for
OAAEventListener:The following is the definition for the callback stub,namespace jnb.com.sri.oaa2.lib { public interface OAAEventListener : JavaRefObject { bool doOAAEvent(jnb.com.sri.oaa2.icl.IclTerm arg0, jnb.com.sri.oaa2.icl.IclList arg1, jnb.com.sri.oaa2.icl.IclList arg2); } }OAAEventListener_CallbackImpl.cs:As you can see, this callback implementation handles retrieval of callback parameters and invoking the interface methods. A .NET class can inherit from this callback implementation in order to implement OAAEventListener. For example:namespace jnb.com.sri.oaa2.lib { public abstract class OAAEventListener_CallbackImpl : CallbackImpl, OAAEventListener { public OAAEventListener_CallbackImpl() : base("com.sri.oaa2.javanetbridge.callbacks.com_sri_oaa2_lib_OAAEventListener_CallbackImpl", Oaa2JavaInterop.getSingleton()) { } public override void doCallback(CallbackParams cparams) { int methodId = cparams.getMethodId(); switch (methodId) { case 0: JavaObject arg0_0 = cparams.getParameter(0); JavaObject arg0_1 = cparams.getParameter(1); JavaObject arg0_2 = cparams.getParameter(2); cparams.setReturn(new JavaBoolean(doOAAEvent((jnb.com.sri.oaa2.icl.IclTerm)arg0_0, (jnb.com.sri.oaa2.icl.IclList)arg0_1, (jnb.com.sri.oaa2.icl.IclList)arg0_2))); break; } } public abstract bool doOAAEvent(jnb.com.sri.oaa2.icl.IclTerm arg0, jnb.com.sri.oaa2.icl.IclList arg1, jnb.com.sri.oaa2.icl.IclList arg2); } }An instanceof AddAgentCallback will implement OAAEventListener, and can then be passed to any method requiring an OAAEventListener, like the registerCallback method of LibOaa. Back To Topusing System; using com.sri.sedc.javanetbridge; using jnb.com.sri.sedc.javanetbridge; using jnb.com.sri.oaa2.lib; using jnb.com.sri.oaa2.icl; using jnb.com.sri.oaa2.com; using jnb.java.lang; namespace com.sri.oaa2.agent.add { class AddAgentCallback : OAAEventListener_CallbackImpl { public override bool doOAAEvent(IclTerm goalTerm, IclList parameters, IclList answers) { Console.WriteLine("doOAAEvent called, goal = " + goalTerm); String goal = goalTerm.toIdentifyingString(); if (goal.Equals("add")) { int num1 = ((IclInt)goalTerm.getTerm(0)).toInt(); int num2 = ((IclInt)goalTerm.getTerm(1)).toInt(); int sum = num1 + num2; IclStruct answer = new IclStruct(goal, new IclInt(num1), new IclInt(num2), new IclInt(sum)); answers.add(answer); return true; } return false; } } }COM Interoperability
In addition to the assemble
Oaa2JavaNet.dll, the filelib/win32/Oaa2JavaNet.tlbis generated which is the type library for COM interoperability. The build process creates this type library, and properly registersOaa2JavaNet.dllfor use with COM. It is important to note that this only occurs when building the .NET library. A naked distribution of OAA2 will contain the fileOaa2JavaNet.tlb, but the dll will need to be registered before COM interoperability can be achieved using the .NETregasmandgacutilutilities. For more information, see installing-dotnet.html.A factory interface class named
Back To TopOaa2COMFactoryandOaa2COMFactoryImplare created so that .NET classes can be created using COM. The classLibOaaEventsis used for COM events, and the classLibOaaUtilis used to access key static methods of the .NET library which are otherwise innacessible from COM. For more information on developing with Oaa2JavaNetBridge and COM, see developing-com.html.Notes
JavaNetBridge was devloped on a short time span specifically for OAA2, and has only been tested on OAA2 source code. Permission is only granted to use JavaNetBridge on OAA2 source code. See
src/dotnetproxyagent/Library/generator_license.txtfor details.A number of sample agents are provided in the directory
src/samples_dotnet, including the TestAgent, which is use in conjunction with the oaalib unit tests and Java tests. A TestAgent is also provided in Visual Basic 6.0 which uses the .NET assembly (through COM-interop, as mentioned above), located in the directorysrc/samples_vb.For more information on developing with Oaa2JavaNetBridge, see develop.html.
Back To Top