projectlombok / lombok

Very spicy additions to the Java programming language.
https://projectlombok.org/
Other
12.91k stars 2.39k forks source link

[BUG] Unable to process resource in "jar in jar" #3125

Closed Ccccx closed 2 years ago

Ccccx commented 2 years ago

Describe the bug

I am building a SpringBoot-based dynamic compilation project. The program runs normally but the corresponding method is not generated. I try to load lombok outside the jar package and this problem will not occur. I am not sure if this is a bug. Debug found that lombok.launch.ShadowClassLoader#getResources has a problem with processing paths when processing "jar in jar" resources.

To Reproduce

If necessary, an example can be provided later. The following is a brief debugging of a debug:

  1. Find the resource name through the ShadowClassLoader#getResources method: "META-INF/services/lombok.javac.JavacAnnotationHandler";
  2. The URL returned by the parent class loader is "jar:file:/E:/IDEA/cloud/online-form/target/online-form-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/lombok- 1.18.22.jar!/META-INF/services/lombok.javac.JavacAnnotationHandler";
  3. After lombok.launch.ShadowClassLoader#isPartOfShadowSuffix, the value of the jarLoc variable is "file:/E:/IDEA/cloud/online-form/target/online-form-0.0.1-SNAPSHOT.jar";
  4. lombok.launch.ShadowClassLoader#isPartOfShadowSuffixJarBased will return false, and the resource path will not be added to the returned result.

Expected behavior The corresponding method can be correctly generated.

Version info (please complete the following information): Lombok version: 1.18.22 Jdk version: 1.8

rzwitserloot commented 2 years ago

This doesn't sound actionable. I certainly have no idea where to even begin; it doesn't strike me as a lombok bug. Try to edit this answer and indicate what's specifically going wrong, an example is obviously required for something like this. Make sure it's self contained.

Ccccx commented 2 years ago

You are right, here is a runnable code sample on github:https://github.com/Ccccx/lombok-dynamic-complier, you can download and package it into a jar to run, different command methods show different results, and there is no exception:

Users Methods:

toString

- run with parameters
Here I put **lombok-1.18.22.jar** in the **D:\software\jdk8\lib directory**

D:\software\jdk8\bin\java -Djava.ext.dirs=D:\software\jdk8\lib -jar lombok-dynamic-complier-0.0.1-SNAPSHOT.jar

result :

11:05:08.702 [main] INFO com.example.LombokDynamicApplication -

Users Methods:

equals toString hashCode canEqual setPassword getUsername getNickname getPassword setUsername setNickname

Obviously the second one is correct. The way ShadowClassLoader handles resources cannot be obtained in "jar in jar". This is also my problem. I'm not sure if this is normal or lombok itself does not recommend this use. Looking forward to you answer, thanks a lot!

Version info: Lombok version: 1.18.22 Jdk version: 1.8

rzwitserloot commented 2 years ago

This kind of crazy compilation is beyond what we support. However, if you want to delve into the source code of the shadow loader (it sounds like you're familiar with class loaders), and there's something relatively simple we can change that'll help you out, maybe we can do that.

For context, the shadow loader's aims is as follows: If you include lombok on your project's classpath, your IDE should not be offering lombok.javac.handlers.HandleToString for example, if you type HandleTo and hit autocomplete. Only the stuff in lombok and lombok.extern should be shown (the actual intended 'public' API of lombok). Hence why we employ a classloader, and why most class files in our jar do not end in .class.

That, and, trying to integrate ourselves into OSGi (Equinox) - the eclipse modularized classloader infra, requires jumping through a lot of hoops.

DioptricCoast22 commented 1 year ago

I had the same problem, but solved it in a crude way. Use javassist to insert a judgment statement in ShadowClassLoader's getResources method at startup to force the JavacAnnotationHandler resource into the vector, and it worked.

private void rewriteSCL() throws NotFoundException, CannotCompileException {
        final ClassLoader classLoader = getClass().getClassLoader();
        // This rewriting is necessary only in the spring boot environment
        if (!(classLoader instanceof LaunchedURLClassLoader)) {
            return;
        }
        final ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(getClass()));
        final CtClass ctClass = classPool.get("lombok.launch.ShadowClassLoader");
        URL[] urLs = ((LaunchedURLClassLoader) classLoader).getURLs();
        List<URL> lombokList = Arrays.stream(urLs).filter(url -> url.toString().contains("lombok")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(lombokList)) {
            return;
        }
        String path = lombokList.get(0).toString();
        String jarNamme = path.substring(path.lastIndexOf("lombok"), path.lastIndexOf(".jar") + 4);
        final CtMethod ctMethod = ctClass.getDeclaredMethod("getResources");
        String codeStr =
                "if (item.toString().indexOf(\"/BOOT-INF/lib/" + jarNamme + "!/META-INF/services/lombok.javac.JavacAnnotationHandler\") > 0) {\n" +
                        "   vector.add(item);\n" +
                        "        }";
        ctMethod.insertAt(453, codeStr);
        ctClass.toClass();
        ctClass.detach();
    }

It only works with Lombok-1.18.20 because other versions of lombok might need to insert at other line numbers, which is interesting