asp:Feature
Calling Java Code from ASP.NET Web Applications
Why Interoperate between ASP.NET and Java?
By Wayne Citrin
Why would you want to call Java code from your ASP.NET Web
applications? The best way to answer that question is by examining a few real-world
case studies:
- A major Web search portal needed to construct an
internal Web site for their sales staff. The vendor-supplied data feed they
needed to use on this internal site was only accessible through a Java library
supplied by the vendor. The portal company had to use the Java library, but for
other reasons they needed to implement on ASP.NET for the internal Web site
platform. They had no choice but to solve the Java/.NET interoperability
problem.
- In another case, a large US savings and loan bank
was building a Web application to display digital images of checks that the
bank had processed. (In the US, this is known as a Check 21 application,
named after the law that allows use of digital images instead of actual paper
checks.) The application would be used both internally and by the public. The
project s architects had settled on ASP.NET as an implementation platform
because of the superior tooling, but the bank s back-end applications were all
J2EE-based, and ran on WebSphere application servers. The Check 21 application
would need to directly access the data through the back-end systems Enterprise
Java Beans (EJBs).
There are many other scenarios that could require ASP.NET
code to call Java. Your company might have invested in a Java library or
application and need to use it with new .NET development. A merger or
acquisition might require .NET and Java applications from the merged companies
to work together. Or you just might want to get existing .NET and Java systems
integrated to accomplish a business goal. In all these cases, you need to solve
the problem of Java/.NET interoperability.
Approaches to .NET/Java Interoperability
There are two main ways to call Java code from .NET: Web
services and bridging. The approaches are applicable whether the .NET
application is an ASP.NET Web application, a Windows Forms or WPF-based rich
desktop application, a console-based application, or a service.
Much has been written about Web services, so this article
won t go deep into the details. While there are other definitions, we ll define
Web services as remote procedure calls that are encoded using SOAP, transmitted
using HTTP, and described to the outside world using WSDL. Web services are
implemented according to established standards, so that any set of platforms
that implement those standards can interoperate. However, standards-based Web
services lead to a least common denominator approach, so that only the
relatively small number of data types and other concepts supported by all the
platforms can be used in the communication; many concepts must be left out. In
addition, the service-oriented approach provided by a Web service typically
allows access to a very narrow API the service. While it may be possible to
access an entire object-oriented API through a Web service, it is generally
quite difficult to set up a Web service that offers such a broad API.
The alternative to Web services is a Java/.NET bridge.
With a bridge, the .NET code still runs in a CLR (Microsoft .NET Common
Language Runtime), and the Java code still runs in a JVM (Java Virtual
Machine). Bridges generally take a proxy-based approach: you can create
.NET-based proxies for the Java classes that will be accessed from .NET, or
vice versa. The .NET code interacts with the proxy classes just as if they were
the underlying Java classes. The proxies, along with the bridge infrastructure,
manage the communications between the two sides, including object marshalling
and unmarshalling, and object lifecycle management. Depending on the bridge,
the Java and .NET sides can run in different processes and communicate over a
socket-based connection, or can run in the same process. There are a number of
different Java/.NET bridges available; the example in this article uses
JNBridgePro, a .NET/Java bridge from JNBridge (http://www.jnbridge.com), which supports
both socket-based communication and in-process interoperability using shared
memory buffers for communication. Figure 1 shows the architecture of JNBridgePro.
Figure 1: Architecture of a
.NET/Java bridge.
The choice of whether to use Web services or a bridge to
implement a .NET/Java integration depends on the requirements of the
application. Web services are standards-based and when they re available they
will usually just work. However, their performance is limited, and they are not
well-suited to calling a rich object-oriented API or passing custom classes or
exceptions or implementing callbacks, all of which is easily done with a
bridge. It also is the case that not every application is Web services-enabled
on the .NET and Java sides. Finally, for architectures where the Java code is
in a simple library located on the same machine as the ASP.NET server, use of a
Web service, which involves multiple processes and a socket-based connection,
is overkill; running the Java in the ASP.NET process, as is available in
several bridges, is a more appropriate solution.
Calling Java Code from an ASP.NET Web Application
The following example shows how to use a bridge to call a
Java library from an ASP.NET Web application. The ASP.NET code supplied below
is C#; the downloadable code also includes the equivalent VB.NET code (see end
of article for download details).
To run this example, it is necessary to have Visual Studio
2008, a Java Runtime Environment (JRE) 1.4 or later, and a copy of JNBridgePro.
Evaluation copies of JNBridgePro can be downloaded from http://www.jnbridge.com.
Let s assume we have an existing Java library that
retrieves names of authors and titles of books from a database. The library
exposes an API including two classes: Author, encapsulating information about
authors, and Book, encapsulating information about books. A simplified version
of the code is as follows:
public class Author
{
public String firstName;
public String lastName;
public Author(String
firstName, String lastName)
{
... // construct a new
Author object
}
public static Author[]
getAuthors()
{
... // return an array
containing all the available Author objects in the database
}
}
public class Book
{
public String title;
public String publisher;
public String year;
public Book(String title,
String publisher, String year)
{
... // construct a new Book object
}
public static Book[]
getBooks()
{
... // return an array containing all the
available Book objects in the database
}
public static Book[]
getBooks(String firstName, String lastName)
{
... // return an array containing all books in
the database by the given author
}
}
Full code for the Java library is available with the
downloadable code.
We are going to provide an ASP.NET Web-based front-end
that will display all authors in the database, and then, when the user clicks
on the name of an author, will retrieve and display all books written by a
specified author.
The first step in the process is to generate proxies for
the Author and Book classes. Start by launching JNBProxy, JNBridgePro s
GUI-based proxy generator. When the Launch JNBProxy form is displayed, select
Create new .NET --> Java project, as we are creating a project in which
.NET code will be calling Java code. Once this is done, JNBProxy s main form is
displayed (see Figure 2).
Figure 2: JNBProxy.
We will need to proxy the Java classes Demo2.Author and
Demo2.Book, so we will need to configure the Java classpath used by the proxy
generator so that it can locate the desired classes. The classes Demo2.Author
and Demo2.Book are contained in the files Author.class and Book.class,
respectively, and they must both be located in a folder Demo2. We must add the
folder containing Demo2 to the classpath. To do so, we use the menu command
Project | Edit Classpath... and locate the necessary folder. When we are done,
we will see an Edit Class Path dialog box similar to that shown in Figure 3.
Figure 3: After creating classpath.
The next step is to load the Author and Book Java classes,
then generate proxies for those classes. To load the classes, use the menu
command Project | Add Classes from Classpath... and enter the fully qualified
class names Demo2.Author and Demo2.Book. It is not necessary to add supporting
classes, as the only supporting classes needed to use Author and Book are
arrays and strings, which are automatically converted to native .NET arrays and
strings.
Loading the classes may take a few seconds. Progress will
be shown in the output pane in the bottom of the window, and in the progress
bar. When completed, Demo2.Author and Demo2.Book will be displayed in the
Environment pane on the upper left of the JNBProxy window (see Figure 4).
Figure 4: After loading classes.
We wish to generate proxies for both of these classes, so
when all the classes have been loaded into the environment, make sure that each
class in the tree view has a checkmark next to it. Quick ways to do this
include clicking on the checkbox next to each package name, or simply by
selecting the menu command Edit | Check All in Environment. Once each class has
been checked, click the Add button to add each checked class to the list of
proxies to be exposed. These will be shown in the Exposed Proxies pane.
Finally, generate the proxies. Select the Project | Build...
menu command, and choose a name and location for the assembly (.dll) file that
will contain the generated proxies. The proxy generation process may take a few
minutes, and progress and other information will be indicated in the Output
pane. In this example, we will call the generated proxy assembly
bookAccess.dll.
Now that the proxies are generated, we will write the
ASP.NET Web application that uses them and accesses the Java classes through
them.
First, create a new C# Web Application project. (Make sure
that you have Internet Information Server installed and properly configured
before you do this. Also note that the downloadable code contains a VB.NET
version of this example, so you will be able to do this same example with
Visual Basic.) Open the web.config file in the project. Add the following code
to the <configSections> section of Web.config:
<sectionGroup name="jnbridge">
<section
name="dotNetToJavaConfig"
type="System.Configuration.SingleTagSectionHandler, System,
Version=2.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
</sectionGroup>
Immediately after the <configSections> section, add the
following section:
<jnbridge>
<dotNetToJavaConfig
scheme="jtcp"
host="localhost"
port="8085" />
</jnbridge>
These changes configure the .NET side to communicate with
the Java side using a socket-based approach. The Java side in this case will
run in its own process. It also is possible to configure the project so that
the Java side runs inside the ASP.NET process and sockets are not needed. This
approach will be demonstrated at the end of the section.
Once the configuration file is edited, use the designer to
create a simple Web form as shown in Figure 5. The two boxes in the Web form
are ListBoxes, not ListViews, which are not available in Web forms. Make sure
that the AutoPostBack property of the top ListBox is set to true.
Figure 5: Web form for example
program.
Assuming that the top ListBox is named ListBox1, and the
bottom ListBox is named ListBox2, add the following event handlers to the Web form s
code-behind:
using Demo2;
. . .
private void ListBox1_Load(object sender, System.EventArgs e)
{
if (ListBox1.Items.Count >
0)
{
return; // don't add anything more
}
// load the authors
Author[] authors =
Author.getAuthors();
for (int i = 0; i <
authors.Length; i++)
{
ListItem li = new
ListItem();
li.Text =
authors[i].lastName + ", " + authors[i].firstName;
ListBox1.Items.Add(li);
}
}
private void ListBox1_SelectedIndexChanged(object sender,
System.EventArgs e)
{
// clear listBox2
ListBox2.Items.Clear();
// if nothing selected,
return
ListItem li =
ListBox1.SelectedItem;
if (li == null)
{
return;
}
// last, first names
string name = li.Text;
int separator =
name.IndexOf(",");
string lastName =
name.Substring(0, separator);
string firstName =
name.Substring(separator+2);
// look up books
Book[] titles =
Book.getBooks(firstName, lastName);
// write out books
for (int i = 0; i <
titles.Length; i++)
{
ListItem li2 = new
ListItem();
string theBook = titles[i].title
+ " (" + titles[i].publisher
+ ", " +
titles[i].year +
")";
li2.Text = theBook;
ListBox2.Items.Add(li2);
}
}
Again, note that the proxies for the Java objects of class
Demo2.Author and Demo2.Book are used exactly as the original objects would be
used in Java. This is where the interop occurs. Every call to a method in
Author and Books, and every access of a field, is actually an access of the
underlying method or field. If the application is set up properly, the cross-platform
interoperation is transparent.
After entering the code, build the project to obtain the
Web application.
Running the program is simple. First, make sure there is
an ODBC data source called BookDemo referencing the supplied Microsoft Access
database books.mdb. Set up your Java environment by copying the files
jnbcore.jar (the JNBridgePro Java-side runtime component) and
jnbcore_tcp.properties (the Java-side configuration file specifying binary,
socket-based communications) to the folder containing the folder Demo2 (which
contains the Java class files). Then, open a console window, navigate to the
folder containing Demo2, jnbcore.jar, and jnbcore_tcp.properties, and run the
following command:
java -cp ".;jnbcore.jar" com.jnbridge.jnbcore.JNBMain
/props jnbcore_tcp.properties
You should see the following output:
JNBCore v4.0.2
Copyright 2008, JNBridge, LLC
creating binary server
This indicates that the Java side is running.
Once the Java side is running, start the Web application
by launching a browser and entering the URL of the Web application (for
example, http://localhost/Demo2/WebForm1.aspx). The Web form will be displayed
with author names, obtained through the Java class Author, displayed in the top
list box (see Figure 6).
Figure 6: Initial Web form.
Clicking on any of the authors will cause books written by
that author to be displayed (see Figure 7).
Figure 7: Web form after clicking on
author.
It is possible to run the Java side in the same process as
the .NET side, using a shared-memory communication mechanism. This has several
advantages: it s much faster than the socket-based mechanism, and it s not
necessary to explicitly start up the Java side it s automatically done before
the first call to a proxy. To use shared memory, stop Java side (if it s still
running), then open the web.config application configuration file. Replace the <dotNetToJavaConfig>
element with the following:
<dotNetToJavaConfig scheme="sharedmem"
jvm="C:\Program
Files\Java\jdk1.5.0_09\jre\bin\client\jvm.dll"
jnbcore="C:/Program
Files/JNBridge/JNBridgePro v4.0/jnbcore/jnbcore.jar"
bcel="C:/Program
Files/JNBridge/JNBridgePro v4.0/jnbcore/bcel-5.1-jnbridge.jar"
classpath="C:\BooksDemo"
/>
You will need to edit the jvm , jnbcore , bcel , and classpath
values to reflect the locations on your machine of jvm.dll (in your Java
runtime environment), jnbcore.jar, bcel-5.1-jnbridge.jar (in your JNBridgePro
installation), and the folder containing the folder Demo2 containing the files
Author.class and Book.class. Once you have made the changes, build the project.
Restart ASP.NET by issuing the command iisreset from a command-line prompt,
then start the Web application. It will run as before, even though the Java
side has not been explicitly started, because the Java side is now running
inside the .NET process.
Conclusion
This example shows how Java classes can easily be used in
an ASP.NET application through use of a Java/.NET bridge. The bridge allows the
Java classes to be called directly from the ASP.NET code through proxies; to
interact with a Java object, access the corresponding proxy object in exactly
the same way. Interactions between the .NET and Java code are transparent, and
use of proxies make the Java classes look as though they were written in a .NET
language like C# or VB.NET.
Returning to the case studies introduced at the start of
the article, the Web search portal company decided to use a bridge to directly
access the data feed through their vendor s Java API. Constructing a Web
service around the API and accessing that seemed like overkill for the problem
at hand. A bridge was more appropriate for direct access of the Java API.
For the savings and loan bank s Check 21 application, the
architects did consider a Web service, as the back-end WebSphere implementation
did offer the option of Web services. However, they discovered that the Web
service offered insufficient throughput, so they also settled on use of a
bridge. The new application was up and running within a few weeks, certainly much
more quickly than if they had decided to re-implement it with Java
technologies, which was another option.
There are situations where a Web service would be a good
solution for calling Java from .NET. If performance isn t the most significant
issue, or if the connection must pass through firewalls or reach some other
organization over the Internet, Web services are a reasonable interoperability
option.
If you are developing ASP.NET applications, ideally you
would like to base the project exclusively on the .NET platform. However, there
are practical reasons why a pure .NET implementation might not be an option.
Knowledge of the various approaches to Java/.NET interoperability, particularly
Web services and bridging, will often allow you to complete projects more
quickly, and without the need to spend time rewriting legacy Java code.
Source code
accompanying this article is available for download.
Wayne Citrin is the
Chief Technology Officer at JNBridge, the leading provider of Java and .NET
interoperability tools (http://www.jnbridge.com).
Citrin co-founded the company in 2001 and has served as the chief architect of
its flagship product, JNBridgePro. He has been engrossed in Java and .NET
interop issues since .NET s earliest days and is an expert on interoperability
issues regarding connecting the two development platforms. Previously, he was a
leading researcher in programming languages and compilers at the University of
Colorado, Boulder. Prior to this position, he was a researcher at IBM s lab in
Z rich, Switzerland. Citrin holds a Ph.D. from the University of California,
Berkeley, in Computer Science. He has presented at several leading conferences
including, JavaOne, Microsoft TechEd, and TechReady. In addition, his work and
research has been published in numerous academic and trade journals.