Passing a Call Context from .NET Using J-Integra® for .NET

Java/J2EE .NET Interoperability Products Page

This example demonstrates how to pass a Call Context from .NET. J-Integra® for .NET is a Java interoperability component that bridges Java/J2EE and Microsoft .NET. It provides bi-directional access of Java objects and .NET components.

Synopsis

Use this example as a guideline to show you how establish a call context before making a method call to a Java object exposed a .NET Singleton using J-Integra® for .NET. After subscribing to com.intrinsyc.janet.InvocationListener, a pre and post invocation method is called before and after the call to the actual Java method.

The Steps to Follow will show you how to deploy J-Integra® for .NET correctly for this example.

System.Runtime.Remoting.Messaging.CallContext

The CallContext class is used for holding data in a way similar to Thread Local Storage for method calls. It provides data slots that are unique to each logical thread of execution. The slots are not shared across call contexts on other logical threads. Objects can be added to CallContext as it travels down and back up the execution code path, and examined by various objects along the path.

When a remote method call is made to an object in another Application Domain, CallContext generates a LogicalCallContext instance that travels along with the remote call. Only objects that expose the ILogicalThreadAffinative interface and are stored in the CallContext are propagated outside the Application Domain in a LogicalCallContext. Objects that do not support this interface are not transmitted in LogicalCallContext instances with remote method calls.

J-Integra® for .NET exposes the class com.intrinsyc.janet.Credentials that, when run through GenNet, will generate a C# class that implements ILogicalThreadAffinative. The com.intrinsyc.janet.Credentials class is extremely simple - it contains public fields for a username and password.

Note: $USER_INSTALL_DIR$ is the directory where J-Integra® for .NET has been installed. If this location contains whitespace characters (e.g. C:\Program Files\...), you may need to include it within "" when referencing it at the command-line.

Prerequisites

  1. A platform with Microsoft Visual Studio .NET 2003 (7.1.3088 or higher) installed, or at least the .NET Framework 1.0.3705 or higher. This will be known throughout the example as the .NET platform.

  2. A platform with J-Integra® for .NET 1.5.1279 (or higher) installed. This will be known throughout the example as the Java platform.

  3. A version of JDK (1.3.1 or higher) installed on the Java platform, and the bin directory included in the path. This is required to compile the Java class and run the J-Integra® for .NET server.

  4. Verify that GenService is installed correctly on the .NET platform.

Steps to Follow

  1. Run Janetor to Configure J-Integra® for .NET (1)

  2. Review the Java Source and Compile

  3. Run GenNet and Compile the Proxies

  4. Run Janetor to Configure J-Integra® for .NET (2)

  5. Start the J-Integra® for .NET Server

  6. Compile and Run the .NET Client

Run Janetor to Configure J-Integra® for .NET (1)

Perform this step on the Java platform.

In this step, you will need to configure J-Integra® for .NET as a server so it can respond to incoming TCP requests. This is done through the Janetor tool.

  1. Windows

    Launch Janetor from the Start Menu.

    Command-Line
    java -jar $USER_INSTALL_DIR$\lib\janetor.jar
    	
  2. Select File - Open and open janet.xml located in $USER_INSTALL_DIR$\examples\callcontext.

  3. Install your J-Integra® for .NET product license. See Installing the J-Integra® for .NET license for further information.

  4. Select java_server.Factory under Local Objects. Here you see that J-Integra® for .NET exposes this class as a Singleton at the at the given machine address and port using TCP.

    Using Janetor Configuration Tool setup local objects

  5. Before you exit Janetor, save the configuration by going to File - Save.

Review the Java Source and Compile

Perform this step on the Java platform.

In this step, you will take a look at the java_server.Factory class source code to get an idea of how J-Integra® for .NET makes use of the .NET call context.

  1. Open the file Factory.java located in $USER_INSTALL_DIR$\examples\callcontext.

    Notice that the class implements com.intrinsyc.janet.InvocationListener, and in the constructor, it adds itself as a listener for any method invocations:

    public Factory() throws RemoteException {
      Janet.addInvocationListener(this);
    }
    	

    The two methods that java_server.Factory inherits from com.intrinsyc.janet.InvocationListener are preInvocation and postInvocation. Each is called before and after the actual method call, respectively. Each of these methods take in a number of parameters, but the one we are most interested in is callContext. This is where the logical call context passed from .NET is stored. Take a look at preInvocation to see how the actual call context is retrieved from callContext:

    Credentials c = (Credentials) callContext.get("JanetCredentials");
    	

    JanetCredentials is a hard-coded constant in the J-Integra® for .NET runtime and it must be used to get at the actual call context each time. This cannot be changed.

    Similarly, notice that postInvocation modifies the data inside the call context before it is passed back to .NET.

  2. Finally, to compile the source, change directory to $USER_INSTALL_DIR$\examples\callcontext and enter:

    javac -classpath $USER_INSTALL_DIR$\lib\janet.jar;. java_server\Factory.java
    

Run GenNet and Compile the Proxies

Perform this step on the Java platform.

In this step, you will use the GenNet tool to generate .NET proxies for the Java classes.

Note: GenNet saves configuration details to an XML file. For your convenience, the settings described below have already been saved to the file gennet.xml in the directory $USER_INSTALL_DIR$\examples\callcontext. If you'd prefer not to manually enter the settings as instructed below, simply copy this file over to $USER_INSTALL_DIR$ before starting GenNet.
  1. Windows

    Launch GenNet from the Start Menu.

    Command-Line

    Change directory to $USER_INSTALL_DIR$ and then enter:

    java -jar lib\gennet.jar
    	
  2. The first screen of GenNet shows a list of machines that have GenService running (screenshot not shown). Select the appropriate one and click Next.

  3. The next screen prompts for the components (JAR files or directories) to be added to the CLASSPATH. Each of these components are required in order for the Java runtime to resolve references to Java classes when generating .NET proxies. You'll need to add the following component:

    - $USER_INSTALL_DIR$\examples\callcontext\

    Using GenNet generate .NET assemblies for Java Objects

    Move on to the next step by clicking Next.

  4. The next screen prompts for the specific Java classes that will have .NET proxies generated for them. Click on Browse... to add the following classes (use the CTRL key to select multiple classes):

    - java_server.Factory
    - com.intrinsyc.janet.Credentials

    Make com.intrinsyc.janet.Credentials a "marshal-by-value" object by clicking on Options... and checking Pass by value.

    Adding Java classes to be exported to .NET in GenNet

    Click on the Filter button and choose not to generate any proxies for the referenced classes as shown below. This will eliminate the number of .NET proxies created.

    Filter Referenced Classes in GenNet

    Move on to the next step by clicking Next.

  5. The next screen prompts you to specify the .NET Assembly name along with the output directory. The screenshot may show a different output directory than what Output Directory is, but it should be pointing to $USER_INSTALL_DIR$\examples\callcontext\csharp_client\bin\Debug.

    Setting output directory for .NET assembly in GenNET

  6. You should see a message that the proxy generation was successful. Click Exit to exit GenNet.

    GenNet proxy generation succeed

Run Janetor to Configure J-Integra® for .NET (2)

Perform this step on the Java platform.

In this crucial step, you'll need to create an entry for com.intrinsyc.janet.Credentials in Janetor under Local Objects. This is required because this class is passed to Java as part of System.Runtime.Remoting.Messaging.CallContext. As a result, .NET Remoting assumes that com.intrinsyc.janet.Credentials is part of mscorlib. The special entry in Janetor ensures that J-Integra® for .NET sends the correct full assembly name, and not that of mscorlib.

  1. Launch Janetor again, load the same configuration file as before, and right-click on Local Objects and select Add class....

  2. The Add Configuration Class is displayed. Enter in com.intrinsyc.janet.Credentials and click OK.

  3. Select the newly created com.intrinsyc.janet.Credentials node. For the Assembly Name enter in the full assembly name (including version) of the .NET Assembly that GenNet created for com.intrinsyc.janet.Credentials in the previous step:

    Using Janetor Configuration Tool setup local objects

    Next, uncheck Pass by Reference so that com.intrinsyc.janet.Credentials will be marshalled by value.

    Notice that the other fields are the same as the java_server.Factory node.

    To determine the full name of the .NET Assembly generated by GenNet: if you did not check Generate Strong Named Assembly when generating the Assembly, then it is of the form:

    [Assembly Name], Version=1.0.1376.17274, Culture=neutral, PublicKeyToken=null
    	

    Otherwise, you can use a .NET Framework provided tool like IDLASM to find it. Alternatively, the following two simple lines of C# code will give you the full name:

    System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(@"C:\temp\MyAssembly.dll");
    Console.WriteLine(assembly.FullName);
    	
  4. Before you exit Janetor, save the configuration by going to File - Save.

Start the J-Integra® for .NET Server

Perform this step on the Java platform.

In this step, you will start the J-Integra® for .NET server.

  1. To start the J-Integra® for .NET server, change directory to $USER_INSTALL_DIR$\examples\callcontext and enter:

    java -cp $USER_INSTALL_DIR$\lib\janet.jar;. com.intrinsyc.janet.Main
    	

Compile and Run the .NET Client

Perform this step on the .NET platform.

In this final step, you will review the .NET client source code, configure the .NET client using a configuration file, then compile and run it.

Along with .NET proxies, GenNet generates .NET configuration files to be used as templates - one for the HTTP channel and one for the TCP channel. As the .NET client will be using .NET Remoting's TCP binary to connect to the Java server, we will modify remoting_tcp.config. You can find this in the same directory that the proxies were generated in.

Note: In the .NET code, notice that a call is made to RemotingConfiguration.Configure("remoting_tcp.config");
  1. Modify remoting_tcp.config to look like:

    <configuration>
      <system.runtime.remoting>
        <application>
          <client>
            <wellknown url="tcp://localhost:8888/Server/java_server.Factory.soap" type="java_server.Factory, JanetExample""/>
          </client>
          <channels>
            <channel ref="tcp">
            <clientProviders>
              <formatter ref="binary"/>
            </clientProviders>
            </channel>
          </channels>
        </application>
      </system.runtime.remoting>
    </configuration>
    	

    Notice that tcp://localhost:8888/Server matches what was entered under java_server.Factory in Janetor. Change accordingly if necessary.

  2. Load csharp_client.sln located in $USER_INSTALL_DIR$\examples\callcontext\csharp_client and view the Client class. In the contructor call to Factory, the Java code adds the current object instance as a listener (see Factory.java). Next, a call context is created by inserting the marshal-by-value Credentials object into a free slot:

    System.Runtime.Remoting.Messaging.CallContext.SetData("JanetCredentials", cred);
    	

    Again, note that JanetCredentials is a hard-coded constant in the J-Integra® for .NET runtime and it must be used to set the call context data. This cannot be changed.

    During postInvocation, the credentials are changed and placed back into the call context. After the actual method call is made, the .NET client code gets the call context data again and displays it's (changed) contents.

  3. Press F5 to run the .NET client, or simply run the executable in the bin\Debug directory.

    Note: If you happen to have an older version of Visual Studio .NET, it is still possible to compile the example, but you will not be able to open the solution file. See build.bat in $USER_INSTALL_DIR$\examples\callcontext\csharp_client.
  4. On the .NET platform, a console should come up and you should see the output:

    Username is: janet
    Password is: janet
    After method call, username is now: janet_changed
    After method call, password is now: janet_changed
    	

    Press Enter to exit the .NET client.

    Meanwhile, on the Java platform, before the method is actually invoked, the console should read:

    [preInvocation]  About to call method hello with username janet and password janet
    	

    After the method is invoked, the console should read:

    [postInvocation]  Called method hello with username janet and password janet.
    Changing crendentials to username janet_changed and password janet_changed.
    	

© 2007 Intrinsyc Software International, Inc. All rights reserved. Legal