Java Development News:

Part 3 - Write an application in Java and deploy it on .Net

By Richard Pawson, Robert Matthews and Dan Haywood

01 Feb 2004 | TheServerSide.com

Part 1: The Case for Naked Objects: Getting Back to the Object-Oriented Ideal
Part 2: Challenging the Dominant Design of the 4-Layer Architecture
Part 3: Write an application in Java and deploy it on .Net
Part 4: Modeling simultaneously in UML, Java, and User Perspectives
Part 5: Fat is the new Thin: Building Rich Internet Applications with Naked Objects

This being TheServerSide.com, using the name of ‘the other development platform’ is probably about as welcome as using the name of ‘the Scottish play’ in a theatre! No, this isn’t a sell-out. Naked Objects is a Java framework, written by Java enthusiasts, and intended for use by Java developers. But we’ve discovered that Naked Objects will allow you to write a business application in Java and then deploy it, without modification, on either the Java or the .Net platforms. This ought to be seen as very good news for the Java community - it potentially positions Java as the de facto standard for specifying business functionality.

What makes this capability all the more remarkable is that it is almost accidental: cross-platform development was never one of our goals. (The principal intended benefits for Naked Objects were laid out in the first article).

A stroke of luck

When Robert started to write the Naked Objects framework in mid-1999 (based on Richard’s original research) he decided to implement it in Java 1.1. At that time, Java 1.2 was already available but 1.1 offered greater stability, plus the possibility of running on the open-source JVMs that were starting to appear. Though Java has since moved through 1.3, 1.4, and 1.5, Robert stuck with the capabilities of 1.1. (The default user interface for Naked Objects uses only the AWT components, not Swing, which was introduced in 1.2).

Many people have queried this policy over the years, but Robert says simply that he saw no advantage in using the capabilities of the later versions for what he was trying to achieve. And though the framework itself was written in 1.1, application developers were free to write their business objects on top of the framework in whichever version of Java they preferred.

Over the last couple of years we have fielded many requests for a .Net implementation of the framework. But there was still so much more that we wanted to do with the Java version that we were reluctant to divert resources onto a .Net version. We were also anxious to avoid having two diverging versions of the ramework.

It was only when Dan got involved in the framework’s development that he spotted something that Richard and Robert had overlooked: J#, Microsoft's port of Java to the .Net platform, was (at least in theory) equivalent to Java 1.1. That is, J# uses the same language syntax as Java 1.1 (and 1.2 and 1.3 actually, since no new keywords were added in those releases). Moreover, it provides a .Net implementation of the Java platform as it existed in JDK 1.1.4.

Dan set about testing Microsoft's claim that J# and Java 1.1 were equivalent. The first step was to port two other frameworks that Naked Objects draws upon: Apache's log4j logging API, and an XML parser (either Xerces or Crimson, again both hosted by Apache). Porting log4j 1.2.7 and Crimson 1.1.3 to .Net took a day or so, a bit of refactoring, and some judicious pruning of un-needed features, but the result did seem to be Java libraries ported over to .Net.

Encouraged, Dan went to work on the Naked Objects framework itself. A further day, and he had compiled the complete framework into a .Net DLL. It turned out that there were a few minor things that needed fixing, where Robert had unwittingly used constructs from later Java versions, but these have all subsequently been written back into pure Java 1.1. The only part of Naked Objects that Dan didn’t initially port was the in-built testing framework, which relies on JUnit and MockObjects. (The testing framework will be the subject of a future article.)

To test out the ported DLLs, Dan also ported an existing Naked Objects application - the Executive Car Services (ECS) demo issued with the framework and described in the Naked Objects book.

Within a short while he had successfully ported a complex framework and a simple application from the Java world to the .Net world, and it was almost disappointingly straightforward. Microsoft's claim that J#.Net was compatible with Java 1.1. suddenly began to look like the most overlooked capability of contemporary software development.

Crossing the divide

What does it mean to say that Naked Objects is available under .Net? First, it means that with the .Net version you can now write your business objects in VB.Net or C#. You must observe the conventions of the Naked Objects framework, but these are few in number. For example, prefixing any method name with ‘action’ will ensure that the method is automatically made available to the user on the pop-up menu for that object. Similarly, naked values, which are represented as fields in the Naked Objects GUI, are coded as methods prefixed with get. In fact, since both C# and VB.Net have language syntax support for properties, you can use this idiom instead of using getter methods.

But here’s the kicker: if, instead, you write your business objects in Java 1.1, then they also will compile for either the Java or .Net platforms.

The listings at the end of this article show extracts of the Booking class taken from ECS demo. Listing 1 shows the original Java version (accessible under examples/ecs in the .Net download), Listing 2 shows the C# version (under examples/ecscsharp) and finally Listing 3 shows the VB.Net version (under examples/ecsvbnet).

When they are running, the .Net and JVM versions of an application are virtually identical (except for the tiny logo in the top left hand corner of the Window) The image below shows a screenshot of the ECS demo application, written in Java 1.1 and running – take it from us – under .Net.

There's clearly some very clever stuff going on under the covers. The Naked Objects framework uses Java's reflection APIs to infer information about the business objects (each of which must inherit from the framework’s AbstractNakedObject class or, alternatively, implement the NakedObject interface). Since these APIs were part of the JDK 1.1.4, Microsoft has also ported these to .Net, so that for example a java.lang.Class correspondsto a System.Type, and a java.lang.Throwable corresponds to System.Exception. When the framework reflects on a business object, the .Net runtime returns the .Net metadata. The code emited by the vjc compiler transparently maps this to java.lang.Class and the rest of the Java Reflection API.

This remarkable support of .Net for Java 1.1 syntax means that, at least in a Naked Objects context, it really matters little whether you choose to develop your business objects in Java, or in J#. What matters instead is the API that you use. If you write in J#, but implement your business logic using .Net platform types (e.g. System.DayOfWeek which is an enumeration with no Java 1.1 equivalent), then your application will run only on .Net. Similarly, if you write in Java but using features from JDK 1.2 or above, then again your application will run only on a Java virtual machine. But if you write in Java 1.1 (or J#) and use only the APIs of the Naked Objects framework then your application will run unmodified on either the JVM or .Net platforms. And if you really need to port to VB.Net or C#, there are tools to do the job. Microsoft provide their Java Conversion Assistant to get from Java to C#, and there are freely available utilities to go from C# to VB.Net if you so desire.

At the moment, the compilation process for the .Net DLLs (crimson, log4j and nakedobjects) is performed using Microsoft's Visual Studio.Net. In keeping with our open source philosophy, the intent is to move the compilation to run under Ant, using the vjc (Visual J#) compiler that Microsoft makes freely available as part of the .Net SDK. But for now, a copy of VS.Net is needed to create the .Net DLLs. In reality, most developers or organizations interested in the .Net version will have VS.Net anyway.

The Naked Objects framework is under active development; there is a new development release every month at least. At every release, we plan to make available the source code, a JAR file and the three DLLs. We will also release an example Naked Objects application as both an executable JAR file and as 3 different MSI installations (J#, C# and VB.Net). See the Naked Objects downloads page: http://www.nakedobjects.org/downloads.html

Note: Since the last article we have made a major new stable release of the framework (version 1.2). This release incorporates the new user interface, whereby all object views are incorporated within a single application window.

Listings

These are the code listings referenced in the article text.

Listing 1: part of Booking.java, one of the business objects in the ECS demo application

 package ecs.delivery; ... public class Booking extends AbstractNakedObject { // Values private final TextString reference; private final TextString status; ... // Associations private Customer customer; private Location pickUp; private Location dropOff; ... public Booking() { reference = new TextString(); reference.setAbout(FieldAbout.READ_ONLY); status = new TextString(); status.setAbout(FieldAbout.READ_ONLY); ... } // Title public Title title() { return reference.title().append(status); } ... // Fields (values) public final TextString getReference() { return reference; } ... // Associations public void associateCustomer(Customer customer) { customer.associateBookings(this); } public void dissociateCustomer(Customer customer) { customer.dissociateBookings(this); } public Customer getCustomer() { resolve(customer); return customer; } public void setCustomer(Customer newCustomer) { customer = newCustomer; objectChanged(); } ... // Actions public Booking actionCopyBooking() { Booking copiedBooking = (Booking) createInstance(Booking.class); copiedBooking.associateCustomer(getCustomer()); ... return copiedBooking; } public About aboutActionCopyBooking() { int sets = 0; sets += ((getCustomer() != null) ? 1 : 0); ... return ActionAbout.enable(sets >= 3); } ... }

Listing 2: part of Booking.cs, the C# implementation of the ECS demo application

 namespace ecs.delivery { ... public class Booking:AbstractNakedObject { // Values private TextString reference; private TextString status; ... // Associations private Customer customer; private Location pickUp; private Location dropOff; ... public Booking() { reference = new TextString(); reference.setAbout(FieldAbout.READ_ONLY); status = new TextString(); status.setAbout(FieldAbout.READ_ONLY); ... } // Title public override Title title() { return reference.title().append(status); } ... // Fields virtual public TextString Reference { get { return reference; } } ... // Associations public virtual void associateCustomer(Customer customer) { customer.associateBookings(this); } public virtual void dissociateCustomer(Customer customer) { customer.dissociateBookings(this); } virtual public Customer Customer { get { resolve(customer); return customer; } set { customer = value; objectChanged(); } } ... // Actions public virtual Booking actionCopyBooking() { Booking copiedBooking = (Booking) createInstance("ecs.delivery.Booking"); copiedBooking.associateCustomer(Customer); ... return copiedBooking; } public virtual About aboutActionCopyBooking() { int sets = 0; sets += ((Customer != null)?1:0); ... return ActionAbout.enable(sets >= 3); } ... } }

Listing 3: part of Booking.vb, the VB.Net implementation of the ECS demo application.

 Namespace ecs.delivery ... Public Class Booking Inherits AbstractNakedObject ' Values Private _reference As TextString Private _status As TextString ... ' Associations Private _customer As Customer Private _pickUp As Location Private _dropOff As Location ... Public Sub New() _reference = New TextString _reference.setAbout(FieldAbout.READ_ONLY) _status = New TextString _status.setAbout(FieldAbout.READ_ONLY) ... End Sub ' Title Public Overrides Function title() As Title Return Reference.title().append(Status) End Function ... ' Values (fields) Public Overridable ReadOnly Property Reference() As TextString Get Return _reference End Get End Property ... ' Association Public Overridable Sub associateCustomer(ByVal customer As Customer) customer.associateBookings(Me) End Sub Public Overridable Sub dissociateCustomer(ByVal customer As Customer) customer.dissociateBookings(Me) End Sub Public Overridable Property Customer() As Customer Get resolve(_customer) Return _customer End Get Set(ByVal Value As Customer) _customer = Value objectChanged() End Set End Property ... ' Action Public Overridable Function actionCopy() As Booking Dim copiedBooking As Booking = CType(createInstance("ecs.delivery.Booking"), Booking) copiedBooking.associateCustomer(Customer) ... Return copiedBooking End Function Public Overridable Function aboutActionCopy() As org.nakedobjects.object.control.About Dim sets As Integer = 0 sets += (IIf(Customer Is Nothing, 1, 0)) ... Return ActionAbout.enable(sets >= 3) End Function ... End Class End Namespace