Lesson 1: Project Setup
The topic of this lesson is how to set up your .NET project to use Java types. As described in the Technology overview, your application code usually deals with .NET proxy types, but the .NET proxy types internally rely on both the managed .NET and the unmanaged C++ runtime library. If you have not read that overview yet, now would be a good time to do it.
We are intentionally using just the commandline C# compiler in this lesson to keep the "magic" that VisualStudio usually performs for you to a minimum. You would probably not do a commandline build but rely on a VisualStudio project and MSBuild. Please take this lesson as an educational exercise on the kinds of decisions you have to make and not as a blueprint for how to set up your own project.
In the Technology overview we glossed over this a little bit, but you do not only deal with .NET proxy types but also directly with the managed .NET runtime library when you configure the Java runtime environment for your .NET application. In fact, you could use just the .NET runtime library without any proxy types to load a JVM into your process.
Therefore, the simplest Java/.NET integration project does not involve any proxy types at all. It only uses some of the framework APIs from the runtime library to load a JVM. Such a program might look like this:
using System; using System.IO; // this makes available all the framework types that you // might normally have to deal with using Codemesh.JuggerNET; namespace Codemesh { namespace Examples { public class LoadJvmTest { public static void Main( string[] args ) { // acquire the loader singleton on which we issue all the configuration API calls IJvmLoader loader = JvmLoader.GetJvmLoader(); try { // feel free to comment out this line to get less clutter loader.SetTraceLevel( TraceFacility.TraceJvm, TraceLevel.TraceInfo ); Console.Error.WriteLine( "INFO: About to load JVM..."); // have the loader attempt to load a JVM. // this could fail for example if no JVM is installed or if the loader // cannot locate an installed JVM // The JVM instance is owned by the loader and does not need to be deleted. IJvm jvm = loader.Load(); Console.Error.WriteLine( "INFO: Successfully loaded JVM!"); } catch( Exception e ) { Console.Error.WriteLine( "ERROR: {0}", e.ToString()); } Console.Error.WriteLine( "INFO: Done with explicit example!"); } } } }
This program uses the JMS Courier managed runtime library's API to acquire a JVM loader
instance and then attempts to use that instance to load the default Java Virtual Machine. In a real integration project,
you would follow the JVM loading by using some .NET proxy types before returning from Main()
.
Once the JVM has been loaded into the process, it is managed internally and remains active until the process
terminates, unless you manually unload it. Please note that most implementations do not support the repeated
loading and unloading of JVMs within one process, so you should pretty much assume that your JVM will be around till your
process exits. While there is an Unload()
function, you should probably never use it and instead
rely on the framework to manage the JVM's lifecycle.
All JMS Courier runtime library type names are located in the Codemesh.JuggerNET
namespace and are packaged into an assembly named netrt
.
Build
Were you to try to compile this code with the C# compiler, for example by typing:
csc -target:exe -main:Codemesh.Examples.LoadJvmTest LoadJvmTest.cs
you would receive several compilation errors related to missing Codemesh.JuggerNET
types. Clearly you need to
tell the compiler where to find these types. Thus you add JMS Courier's managed runtime library to the
compiler invocation.
csc -references:netrt -target:exe ...
The problem now is that the compiler does not know where to find the netrt.dll
assembly that
contains all of Codemesh's framework types. Exploring your JMS Courier distribution
leads you to the dotnet/v3
directory, which contains several different versions of the
assembly. It is now time to make an important decision, though usually that decision will already have been made
for you at a much earlier point.
You need to decide for which platform you want to build your application. Wait, isn't .NET platform-portable? Yes, .NET is platform-portable, and so is Java, but the portability is implemented at the bytecode level, not at the binary level. A 32-bit Java version uses DLLs that have been built exclusively for 32-bit mode. A 64-bit Java version uses DLLs that have been built exclusively for 64-bit mode. You cannot mix and match DLLs that are built for different platforms within one process. Remember, to achieve the highest performance and the tightest possible integration, JMS Courier loads the JVM into the .NET process. But you can never load a 32-bit JVM into a 64-bit process or vice versa. This is a limitation enforced by the operating system.
So you need to decide whether you want to run with a 32-bit version of Java or with a 64-bit version of Java
and build your .NET application accordingly. Alternatively, you could build your .NET application to be platform neutral
and rely on your application's configuration to ensure that you never try to load the wrong kind of JVM (for example
by using platform APIs to figure out whether you're running as a 32-bit or 64-bit process and then configuring one or another
of two bundled JVMs). Either way, you have to decide which path you're going to take and pick the proper
version of As most of you will be working on modern 64-bit Windows computers we will explicitly build for 64-bit Windows
and pick the matching runtime assembly:
Your application should now build but it won't run yet. You need to make the Now it will run but it will crash. The reason is very clear if you look at the diagram illustrating the
anatomy of a function call on the Technology page.
Once downloaded, you have access to a range of different runtime libraries and hopefully one of them
matches your runtime environment. If not, you can always talk to us and we can build a runtime library for you.
Irrespective of platform, the runtime library is always called The libraries can be located in subfolders of the The screenshot to the right shows a typical directory hierarchy of runtime library directories.
As you can see, the first level after The second level is a processor architecture identifier. The processor architecture is used to identify both
the family of processors and the bittedness. 64-bit versions of a processor architecture are represented by their
own code. For example, the 32-bit version of Intel X86 compatible processors is represented by the family code
The third level is a combined compiler type and compiler version identifier. As mentioned above, you don't
necessarily have to match the type and version exactly, but you should try to use the best match that you can find.
Finally, there are debug and release versions of the runtime library. The fourth level distinguishes between
these two flavors.
Simply pick the most modern release version that matches the platform of the JVM you intend to use and copy
it into the diretory containing your executable.
netrt.dll
. If you are going to target 64-bit deployments, you should pick the dotnet/v3/bin/x64
version. If you target 32-bit deployments, you should pick the dotnet/v3/bin/x86
version. If you are going to manage
platform compatibility internally and remain portable, pick the base csc -platform:x64 -lib:<install>/dotnet/v3/bin/x64 -references:netrt ...
netrt.dll
that you
referenced at build time available at runtime. You can simply copy it into the directory containing your built
executable.
netrt.dll
delegates to the unmanaged runtime library xmogrt.dll
to perform the actual
integration with Java. You have not yet deployed a proper version of xmogrt.dll
.
Where to Find the Right Runtime Library
xmogrt.dll
.
cpp/v3/lib
directory.
cpp/v3/lib
is an operating system identifier.
x86
whereas the 64-bit version is represented by the family code amd64
. We use commonly
known identifiers, so you should not have any trouble locating the proper directory.