Comments on: Static Java classpath hacks https://undocumentedmatlab.com/articles/static-java-classpath-hacks?utm_source=rss&utm_medium=rss&utm_campaign=static-java-classpath-hacks Professional Matlab consulting, development and training Tue, 28 Apr 2020 10:46:14 +0000 hourly 1 https://wordpress.org/?v=6.7.3 By: Joonas T. Holmi https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-508577 Tue, 28 Apr 2020 10:46:14 +0000 http://undocumentedmatlab.com/?p=5945#comment-508577 Very good comment Yair! I posted the above function too hastily… I will have to apologize for claiming above that it allows updating MATLAB’s jar files without conflicts. I finally tested it more thoroughly myself and it eventually failed in errors. I was trying to use updated version of Apache’s commons-compress.jar in my toolbox without success. Perhaps my functions in GitLab can however help someone far more savvy Java programmer to solve i.e. this obsolete jar updating issue.

]]>
By: Yair Altman https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-508548 Fri, 24 Apr 2020 14:42:13 +0000 http://undocumentedmatlab.com/?p=5945#comment-508548 In reply to Joonas T. Holmi.

Thanks for sharing Joonas – this seems useful indeed.

Users who will use your script are probably savvy Java programmers, who don’t need a reminder of the dangers in subverting Matlab’s shipping Java classes with different ones… 🙂

]]>
By: Joonas T. Holmi https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-508547 Fri, 24 Apr 2020 14:31:59 +0000 http://undocumentedmatlab.com/?p=5945#comment-508547 Yair and others,
I found yet another way to add files to Java’s class path. See the function java_class_path_add_before_static(file) below. Benefit of this new method is that the classes are dynamically set before MATLAB’s static ones and its classes will be found first. This allows me to, for instance, provide newer version of *.jar of same jar libraries MATLAB uses (i.e. commons-compress.jar). It worked in at least R2019b and R2011a in short test.

I also wrote few other useful functions, available at my MATLAB toolbox repository under free BSD license:
https://gitlab.com/jtholmi/wit_io/-/tree/develop/helper/java

% Add a file to custom URL ClassLoader, which is set as a new ancestor of
% Java's System ClassLoader. In effect, its classes are found first before
% those of System ClassLoader during the class search. For instance, some
% of the MATLAB's built-in jar files can now be updated to newer version
% without conflicts. This also tries to make the file classes available
% on MATLAB side without need for external calls like importing. The
% following website has inspired developing this code:
% http://undocumentedmatlab.com/articles/static-java-classpath-hacks
function java_class_path_add_before_static(file),
    persistent jUCL cURLs;
    
    % Error if file does not exist
    jF = java.io.File(file);
    if ~jF.exists(),
        error('%s (The system cannot find the file specified)', file);
    end
    
    % Construct URL
    jURL = jF.toURI().toURL();
    cURL = char(jURL.toString());
    
    % Add if not already added or exit
    if any(strcmp(cURLs, cURL)),
        return;
    end
    cURLs{end+1} = cURL;
    
    % Create new custom URL ClassLoader if missing
    if isempty(jUCL),
        args = javaArray('java.net.URL', 1);
        args(1) = jURL;
        jUCL = java.net.URLClassLoader(args, []); % Set its parent to Bootstrap ClassLoader
        % Change Application ClassLoader ancestor to it
        jC_CL = java.lang.Class.forName('java.lang.ClassLoader');
        jF_parent = jC_CL.getDeclaredField('parent');
        jF_parent.setAccessible(1); % Set field public
        root = java.lang.ClassLoader.getSystemClassLoader(); % Application ClassLoader
        while true, % Loop until the Bootstrap ClassLoader is found
            parent_new = jF_parent.get(root); % Parent of Application ClassLoader is Extension ClassLoader. % Parent of Extension ClassLoader may be Bootstrap ClassLoader.
            if isempty(parent_new), break; % Bootstrap ClassLoader is null
            else, root = parent_new; end
        end
        jF_parent.set(root, jUCL); % Make it the highest ancestor
    else, % Otherwise, append to custom URL ClassLoader
        jC_UCL = java.lang.Class.forName('java.net.URLClassLoader');
        params = javaArray('java.lang.Class', 1);
        params(1) = java.lang.Class.forName('java.net.URL');
        jM_addURL = jC_UCL.getDeclaredMethod('addURL', params);
        jM_addURL.setAccessible(1); % Set method public
        jM_addURL.invoke(jUCL, jURL); % Append file to the java class path
    end
    
    % Try to make file class content visible on MATLAB side without need for external calls
    [~, name, ext] = fileparts(file);
    if strcmpi(ext, '.jar'),
        jJF = java.util.jar.JarFile(file); % Benefits from java.io.RandomAccessFile
        ocu_jJF = onCleanup(@() jJF.close()); % Safe cleanup of the file reading
        entries = jJF.entries();
        while entries.hasMoreElements(),
            entry = entries.nextElement();
            entry_file = char(entry.getName());
            [entry_path, entry_name, entry_ext] = fileparts(entry_file);
            if strcmpi(entry_ext, '.class'),
                % Assume that entry_path describes the package route to the entry_name.class
                entry_classname = [strrep(entry_path, '/', '.') '.' entry_name]; % JAR file separator always '/'
                try,
                    java.lang.Class.forName(entry_classname, 1, jUCL); % Try to initialize class
                    break; % On success, stop loop.
                catch,
                    % Failed. Try next.
                end
            end
        end
    elseif strcmpi(ext, '.class'),
        % Assume that bare *.class file does not belong to a package
        classname = name;
        try,
            java.lang.Class.forName(classname, 1, jUCL); % Try to initialize class
        catch,
            % Failed.
        end
    end
end
]]>
By: Joonas T. Holmi https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-508535 Thu, 23 Apr 2020 06:58:07 +0000 http://undocumentedmatlab.com/?p=5945#comment-508535 Krishnan,
It is ‘java.lang.System.getProperty‘ and ‘java.lang.System.setProperty‘. I read that JVM uses ‘java.class.path’ content only at startup to create its SystemClassLoader and ignores it later on. I read that SystemClassLoader could be made to reinitialize by setting its sys_paths-field to null (https://stackoverflow.com/questions/15409223/adding-new-paths-for-native-libraries-at-runtime-in-java), but nothing happens unfortunately:

jC_CL = java.lang.Class.forName('java.lang.ClassLoader');
jF_sys_paths = jC_CL.getDeclaredField('sys_paths');
jF_sys_paths.setAccessible(1);
jF_sys_paths.set([], []); % Tell SCL to reinitialize on the next class load
]]>
By: Krishnan https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-369159 Thu, 04 Feb 2016 12:17:02 +0000 http://undocumentedmatlab.com/?p=5945#comment-369159 Yair,

I think I have a method of adding and removing from static class path, but I am unable to make it stick on subsequent restarts of Matlab. The idea is to use java.lang.setProperty and java.lang.getProperty to set and edit the static class path.
1. Get the string from static class path and store it as a char

in_string=char(java.lang.getProperty('java.class.path');

2. manipulate the string either through string concatenate (strcat), string replace (strrep)
3. put the string back into the java.class.path property

java.lang.setProperty('java.class.path',java.lang.String(in_string));

If you could help add the last bit where loaded class paths can be made permanent. This would be great.

On a secondary note thank you for sharing whereisclassloadingfrom from Andrew, it has saved so much time.

thanks
krishnan

]]>
By: Ed Yu https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-354127 Thu, 30 Jul 2015 16:52:09 +0000 http://undocumentedmatlab.com/?p=5945#comment-354127 Yair,

I would say with regard to loading classes through the java (root) classloader directly should not be that risky… As long as MATLAB is using Java, the root classloader should behave exactly the way it is designed. But you are right if you load classes through the MATLAB classloader, it is definitely prone to changes in the future. That’s why the utility whereisjavaclassloadingfrom from Andrew is wonderful, a necessity to tell what classloader a class comes from (in order to debug class loading issues).

Ed.

]]>
By: sebastian https://undocumentedmatlab.com/articles/static-java-classpath-hacks#comment-354120 Thu, 30 Jul 2015 14:54:55 +0000 http://undocumentedmatlab.com/?p=5945#comment-354120 Specifying relative paths in classpath.txt may not be supported, but for me it works just fine in R2011b.

Simply add paths relative to the location of classpath.txt (i.e. relative to matlabs startup directory):

# matlab's original classpath entries here
# ...

# your own stuff
myjars/myjar1.jar
myjars/myjar2.jar

The corresponding entries in the javaclasspath within matlab are not expanded properly, but all jars are properly loaded.

]]>