Time for genuine 'write-once, run-anywhere' Java
Oracle and BEA: a new hope?
One of the big selling points of Java has been its "write once, run anywhere" capabilities. Of course, in practice, this has always been "write once, test everywhere" you intend to deploy your chosen application.
With the planned purchase of BEA Systems by Oracle, I got to thinking about what this meant for the "write once, run anywhere" mantra in relation to application servers.
My thoughts were partly spurred by my need to port a large server application to IBM's WebSphere and Oracle's Application Server from BEA's WebLogic. This left me thinking about the practicalities involved in porting Java, and looking for areas where an ironing out of inconsistencies between Java containers would have a beneficial effect.
As I looked deeper at the challenges the companies would face, it became clear that in many cases the problem with Java portability springs from a combination of developer ignorance and specification omissions - or at least limitations.
When it comes to Java across different Virtual Machines, there are at least two gotchas you should be wary of. The first relates to the type of JVM available. For example, we had an application running on one hardware platform using a 64-bit JVM and all was fine. In moving to a new platform, there was only a 32-bit JVM available. On this platform the application suddenly started experiencing out of memory exceptions. In a similar vain, I know of a colleague who had a working system on a 32-bit JVM but when moving to a 64-bit JVM found that it required more memory.
The second gotcha relates to non-Java standard classes. For example, a little while ago I encountered a developer who had directly referenced the sun.security.provider.X509Factory class; this was only found when the code was run on an IBM JVM and this class was, of course, missing. Perhaps the developer should not have used this class - but nothing actually stopped him from doing so.
Next, there are application-server specific issues. I refer, of course, to those parts of a server-side system that are not specified by Java EE but are needed to correctly deploy an application to the application server. These are application-server specific deployment descriptors (such as weblogic.xml or orion.xml). Each of these files has its own format, which may vary widely (for example WebSphere uses an .XMI format). However, it goes further than that, as in some cases defaults can be assumed and on other application servers no default is provided - or an alternative default is used.
Wow, where to begin?
Oooh, a topic for me. To add to your list:
1. Converting from byte to String without specifying the encoding. Good trap for young players this - 7-bit bytes work fine until... until... you run on an OS/390 box running EBCDIC.
2. java.nio. An absolute dogs breakfast this. Garbage collection is different on Windows or UNIX (to be fair this is due to differences in the underlying file system model, but Java should be abstracting this). If you're copying files to an SMB mounted drive on windows using nio, the maximum block size is 62MB. The maximum number of open files on Sun/Windows 1.4 is much smaller than it should be. The list goes on.
3. Swing. Yes, this should be obvious, but it's not just a case of different layouts, it's different functionality too. I give you JComboBox.setPrototypeDisplayValue, which does not work as advertised on Windows. And don't even start me on the GTK+ widgets. Any moderately complex, well designed UI really has to include different code for different LayoutManagers.
4. Memory requirements. Back in the bad old days on JDK 1.4 we found simply switching JVM from Sun to IBM on Linux raised memory requirements by 50%. Again, less of an issue now but still crops up now and again.
5. JVM bugs. Goes without saying I suppose. The IBM 1.3 JVM was big trouble, but even though it's much better now we still hit a few. We have IBM, Sun, JRockit and Apple JVMs here and find ourselves needing to test on all of them more than I'd like.
6. Security. Different JCE Providers supply different algorithms, different root certificates and have differing support for X.509 Certificate extensions. This is a definite issue for us, although things are much better than they were with Java 1.4.
7. Locales. A variation on point 2, but the number of questions we've had when a client has moved from staging to production and found all their ISO-8859-1 characters are now mapping to some Windows codepage are now legion.
That's off the top of my head, that's just J2SE, and that's ignoring the obvious no-no's like calling the com.sun packages directly.
Obviously things are a lot better than coding in C, but I can guarantee that if you're developing for multiple customer environments, any form of I/O to disk, screen or network is a potential problem.
"if we just take the CLR functions, .NET is vastly superior to Java and better organised"
And you clearly don't know what you're talking about. CLR is the runtime environment (bytecode compiler / interpreter), the equivalent of the JVM. The bytecode instructions for both are remarkably similar, which isn't surprising since the CLR evolved from J++ (the deliberately incompatible implementation of Java that got MS into legal hot water).
I think you meant that the .Net class library is "vastly superior" to the Java one. While there is some cruft in the Java class library (just how long are come things going to be deprecated before they're removed?) the differences are minor. The only area where differences are considerable is in the GUI components, and it took a long while for MS to even provide a decent layout manager.
When it comes down to it, the differences between C# and Java (the languages themselves) aren't enough to say one is better than the other. Likewise for the class libraries, although much more third party software exists for Java than C#/.Net. It's really whether you want to be tied to a platform like Windows, with it's indifferent performance, limited scalability, expensive licensing and legendary security issues.
It really boils down to the implementation
Take these sentences: "For example, we had an application running on one hardware platform using a 64-bit JVM and all was fine. In moving to a new platform, there was only a 32-bit JVM available. On this platform the application suddenly started experiencing out of memory exceptions." What was he doing? Some high performance application that soaked up every iota of memory on the system? In general, the java apps I've worked with do not do this when going from platform to platform, so it's something in the code.
Odds are all too good that it is another app where the developers "assumed" they had unlimited amounts of memory available, and good memory management practices went out the window. Or, worse, they just didn't give any thought as to the memory footprint. No wonder most java apps are slow, clunky memory hogs. Just because the platform reclaims unused memory doesn't mean you don't need to manage it. I can write a perl script for ETL that can use everything our mainframes have and then some, or I can write them to manage what I use. It's up to me and my program, not the language.
I speak from the receiving end of having to push these apps out and manage them -- not all apps are equal, and not all coding memes fix things. Remember the coders that thought their memory problems in wfw 3.11 would go away with win95? It was BS then, and it is BS now. Well written, .NET and java apps are useful and portable. So are perl scripts. But when good practices go by the wayside, portability is lost.
It's not to say that they are poor programmers -- just that apps written that don't follow portability guidelines and best practices won't port all that good, and who should be surprised? As to waving the magic .NET wand at it -- wow. Portability within the Windows platform? <sarcasm>Mighty portable, mighty portable.</sarcasm>