This page lists problematic code snippets we found in common free Java applications that make them dependent upon the Sun Java platform and shows how to implement things in a way that standards are respected and compatibility with GNU Classpath is the result.

One of the main problems we encounter is code which depends on com.sun.* classes. This is bad practice, as such code will not run on any non-Sun JVM, including the proprietary JVMs distributed by IBM and Apple (both of which have passed the Java TCK). So, please hold in mind that com.sun packages are not part of Java. Read Sun's take on it, here: http://java.sun.com/products/jdk/faq/faq-sun-packages.html

Index

Accessing properties in a security aware environment (sun.security.action.getPropertyAction)

We found the following piece of code:

  return URLEncoder.encode(str, (String) AccessController.doPrivileged(new 
     sun.security.action.GetPropertyAction("file.encoding")));

It should be clear that this will not be compileable with a Free Java library because the class sun.security.action.GetPropertyAction does not exist there. What the user is doing here is accessing the system property file.encoding using the AccessControll.doPrivileged method. A standards compliant and GNU Classpath compatible implementation would look like this:

  return URLEncoder.encode(str, (String) AccessController.doPrivileged(
        new PrivilegedAction() {
           public Object run() {
             return System.getProperty("file.encoding");
           }
        });

This uses an anymous class implementing the PrivilegedAction interface.

Applet audio clips (sun.applet.AppletAudioClip)

Some applets use the Sun internal class sun.applet.AppletAudioClip for audio clips, typically:

AudioClip ac = new sun.applet.AppletAudioClip(someUrlString);

This is undocumented stuff and won't work with Classpath. The correct way of getting an audio clip is:

AudioClip ac = java.applet.Applet.getAudioClip(someUrlString);

And use the AudioClip interface the way it is intended.

SSL sockets (com.sun.net.ssl)

As of JDK 1.4, newer versions of these classes are available in javax.net.ssl which is also available in Classpath.

SSL provider (com.sun.net.ssl.internal.ssl.Provider)

The JDK has an internal SSL provider which can be used with java.security.Security. Typically, it can be registered dynamically like this:

Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

This will not work on non-Sun JVMs. A solution is to use the free, pure-java Jessie implementation. It has a provider in org.metastatic.provider.Jessie, so with the jessie package in your classpath, you can register the Jessie provider the same way:

Security.addProvider(new org.metastatic.jessie.provider.Jessie());

JPEG encoding (com.sun.image.codec.jpeg)

We've run into programs accessing Sun's JPEG encoder directly:

import com.sun.image.codec.jpeg.*;

...

FileOutputStream out = new FileOutputStream(filename);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);

param.setQuality(compression, false); 

encoder.setJPEGEncodeParam(param);
encoder.encode(image);

out.close();

This can be done just as well using the public ImageIO interface:

import javax.imageio.*;
import javax.imageio.plugins.jpeg.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;

...

FileImageOutputStream out = new FileImageOutputStream(new File(filename));
ImageWriter encoder = (ImageWriter)ImageIO.getImageWritersByFormatName("JPEG").next();
JPEGImageWriteParam param = new JPEGImageWriteParam(null);

param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(compression);

encoder.setOutput(out);
encoder.write((IIOMetadata) null, new IIOImage(image,null,null), param);

out.close();

Base64 Encoding (sun.misc.Base64Encoder)

GNU Classpath includes a Base64 encoder in gnu.java.net.BASE64, but this is not really an exported API. Users could refactor this into a different package and use it, however. The idea here would be that they would simply carry a copy around with their application, to eliminate their sun.* dependency.

There is a public domain Base64 implementation here: http://iharder.sourceforge.net/base64/

For projects compatible with the ASL, there is an ASF Base64 package. The javadoc is here: http://jakarta.apache.org/commons/codec/apidocs/org/apache/commons/codec/binary/Base64.html

CORBA properties

org.omg.CORBA.ORBClass and org.omg.CORBA.ORBSingletonClass

These two properties are not proprietary, but by they nature they are sometimes set to the proprietary values. In some tutorials we found the code like:

public class HelloServer {
  public HelloServer(String[] args) {
      Properties p = System.getProperties();
      // add runtime properties here
      //Please note that the name of the servertool 
      //class may change in future releases.
      p.put("org.omg.CORBA.ORBClass", 
          "com.sun.corba.se.internal.POA.POAORB");
      p.put("org.omg.CORBA.ORBSingletonClass", 
          "com.sun.corba.se.internal.corba.ORBSingleton");

      ORB orb = ORB.init( args, p );

This code will never work with GNU Classpath because it directly enforces to use the central CORBA classes (ORBs) from com.sun.corba subpackages. It may crash with other alternative CORBA implementations as well. When migrating, just delete statements that set these two system properties to "com.sun (..)". CORBA should have and use the default values.

com.ibm.CORBA.ListenerPort and com.sun.CORBA.POA.ORBPersistentServerPort

Various firewalls does not allow opening multiple randomly selected ports, as the default CORBA implementation used to do. The firewall must be configured to allow CORBA to work on one fixed port or (for better performance) on a small fixed range of ports. This does not restrict the maximal number of the connected objects as the objects can share the same port.

As far as I currently know, there is still no OMG standard property for this, despite it was suggested long time ago as issue 3666. However there are proprietary properties like com.ibm.CORBA.ListenerPort or com.sun.CORBA.POA.ORBPersistentServerPort. Lack of this feature may prevent adaptation of java programs that needs them, so introduced the analogical gnu.CORBA.ListenerPort property. The value of this property is a single port or a range of ports, boundary values (inclusive) being separated by dash (for instance, "1245-1250").

// Create two coexisting ORB's:
Properties p = new Properties();
// This ORB will serve on the port 1234 only:
p.put("gnu.CORBA.ListenerPort", "1234");
ORB orb1 = ORB.init(args, p);

// This ORB will pick for its objects random ports from the range 1300-2000, inclusive:
p.put("gnu.CORBA.ListenerPort", "1300-2000");
ORB orb2 = ORB.init(args, p);

http://www.omg.org/issues/issue3666.txt

com.sun.CORBA.connection.ORBSocketFactoryClass (or ORBConstants.SOCKET_FACTORY_CLASS_PROPERTY field)

This property sets a custom socket factory class and is most frequently used to implement communications via SSL. Direct replacement is not possible because that class must implement interface from the com.sun.corba domain. You may need to rework your code. To help with adaptation, we provide a property gnu.Corba.SocketFactory that replaces the socket factory for the ORB being currently instantiated. The factory must implement trivial gnu.CORBA.interfaces.SocketFactory.

http://mihaibucica.3x.ro/corba_ssl.html

Assuming memory is limited

Free runtimes often have less limitations then proprietary runtimes. One example is that they often don't have a limit on the memory you can use (it is only limited by the amount of virtual memory a process can get). That means that code that does something like:

  // Use a quarter of the memory for our cache
  long cacheSize = Runtime.getRuntime().freeMemory() / 4;
  byte[] cache = new byte[cacheSize];

might create a cache that is much larger then you expect if you assume the heap is limited to 32 or 64 MB.

Make the actual upper bound of any cache a user defined property to prevent surprizes like eating up all physical memory on a machine.

Checking for malformed input (sun.io.MalformedInputException)

We found code that reports malformed input by identifying the Sun-private exception, sun.io.MalformedInputException:

  try
    {
      // Code that may encounter malformed input.
    }
  catch (Exception e)
    {
      if (e instanceof sun.io.MalformedInputException)
        {
          // Report malformed input.
        }
      else
        {
          // Report generic error.
        }
     }

We used reflection to determine that the standard exception java.io.CharConversionException is an ancestor of sun.io.MalformedInputException. You can use java.io.CharConversionException to ensure that your malformed input check will work on GNU Classpath-based runtimes:

  try
    {
      // Code that may encounter malformed input.
    }
  catch (Exception e)
    {
      if (e instanceof java.io.CharConversionException)
        {
          // Report malformed input.
        }
      else
        {
          // Report generic error.
        }
     }

ClasspathMigration (last edited 2007-08-13 05:25:41 by MichaelKoch)