Java class access pitfalls

A few days ago I consulted to a company that wanted to integrate some of their Java code in Matlab. The Java code compiled ok and ran just fine in the Java environment, but when we wanted to use the relevant Java classes in Matlab we got all sorts of errors. Since I believe that this could be a common problem, and I’m not sure that there’s any other place that does this, I thought to list the possible error causes in today’s article:

  1. Forgetting or mistyping the package name:
    >> jObject = JButton('Click me!');  % missing package name
    Undefined function 'JButton' for input arguments of type 'char'.
    
    >> jObject = java.swing.JButton('Click me!');  % should be javax.swing...
    Undefined variable "java" or class "java.swing.JButton". 
    
    >> jObject = javax.Swing.JButton('Click me!');  % should be javax.swing...
    Undefined variable "java" or class "javax.Swing.JButton". 
    
    >> import java.swing.*  % should be javax.swing... - no error here, only in the next line
    >> jObject = JButton('Click me!');   % missing package name
    Undefined function 'JButton' for input arguments of type 'char'.
    
    >> import java.swing.JButton  % should be javax.swing...
    Error using import
    Import argument 'java.swing.JButton' cannot be found or cannot be imported.
    

    Note: package names are typically lowercase, but 3rd-party packages might not follow this convention! Since Java is case-sensitive, the package name must be exact.

  1. Mistyping the class name. As with package names, Java class names are also case-sensitive. Beware of cases of O/0 (capital-“o” vs. the digit zero), and I/l/1 (capital-“i” vs. lowercase “L” vs. the digit 1). Also note whether the class name includes the underscore (“_”) character.
     
  2. Using a Java class that was compiled using a newer JDK major-version than the JVM used in Matlab. The specific JVM used by Matlab varies across platforms and Matlab releases. On R2014a Win64, the default (pre-installed) version is 1.7, which means that classes compiled using (or targeting) JDK 7 or below are ok, but classes compiled using JDK 8 or above would simply not run:
    >> a = MyJavaClass;
    Undefined function or variable 'MyJavaClass'.
    
    >> a = my.java.Class;
    Undefined variable "my" or class "my.java.Class".
    

    To get Matlab’s current JVM version, use the version command:

    >> version -java
    ans =
    Java 1.7.0_11-b21 with Oracle Corporation Java HotSpot(TM) 64-Bit Server VM mixed mode
    

    The default JVM used in Matlab R14 (7.0) to R2013a (8.1) was 1.6 (=JDK 6); R2013b (8.2) and later use 1.7 (=JDK 7). If you wish to ensure compatibility with earlier Matlab releases, I suggest compiling using JDK 6 rather than 7 or 8. This can easily be modified in all Java IDEs, in the project’s preferences. Remember to reference the corresponding JRE 1.6 libraries as well, rather than the newer JRE ones. In many cases, the Java code would still compile and run properly, even when compiled using the earlier JDK/JRE.

    If downgrading the compilation JDK is not possible (for example, if you do not have access to the source code, or if it uses some new JDK features), then consider installing a newer compatible JVM in Matlab.

    Note that when we try running the Java classes using the java executable (outside Matlab), we do get a meaningful message about the fact that the classes were compiled for a higher JVM version. Unfortunately, this information is simply not being provided by Matlab – just the standard plain error about the function or file not being found. I hope this will be fixed in some future Matlab release.

  3. Forgetting to update the Java classpath. Java has a different classpath than Matlab’s. Class or JAR files placed on Matlab’s path are not recognized. Instead, these files need to be added to the Java classpath, either dynamically within the Matlab session (using the javaaddpath function), or statically (by adding entries to the classpath.txt or javaclasspath.txt files). Read here for additional details. Note that the javaclasspath.txt alternative only exists since R2012b (8.0). You can view the current Java classpath in Matlab using the javaclasspath command.
     
    When updating the Java classpath, either dynamically or statically, beware of the following pitfalls:

    • If you add a folder name to the javaclasspath, then all *.class files are automatically recognized and they will be loaded when first used. However, this does NOT include *.jar or *.zip files placed in these folders. The Java specification requires such files to be added individually to the classpath, using their full pathname.
    • While we can use relative paths when adding elements dynamically (using javaaddpath), we must specify the full pathname when updating the static classpath files.

     

  4. Dependency #1: A class that depends on any other class that is not on the current Java classpath (either dynamic or static). This is typically due to the fact that the class was compiled in a Java IDE where there were multiple referenced libraries, which were not copied to the Matlab classpath. It is not enough to simply place the generated output *.class or *.jar files in Matlab’s Java classpath – the referenced libs must also be placed there. If they are not, then Matlab simply issues the generic error that the function or class cannot be found. Matlab provides no information that this is a dependency error and we must infer this ourselves. Note that it does not matter that the non-existent class might referenced in an obscure portion of the class code that is never reached – the mere fact that there is a reference in the class that cannot be located causes the entire class to fail to load. Debugging this can indeed be nerve wrecking (and another example)… I hope that this too will be fixed in some future Matlab release.
     
  5. Dependency #2: A class on the static Java classpath that depends on another class which is on the dynamic Java classpath. All static classes must depend only on other static classes, since the static classpath is processed before the dynamic one, when the JVM is launched at Matlab startup.
     
  6. Non-public class – Matlab can only access public classes. If the class is private, protected or has package visibility (by not specifying any visibility modifier at all), then the class would not be loadable by Matlab, only by other Java classes. So remember to add the public keyword in the definition of all the Java classes that you wish to directly use in Matlab.
     
  7. Non-public constructor – Matlab can only create objects for non-static classes that have public constructors. If all the class constructors are non-public, then Matlab cannot create a class instance object. In such a case we would get a misleading error message, that at least hints that the problem is with the constructor:
    >> jObject = MyJavaClass();
    No constructor 'MyJavaClass' with matching signature found.
    

    Note that using private constructors is common practice for singleton designs. In such cases we might find a public static class method that returns the class reference (e.g., MyJavaClass.getInstance()).

  8. Non-matching constructor – This is a variation of the previous pitfall. In this case, we try to invoke a class constructor with input parameters that do not match the declaration. A typical example of this is when the constructor is declared to accept an int, and we try to pass a standard Matlab number (which is a double). In such cases, a simple solution could be to either modify the class, overloading the constructor to also accept double, or to type-cast the inputs in Matlab to the relevant type expected by a constructor:
    jObject = MyJavaClass(int32(value));

Any other pitfalls I forgot to mention? Let us know in a comment below.

p.s. – the javaclasspath function has an undocumented input parameter: specifying -v0, -v1 or -v2 sets the Java classloader’s verbosity level to the relevant value. If you don’t know what this means, then you probably don’t need to use this feature…

Categories: Java, Low risk of breaking in future versions, Undocumented feature

Tags: ,

Bookmark and SharePrint Print

6 Responses to Java class access pitfalls

  1. waldroje21 says:

    Any idea why a Matlab would recognize a Java class file from the Dynamic path, but not the Static? I’ve added it to my Static path, and it’s in there… but does not work… when I then add the same path using javaaddpath , it works.

    • Perhaps one or more of the following happened:

      * you edited the wrong classpath.txt file
      * you forgot to restart Matlab after editing the classpath.txt file
      * you mistyped the class’s full path in the file
      * you used a relative path in the file

    • waldroje21 says:

      in regard to the first… it shows up on the bottom when I type javaclasspath in the workspace, so I assume it can’t be the wrong file, and that path is what i get when i type prefdir… I definitely restarted Matlab…. and I’m using the same exact file string in the Dynamic path, that I use in the javaclasspath.txt, i just copied it straight from terminal. I’ve done this with no problem on 3 other installs, so it’s a bit strange.

    • If you wish me to help you debug your specific installation, contact me by mail for a private consulting.

  2. Vincent says:

    How about what to do when you get a “java.lang.NoSuchFieldError” because your java code uses a version of Apache’s httpcore that is newer than the one matlab ships with, and the old one seems to be creating conflicts with the newer one you’ve added to your java path?

    • Malcolm Lidierth says:

      The class loader will only load one copy of a specific package/class combination. A newer version will not replace the older one even if you have called javaaddpath (hence the NoSuchFieldError which implies you have created an instance using the old code). This is a Java class loader feature: if you load a jar that has dependencies on other jars (e.g. in a /dist/lib folder), and some of those jars are already loaded, the already loaded versions will continue to be used.

      In general, open source projects with the Apache brand will maintain backwards compatibility – deprecating, but not removing, old fields/methods. If backwards compatibility is not maintained Apache projects will usually alter the package names so both versions can be used simultaneously. This being the case, replacing the jar file shipped with MATLAB with the newer version should produce no problems. But, anyone else using your code will need to do that too. Buyer beware.

Leave a Reply


Your email address will not be published. Required fields are marked *