Adding com.sun.tools.jar in classpath of jar
Asked Answered
P

3

1

I'm having trouble using tools.jar present in jdk1.8.0_121/lib/tools.jar.

My $JAVA_HOME is set to:

# echo $JAVA_HOME
/usr/local/java/jdk1.8.0_121

The path to tools.jar is :

# ls /usr/local/java/jdk1.8.0_121/lib/tools.jar
/usr/local/java/jdk1.8.0_121/lib/tools.jar

And I use the following java executable to run the code:

/usr/local/java/jdk1.8.0_161/bin/java

But, when I access the VirtualMachine class, it throws

Caused by: java.lang.ClassNotFoundException: com.sun.tools.attach.VirtualMachine
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_161]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_161]
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338) ~[na:1.8.0_161]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_161]
        ... 72 common frames omitted

Can someone explain why Java is not able to find lib/tools.jar in its classpath & What can I do to correct this behaviour?


To run on my local machine, I've added the following dependency in my pom:

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

But, when I deploy it on the server, this jar is not packaged due to system scope & neither does it find the jar on the server's jdk path.

Isn't it supposed to find all the jdk jars automatically?

I've also tried to add env variable $JAVA_HOME in the class-path entry of jar's MANIFEST file as follows:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: pankajsinghal
Class-Path: $JAVA_HOME/lib/
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_181

But, this is also not working. Also, I don't want to add this lib's jar in my code explicitly as it's a JDK lib and I guess the proper way to access this would be from the system's JDK path itself. So, looking for a solution in this direction itself.

Any help is really appreciated.

Phyllis answered 6/8, 2018 at 12:36 Comment(5)
Why do you need tools.jar ?Featherstitch
I need to attach a javaagent programmatically.Phyllis
Which kind of javaagent ?Featherstitch
aspectjweaver - for AOP LTWPhyllis
Don't use system scope unless you know what you are doing and really need to.Burnight
R
1

You can try it like this:

java -cp "/path/your.jar:/usr/local/java/jdk1.8.0_121/lib/tools.jar" your.MainClass

or Refer to the following:

The type "com.sun.tools.javac.util.Assert" is not accessible

Hopefully it helped you.

Resect answered 2/4, 2020 at 9:26 Comment(0)
E
0

You have to add that jar in project properties. In eclipse, To Add this Jar to your build path Right click the Project > Build Path > Configure build path> Select Libraries tab > Click Add External Libraries > Select the Jar file.

Emmet answered 6/8, 2018 at 12:40 Comment(3)
As I mentioned in the question, I'm able to run it locally by adding system dependency. How can I access this jar on the server while running from command line?Phyllis
As I said I tried adding it in the classpath in the following manner Class-Path: $JAVA_HOME/lib/, but it doesn't seem to work. Hardcoding the absolute path might depend from machine to machine & I'm looking for a generic solution which works everywhere without hardcoding/using machine specific paths. I tried with $JAVA_HOME because this would be available on every machine. If you have a generic solution, it would help me.Phyllis
Environment variables are not recognized in Manifest entries.Burnight
A
0

you can directly add toos.jar to your current classLoader, but it just an idea.

File getJar = new File(folderLibsPath + File.separator + "tools.jar");
URLClassLoader classLoaderExt = (URLClassLoader) this.getClassLoader();
URL jarUrl = getJar.toURI().toURL();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoaderExt, jarUrl);

referenced from:How to load JAR files dynamically at Runtime?

and don't forget to load the attach.so (or attach.dll)
by System.load(absPath) or System.loadLibrary(dllName)

File attachedDLL = new File(folderLibFilePath);
if (attachedDLL.exists()) {
    System.load(attachedDLL.getAbsolutePath()); 
}

I think we had got the same issue, and this code works for my case.

Also, there is another way to add tools.jar into classpath, but actually they did the same thing:

public void onEnable() throws Exception {
    URLClassPath ucp = (URLClassPath) Reflection.getPrivateField("ucp", this.getClassLoader().getParent()); // reflect the subClass of URLClassLoader
    File getJar = new File(folderLibsPath + File.separator + "tools.jar");
    URL jarUrl = getJar.toURI().toURL();
    ucp.addURL(jarUrl); // or just change its "URLs" field by put your jarURL in its Stack
}

But it should be mention that, this way the Java will use the AppClassLoader(SystemClassLoader) to load the tools.jar (also the invoker - your application will). This may have a bad effect on your original class initialization if you use CustomClassLoader. (because depending on the Java Parent Delegation Model, the superClassLoader cannot know which class load by its subClassLoader).

So if you are developing a plugin under a customClassLoader (the subclass of system classloader), the classpath in AppClassLoader should be removed (which means let custom PluginClassLoader to load it, or not its super one) after your VM was detached.
here I used reflection to accomplished.

public class Main {
    public void onEnable() throws Exception {

        /** load attach.dll */
        System.loadLibrary("attach");

        /** load tools.jar */
        URLClassPath ucp = (URLClassPath) Reflection.getPrivateField("ucp", this.getClassLoader().getParent());
        File getJar = new File(folderLibsPath + File.separator + "tools.jar");
        URL jarUrl = getJar.toURI().toURL();
        ucp.addURL(jarUrl);
       
        /** attach, load, detach VM */
        VirtualMachine vm;
        vm = VirtualMachine.attach(this.getPid());
        // if the current jar itself is the agent
        vm.loadAgent(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath());
        vm.detach();
        
        /** change the classLoader back to your custom */
        changeClassLoaderBack();

        /** unload native DLL Lib */
        unloadNativeLibs(); // or you can add a condition to unload attach.dll only
    }

    public void changeClassLoaderBack() {
        
        URLClassPath ucp = (URLClassPath) Reflection.getPrivateField("ucp", this.getClassLoader().getParent());
        
        /** reset field path */
        List<?> path = (ArrayList<?>) Reflection.getPrivateField("path", ucp);
        List<URL> newPath = new ArrayList<>();
        path.forEach((v) -> {
            if(!((URL)v).getPath().contains("toos.jar") && !((URL)v).getPath().contains(this.getPlugin().getName())) {
                newPath.add(((URL)v));
            }
        });
        Reflection.setPrivateField("path", ucp, newPath);
        
        /** reset field URLs */
        Reflection.setPrivateField("urls", ucp, new Stack<URL>());
        
        /** reset fields loader and LMAP */
        List<Object> newLoader = new ArrayList<>();
        Map<Object, Object> newLMAP = new HashMap<>();
        ((HashMap<?,?>)Reflection.getPrivateField("lmap", ucp)).forEach((k,v) -> {
            if (!((String)k).contains("tools.jar") && !((String)k).contains(this.getPlugin().getName())) {
                newLMAP.put(k, v);
                newLoader.add(v);
            };
        });
        Reflection.setPrivateField("lmap", ucp, newLMAP); 
        Reflection.setPrivateField("loaders", ucp, newLoader);
        
    }

    private String getPid() {
        RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
        String pid = bean.getName();
        if (pid.contains("@")) {
            pid = pid.substring(0, pid.indexOf("@"));
        }
        return pid;
    }

    private void unloadNativeLibs(ClassLoader unloadDLLfromWhichLoader) {
        try {
            ClassLoader classLoader = unloadDLLfromWhichLoader;
            Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
            field.setAccessible(true);
            Vector<?> libs = (Vector<?>) field.get(classLoader);
            Iterator<?> it = libs.iterator();
            Object o;
            while (it.hasNext()) {
                o = it.next();
                Method finalize = o.getClass().getDeclaredMethod("finalize", new Class[0]);
                finalize.setAccessible(true);
                finalize.invoke(o, new Object[0]);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Reflection {
    public static Object getPrivateField(String fieldName, Object object) {
        Field field;
        Object o = null;
        try {
            field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            o = field.get(object);
        } 
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        } 
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }

    public static void setPrivateField(String fieldName, Object object, Object newField) {
        Field field;
        try {
            field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(object, newField);
        } 
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        } 
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

hope it can help you at some points

Aramanta answered 4/12, 2020 at 8:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.