Blog

Java 7 update “silently” deletes Java 6, breaks applications

Software updates shouldn’t do unexpected things. They particularly shouldn’t remove software other than what they’re ostensibly updating, and they shouldn’t break running applications. It’s even worse when this all happens automatically and without warning.

The other day, one of our customers, an ISV that uses JNBridgePro in one of their applications that includes both Java and .NET, told us that several of their customers had reported that their applications stopped working after the customers updated their installations of Java 7. The strange thing is that the applications didn’t use Java 7; they used Java 6. The problem was fixed by reconfiguring JNBridgePro on those machines to point to Java 7 rather than Java 6. Our customer asked us whether JNBridgePro had problems with this update, or with Java 7. We answered that there should be no problem: JNBridgePro works fine with both Java 6 and Java 7, including the latest updates.

Something odd was going on, so we started digging deeper. Running the auto-installer for the new Java 7 update, we saw the following screen, with the relevant message buried in it:

Then we found the following notice on the Oracle website:

About the Java 6 Auto-Update to Java 7

Oracle will start auto-updating Windows 32-bit, Java Runtime Environment (JRE) users from JRE 6 to JRE 7 in December 2012.

The Java auto-update mechanism is designed to keep Java users up-to-date with the latest security fixes. To achieve this goal Windows users that rely on Java’s auto-update mechanism will have their JRE 6 replaced with JRE 7.

In December 2012 Oracle will start to auto-update a sample of users from JRE 6 to JRE 7 to evaluate the auto-update mechanism, user experience and seamless migration. Oracle will then start auto-updating all Windows 32-bit users from JRE 6 to JRE 7 with the update release of Java, Java SE 7 Update 11 (Java SE 7u11), due in February 2013.

    • JRE 7 has been the default version on Java.com since April 2012 and is now being used by millions of users.
    • As we did when JRE 5 was replaced by JRE 6, we will auto-update users of the older release to the newer version of Java.
    • As always, all users are encouraged to update to the most recent Java versions available for public download.
    • In February 2011 Oracle announced the End of Public Updates for their Java SE 6 products for July 2012. In February 2012 Oracle extended the End of Public Updates for 4 months, to November 2012. See:

• Oracle is now extending the End of Public Updates again for 4 additional months to provide developers and users with additional time to migrate to Java 7. The last publicly available release of Java 6 will be in February of 2013 with the release of Java SE 6 Update 39 (Java SE 6u39).

Java 6 End of Public Updates extended to February 2013

(Emphasis ours.)

This is absolutely astonishing. Oracle has decided that, in order to fix extensively-reported security problems, they will not only update Java 7 (their latest version of Java), they will also completely delete a completely separate product. Yes, Java 6 is a separate product from Java 7. They can be installed side-by-side, and many users have both Java 6 and Java 7 installed on their machines. Some of their applications depend on Java 6, and others might depend on Java 7, and these dependencies are typically hard-coded or configured to point to the correct, and different, file locations. Can you imagine if Microsoft released an update to .NET 4.0 that also removed .NET 2.0? This is just as serious.

Worse, it appears that they are taking it upon themselves to replace installations of Java 6 with Java 7 even if the users have only Java 6 on their machines.

How is this different from, say, Microsoft updating Office by replacing one version by another? That’s an update-in-place, so hard-coded paths will often still work. Even so, updating one version of Office to another likely won’t involve an auto-update, but rather an explicit re-installation, and one would expect dependencies to break. Java 6 and 7, on the other hand, are side-by-side installations, and one doesn’t expect an update to one to affect the other in any way.

Let’s look at this from Oracle’s point of view. The security holes that they plugged in Java 7 likely also exist in Java 6, and they have stopped providing new updates to Java 6. Why not replace Java 6 with Java 7 and fix these problems?

This strategy might make sense for less sophisticated users who only use Java inside their browsers. They likely do not know which version of Java they have, or even if they Java at all. In this case, it makes sense to fix the problem by updating the Java installations, so that machines aren’t infected with malware by visiting rogue websites.

However, most of our customers aren’t using Java in their browsers. Their Java is running on servers, or in self-contained desktop applications that, if they connect to the Internet, only connect to specific sites. Their applications depend on specific versions of Java, or on Java files being in specific places. JNBridgePro’s shared memory mechanism, in particular, depends on an absolute path to a specific jvm.dll, but that’s not the only case where dependencies like this occur. With their update, Oracle has silently pulled the rug out from under many running applications.

Why do I say “silently”? Even though the update installer mentions that Java 6 “might” be removed, and the notice on the website says it “will” be removed, very few people will read the text in the installer; they will likely just click through it, since nobody expects a Java 7 updater to remove Java 6. And almost nobody will read the notice on the website unless they are specifically searching for it.

One could say that IT shops should turn off automatic updates, and apply updates in a controlled process after extensive testing. That’s true, but clearly auto-updates can still happen; it’s not reasonable to assume that all business users have sufficient IT support. After all, it happened to our customer’s customers. It’s also the case that a situation like Java 6 being removed in a Java 7 update might not be found in a controlled test, since most such tests will only try to see whether applications that use Java 7 will be affected. To make matters more difficult, the mechanism for turning off automatic Java updates isn’t obvious.

If you find yourself with a broken application that uses JNBridgePro and Java 6 after updating Java 7, here’s what you can do:

  • You can reconfigure your application (and particularly the JNBridgePro component) to use Java 7 rather than Java 6. JNBridgePro will have absolutely no problem with Java 7. Whether your Java code will work with Java 7 is something that you will need to determine yourself.
  • You can go to the Oracle Java website and download and reinstall Java 6. Then, you’ll be back where you were.

Finally, you should strongly consider turning off automatic Java updates. As I said it isn’t immediately obvious how to do this, since the Java control panel, by default, doesn’t display the Update tab that contains the switch that turns off updating. The Update tab only appears when the control panel is run as administrator. You can turn off the auto-update switch as follows:

  1. In Windows Explorer, navigate to your JRE’s bin folder (for example, C:Program Files (x86)Javajre7bin, although it might be different on your machine).
  2. Once you’re there, find javacpl.exe. Right-click on it, and select “Run as administrator.”
  3. Inside the control panel, you can now see the Update tab. Select it, then uncheck the “Check for Updates Automatically” checkbox.

The control panel will ask if you really want to do this. Trust me, you do. Then click on the OK button.

Note that if you do this, it’s your responsibility to make sure that your Java installations are up to date, and that you engage in good security practices. You will need to keep track of the latest Java security problems and the latest updates when they become available, and you can download them from Oracle’s Java site. The downloaded updaters will only update the specific Java versions, and they won’t pull the rug out from under you by removing completely different versions of Java that your software might depend on.

In summary, Oracle’s latest automatic Java update is dangerous and irresponsible because it “silently” removes software other than the software it ostensibly updates, thereby breaking running code. By all means update and secure the Java running inside browsers, but leave our server and desktop software alone.

Follow-up to the Java update post

We had a great response to our recent post on Oracle’s new Java 7 auto-updates, their silent removal of Java 6, and the problems that can cause. We had coverage in The Register, InfoQ, and DZone. The Register article in particular had a great comment thread, and I urge you to check it out.

At the end of that thread, I wrote a response to several points that were brought up, and I thought I’d post a version of that response here.

Why not just support Java 7 and be done with it? Why make people use Java 6?

Our product does handle Java 7 (and 6, and 5, etc — our stuff works with Java back to 1.3.1, although we’ll probably move that up to Java 5 in the next release) just fine. But it’s a tool that customers use to run and deploy their own software — it allows .NET code to communicate with Java code. The Java runs in its own JVM, and the users get to choose whichever JRE they want — it can be any version, it can be 32-bit or 64-bit. It can be from just about any vendor. That’s a good thing, because our users have their own environments, and it’s their own business — we don’t dictate or judge. So, the problem isn’t ours (we’re not making people use Java 6 — but our customers might choose to use Java 6), except that our customers’ problems become our problems, and then we have to scramble. But it bothers me when we have to scramble to solve a problem that really wasn’t caused by us, and which really shouldn’t have been a problem to begin with.

Why not just get version of the latest installed Java from the registry and use that?

The problem is that that only tells us what Java is on the machine — it doesn’t tell us what Java the user wants or needs. Again, we let the user make that decision — checking the registry won’t tell us what we want to know. (Nor will JAVA_HOME, as someone else suggested.)

Why would an enterprise user allow auto-updates, when unexpected things can clearly happen?

The short answer is that they shouldn’t. But clearly it happens — it happened to the customers of our customer. (Our customer is an ISV that uses our product. Their customers are the end users.) And when it happened, our customer heard about it from their customer, and called us, and we had to scramble, and the problem was easily corrected, but it shouldn’t have been a problem in the first place.

Why not just supply the jvm.dll?

First, because it should be up to our users to determine which version they need — we can handle just about any one chosen and don’t dictate. Second, because jvm.dll doesn’t work in isolation and we’d have to supply an entire private JRE — it’s much more than a single file.

Finally, I just want to point out that in our case, the problem is just the validity of a file path — Java 6 and Java 7 reside in different places, and a single path won’t work with both. However, the comment thread on The Register’s article has certainly come up with plenty of examples of Java software that works with Java 6 that simply won’t work with Java 7, so for other users this is a much bigger issue than just an invalid file path.

Software Development and the Bitness Challenge

Since 64-bit processors were introduced about ten years ago, they have become the default on both desktop and server machines. With a larger addressable process memory space, wider range of representable integers, and greater floating point precision, what’s not to like? For software developers that need to support both 32- and 64-bit machines, it’s a bit complicated.

Applications written in pure managed code (whether in .NET Intermediate Language or Java bytecodes)  will typically work just fine regardless of whether they’re running as 32-bit or 64-bit processes. However many applications also contain non-managed or native code that is targeted to a specific “bitness” (where “bitness” describes the distinction between 32-bit and 64-bit qualities of a platform, process, or system). In these cases, the developer needs to be careful that the right version of the non-managed code executes when the application is run, depending on the bitness of the running process. For software vendors, like JNBridge, which produce both self-contained applications and components that are integrated inside other users’ applications, the situation becomes even more tricky – the right version of the components needs to be included in the application, and it’s a common and easy mistake for users to include the components with the wrong bitness.  The user sees an error, and we get a support call. Better to reduce the chances of an error, and everybody will be happy.

After facing these issues for a number of years, we now have a workable solution, and our new JNBridgePro 7.0 release reflects this experience. We’d like to share what we’ve learned, so that other developers can successfully address the bitness challenge.

Create a single unified installer for both 32-bit and 64-bit scenarios. Developers’ first instinct is to create separate 32-bit and 64-bit builds and distribute separate installers for each. While this may seem logical, it leads to a lot of user confusion. In our experience, users faced with the decision as to whether to download the installer for the 32-bit or 64-bit version frequently download the wrong one. When they discover this error, they have to go back and download the other version, often after engaging support. Combining the 32- and 64-bit versions into a single installer is a win for the user and a win for us.

The challenge in building a single installer is that most installer generator packages either don’t allow 32-bit and 64-bit components to be combined in a single installer that would run on either system, or if they do support it, the resulting installation is too complicated. In particular, the usual technique of combining 32-bit and 64-bit installers, and a bootstrapper, into a single installer won’t work in our case, since we want 64-bit components to be installed on 32-bit machines, too. We resolved this by creating a single 32-bit installer (that is, one that would run on both 32-bit and 64-bit machines), and fooling the installer generator into thinking that the 64-bit components were simply “content,” so that the installer generator wouldn’t reject them. The “content” is all the 64-bit components packed into a single zip file. When the installer is run, a custom action extracts the 64-bit components then removes the zip file. While there are subtleties in getting this right (Google ‘advertised shortcut’ for pointers to issues that can arise), when done correctly this approach works perfectly and yields a single installer that runs on both 32-bit and 64-bit machines, and installs both 32-bit and 64-bit components on both machines. (Why do we need to install 64-bit components on 32-bit machines? Because, while the build machine may be 32-bit, the developer may be generating 64-bit applications.)

Ensure that users can use your components to create “Any CPU” applications. “Any CPU” applications are designed to automatically run as 32-bit processes on 32-bit machines and as 64-bit processes on 64-bit machines. This is very attractive as it allows the application to take advantage of whatever platform it runs on, and suggests that the code is portable. “Any CPU” implies that the code is completely managed, but there’s nothing to prevent unmanaged code, including third-party components, from being included. We’ve encountered scenarios where a developer used JNBridgePro to create an Any CPU application on their 64-bit development machine, successfully test it there, then ran into problems in deploying the application to a 32-bit machine. Situations like this suggest that developers should develop both 32-bit and 64-bit versions of their components or applications, but this is kicking the developer’s problem down the road, forcing the user to deal with using the right version. This should be as unacceptable to the software developer as it is to the user.

In order to allow users to create and run “Any CPU” applications, we changed JNBridgePro so that the appropriate 32-bit or 64-bit components would be loaded at run time depending on the bitness of the running process. However, platform vendors don’t make this easy. We discovered that the simplest way to test bitness at run time in .NET is to check the value returned by IntPtr.Size: 4 means you’re running as a 32-bit process; 8 means it’s a 64-bit process. (Note that the .NET alternative of calling Environment.Is64BitProcess won’t work in our case, since that API is only supported in .NET Framework 4.0 and later, and our components also need to be able to run in .NET Framework 2.0 through 3.5.) In Java, simply check the value of the system property os.arch. “x86” means that it’s a 32-bit process; “x86_64” means it’s a 64-bit process. These tests allow us to load the appropriate components at run-time, thereby supporting users who want to create applications that run anywhere, even though some of our components contain 32-bit or 64-bit targeted code.

Be careful using the Windows registry. When Microsoft created 64-bit Windows, they made the fateful decision to provide separate 32-bit and 64-bit registries, accessible only to respective 32-bit and 64-bit processes. The problem arises when the registry is used to share information between applications. Information deposited by  a 32-bit application is inaccessible to a 64-bit application, and vice versa. The answer is to only use the registry for information used by a single application, and only when the application’s bitness on that machine will always be the same. Save the information shared between applications in files; do not use the registry. JNBridge used to store information in the registry, but this became a constant source of support headaches once 64-bit platforms were introduced. Now that we store information in files, not the registry, these bitness-related headaches have gone away.

In conclusion: If all the processes in the world were 64-bit processes, the problems I’ve mentioned above wouldn’t exist.  However, as long as 32-bit processes and platforms still exist and must be supported, software developers must be careful and watch out for pitfalls that can trip up users. When developers adhere to the guidelines discussed above, users’ bitness problems should disappear, to the great benefit of both users and software support teams.

JNBridgePro and Java 8

Java 8 has a couple of new features (particularly, static and default methods in interfaces) that create problems for our current JNBridgePro 7.1. We will be coming out shortly with a new version that supports Java 8 along with previous versions of Java, but if you’re currently using JNBridgePro 7.1 and are having problems with Java 8, contact us and we’ll send you a patch.

New Java 8 features in JNBridgePro 7.2

Usually, when new versions of Java are released, we at JNBridge don’t have much to do.  The features generally don’t have an impact on what JNBridgePro does, and things just work.  Sometimes, as with variable-length argument lists (introduced in Java 5), they are simply syntactic sugar and are automatically proxied into their underlying form (an array).  Other times, new features occur behind the scenes (for example, lambda expressions in Java 8) and are never exposed to .NET users through proxies, so JNBridgePro doesn’t need to worry about them.

Java 8, however, contains two new features, static interface methods and default interface methods, that do affect JNBridgePro functionality. Both features are designed to improve usability of the language (whether or not they do is debatable, but that’s a topic for another blog post).  Static methods in interfaces are designed to add additional functionality to an interface, in the same way that static field constants have existed in Java interfaces in the past.  Default interface methods are intended to spare users from having to incorporate common implementations of certain interface methods; only unusual implementations need to be supplied.  Both static and default interface methods cause interfaces to act like abstract classes, with greater flexibility.

For JNBridgePro, the problem is that interfaces in most .NET languages (including C#) do not have these features, and attempting to map these features directly during proxy generation will cause exceptions to be thrown, or will cause DLLs to fail verification at run time.  In order to make these new Java 8 features available to .NET developers, we had to make some changes in the way that these new members of Java interfaces are mapped to their .NET proxies.

It was easy to determine how to map static interface methods. We’ve already had to deal with the problem with mapping static interface constants to proxied .NET interfaces, because most .NET languages don’t allow these, either.  For each proxied Java interface, we automatically create an associated helper class (called IConstants, when the original interface is I), which contains the static constants. Starting with JNBridgePro 7.2, static interface methods are also proxied into those same helper classes.

Handling default interface classes is a bit more involved. Not only are C# interfaces not permitted to include actual method implementations, “default” or not, but any C# class implementing an interface must account for every method in the interface, even if a Java class implementing the underlying interface doesn’t need to account for default interface methods.  This means that when we proxy a Java class that implements an interface with default methods, and that uses the default method, the proxied class will fail .NET verification because it doesn’t account for all methods in the implemented interface. To remedy this, starting with JNBridgePro 7.2 if a Java class relies on a default interface method and doesn’t actually provide an implementation of its own, then the .NET proxy will include an implementation of the method, which when called will result in a call to the default method. This should be transparent to the user.

Java-in-.NET embedding and Java 7 and 8

Embedding Java components in .NET applications, when using Java 7 or 8, doesn’t work the same way it previously did with Java 5 or 6, as the focus handling has changed.

When Java components are embedded in .NET applications, and Java 7 or 8 is being used, focus-related events like keyboard events and mouse wheel events are no longer handled properly — they are no longer directed to the appropriate component, but rather are directed to the wrong place and dropped. (Other mouse events, including clicks, which are not focus-related, still function properly.)

Starting with Java 7, the Windows implementation of the AWT focus subsystem was changed.  Previously, every AWT and Swing component was mapped to an underlying Windows component (a “focus proxy”) that handled focus-related events and dispatched them to the appropriate AWT/Swing component. With Java 7 (and continuing into Java 8), the owning frame acts as the focus proxy for all components that are contained within it. Oracle claims that “the mechanism is transparent for a user,” but the change does dramatically affect the behavior of Java AWT and Swing components that are embedded inside Windows Forms and WPF applications. Our research indicates that the AWT focus subsystem is choosing the wrong Windows component as the focus proxy.

We are currently working on a fix to this problem, but we have no estimate on when that fix will be ready. In the meantime, if you are embedding Java components in .NET applications, we recommend using Java 6 for the moment. Note that if your embedded Java component does not depend on focus-related events (for example, it does not take text input or use keyboard shortcuts or respond to mouse wheel events), then you should be able to use Java 7 or 8.

Also note that embedding .NET UI components inside Java applications still works fine as before, whether Java 5, 6, 7, or 8 is being used.

We thank you for your patience while we work on this issue, and we apologize for the inconvenience.