12

MATLAB is configured to search its static java class path before searching the user-modifiable dynamic path. Unfortunately, the static path contains quite a number of very old public libraries, so if you are trying to use a new version you may end up loading the wrong implementation and get errors.

For instance, the static path contains an old copy of the google-collections.jar, which has long been supplanted by Google's guava library and which has some of the same class names (e.g. com.google.common.base.Objects). As a result, if you invoke a Guava method that uses a newer method of one of such a class, you will end up getting surprising NoSuchMethodErrors because the google-collections jar is found first.

As of R2012b, MATLAB lets you specify additional jars to add to the static path by putting a javaclasspath.txt file in your preferences folder, but that adds jars to the end of the path, and doesn't let you override jars that are built into MATLAB.

So what is the best way around this?

Amro
  • 123,847
  • 25
  • 243
  • 454
Christopher Barber
  • 2,548
  • 1
  • 22
  • 23

4 Answers4

13

I got an official response from Mathworks:

As of MATLAB R2013a (also in R2012b), classes can be added to the front of the static Java class path by including the following line in javaclasspath.txt:

<before>

Any directory that is after this line in javaclasspath.txt will be added to the front of the static Java class path. This is an undocumented use of javaclasspath.txt as of R2013a.

But overall in MATLAB, the ability to add classes to the front of the static Java classpath is not available through javaclasspath.txt in MATLAB 8.0 (R2012b).

MATLAB searches for classpath.txt in the following order:

  1. In the startup directory. As of MATLAB 8.0 (R2012b) a warning will be shown if the file is found there and it will be ignored.

  2. In the first directory on the MATLABPATH environment variable. (This environment variable is used in the bin/matlab shell script on Linux and in general is not used by the end-user).

  3. In the toolbox/local directory.

Although the MATLABPATH environment variable of point 2 is normally not used by end-users we can use it in a workaround to allow reading a custom classpath.txt outside of the toolbox/local directory.

On Windows:

You will need to create the MATLABPATH environment variable. The first directory on it should be your directory with the custom classpath.txt AND you will also need to add the toolbox\local directory as second option. So from a cmd prompt you could do:

set MATLABPATH=c:\Users\user\Documents\myMATLABClasspath;c:\Program Files\MATLAB\R2012b \toolbox\local matlab.exe

supyo
  • 3,017
  • 2
  • 20
  • 35
Christopher Barber
  • 2,548
  • 1
  • 22
  • 23
  • I think you are missing something in your answer.. What line should we add to `javaclasspath.txt` file? – Amro May 10 '13 at 02:17
  • 1
    Presumably this new hack suffers from the same risk of accidentally breaking MATLAB's internal use of these jars, but it is better than having to hack the internal classpath file. However, if you have to support users using many different versions of MATLAB, then this new hack doesn't really help all that much. – Christopher Barber Jun 07 '13 at 18:03
  • Ah, I should have known it was an HTML formatting thing :) Thanks chriswynnyk. @ChristopherBarber: I just remembered another idea I saw elsewhere; try using this [ClassPathHacker](http://stackoverflow.com/a/3581598) tool (it uses reflection to bypass encapsulation of the `URLClassLoader` class).. You can then use [this code](http://stackoverflow.com/a/4380622) to check which version is being used – Amro Jun 08 '13 at 06:43
  • 2
    @Amro: FYI, I put together an M-code version of the ClassPathHacker over at http://stackoverflow.com/a/22524112/105904. There are a couple issues using static classpath hacking in Matlab; in particular, javaclasspath()'s output doesn't reflect the changes. I'll bug-report it to MathWorks. – Andrew Janke Mar 20 '14 at 06:50
  • @AndrewJanke: that should make it easier to use the hack in MATLAB. Thanks for sharing the code. – Amro Mar 20 '14 at 16:53
  • there is no javaclasspath.txt file. Do you mean to say classpath.txt file? – liang Nov 29 '15 at 10:01
2

One hack that appears to work is to add the jar to the top of the classpath.txt file that can be found in your MATLAB installations toolbox/local folder. Unfortunately, this is automatically generated and may get rewritten at some unspecified time, such as when you install new toolboxes, so this approach would require you to have some way to notice when this happens and reapply the hack.

Christopher Barber
  • 2,548
  • 1
  • 22
  • 23
  • 1
    I cant think of another way, although you risk compatibility problems if you mess with the set of JAR files MATLAB is loading for its own use. – Amro May 03 '13 at 20:07
  • 1
    Yes, I am afraid this might be the only way around this. Matlab doesn't appear to use the thread context class loader, so overriding that won't do anything. Really, they should have found a way to isolate their use of these jars from the class loader path used by user-provided Java code, but it is clear that getting this kind of thing right is not one of Mathworks areas of competency. – Christopher Barber May 04 '13 at 18:45
  • 1
    @ChristopherBarber: It's clear that getting this kind of thing right is not one of the Java development community's areas of competency either. JSR 277 is dead, Project Jigsaw got pushed back _again_ to Java 9, and OSGi is rather complex and enterprisey to stick in something like Matlab. If Java's own developers are having trouble building a module system, it seems hard to blame Mathworks for not doing it for them. – Andrew Janke Mar 20 '14 at 06:58
  • Yes, Java doesn't do a great job on this, either but MATLAB is *much* worse. At least Java has a concept of packages and class loaders, where MATLAB really has no module system at all. Furthermore, MATLAB costs a lot of money but Java is free. – Christopher Barber Jul 01 '14 at 14:34
1

If you're distributing a jar that's intended to be used with matlab, it may be better to use proguard as described at http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava.

If you specify that all of your classes and their (public) fields and methods are to be preserved and include guava as a program jar (not a library), then it will rename all of guava's methods and update your compiled bytecode to reference the new names.

It seems a bit hackish, but depending on the audience, it may be significantly easier than teaching your users about static vs. dynamic classpath, and it won't break any matlab code that depends on the old behavior.

1

Instead of obfuscating the package as suggested by @user2443532, I have found it easier to "shade" the conflicting package instead of obfuscating it - unless you actually need obfuscation. One easy way to do this is to build your package using Maven and use the maven-shade-plugin. Internal calls are modified automatically, so you don't need to modify any of the Java code.

Direct calls from Matlab will need to be modified - for example, calls to com.opensource.Class become shaded.com.opensource.Class.

For more info on shading, see What is the maven-shade-plugin used for, and why would you want to relocate Java packages?

Phil
  • 51
  • 5