简体   繁体   English

如何从 .war 文件加载类并使用它们?

[英]How to load classes from a .war file and use them?

I have a project that is generating a .war file as a build artifact.我有一个项目正在生成一个.war文件作为构建工件。 I want to load this .war into the JVM so that I can parse the runtime annotations it contains, but I am having trouble with loading the classes.我想将此.war加载到 JVM 中,以便解析它包含的运行时注释,但在加载类时遇到问题。

I have the following snippet of code to fetch class names from a JarFile :我有以下代码片段从JarFile获取类名:

public static Set<String> getClassNamesFromJarFile(JarFile jarFile) throws IOException {
    Set<String> classNames = new HashSet<>();
    Enumeration<JarEntry> e = jarFile.entries();
    while (e.hasMoreElements()) {
        JarEntry jarEntry = e.nextElement();
        if (jarEntry.getName().endsWith(".class")) {
            String className = jarEntry.getName()
                    .replace("/", ".");
            className = className.substring(0, className.length() - 6);
            classNames.add(className);
        }
    }
    return classNames;
}

And I use this snippet for getting the JarFile :我使用这个片段来获取JarFile

protected static JarFile getJarFile(String jarFileUrl) throws IOException {
    try {
        return new JarFile(new URI(jarFileUrl.replaceAll(" ", "%20")).getSchemeSpecificPart());
    } catch (URISyntaxException ex) {
        return new JarFile(jarFileUrl.substring("file:".length()));
    }
}

Then, on my main, I have the following code:然后,在我的主要部分,我有以下代码:

public static void main(String[] args) throws IOException {

    String warpath = "C:\\User\\Me\\Desktop\\someWarFile.war";

    // Fetch JarFile
    JarFile jar = getJarFile("file:" + warpath);

    // Load .war into the JVM
    URL url = new File(warpath).toURI().toURL();
    URLClassLoader child = new URLClassLoader(
            new URL[]{url},
            Object.class.getClassLoader()
    );

    // Get class names in the .war
    Set<String> classes = getClassNamesFromJarFile(jar);

    // Try to get classes from name
    for (String className : classes) {
        try {
            System.out.println("CLASS FOUND: " + className);
            Class<?> actualClass= Class.forName(className, true, child);
        } catch(Error | Exception e) {
            System.out.println("ERROR: " + e);
        }
    }
}

For every class I get the output:对于每个类,我都会得到输出:

...
CLASS FOUND: WEB-INF.classes.com.some.company.project.state.ProjectStateProvider$1
ERROR: java.lang.NoClassDefFoundError: com/some/company/project/state/ProjectStateProvider$1 (wrong name: WEB-INF/classes/com/some/company/project/state/ProjectStateProvider$1)
CLASS FOUND: WEB-INF.classes.com.some.company.configuration.rest.JsonProvider
ERROR: java.lang.NoClassDefFoundError: com/some/company/configuration/rest/JsonProvider (wrong name: WEB-INF/classes/com/some/company/configuration/rest/JsonProvider)
...

I have tried removing the WEB-INF.我试过删除WEB-INF. and the WEB-INF.classes.WEB-INF.classes. prefixes from the className, I have also tried adding .class to the className and every combination of the previous solutions, but I just get a java.lang.ClassNotFoundException .来自类名的前缀,我也尝试将.class添加到类名和以前解决方案的每个组合中,但我只得到一个java.lang.ClassNotFoundException

How do I reference classes loaded from the .war like the WEB-INF.classes.com.some.company.project.state.ProjectStateProvider$1 class for example?如何引用从.war加载的类,例如WEB-INF.classes.com.some.company.project.state.ProjectStateProvider$1类?

I updated the code now.我现在更新了代码。 It now finds the respective class file inside the .war file.它现在在 .war 文件中找到相应的类文件。

The problem that will arise now, is if you want to load a class that depends on other classes that have not yet been loaded.现在会出现的问题是,如果要加载一个依赖于其他尚未加载的类的类。

So you can either所以你可以

  • interrupt loading there and search for that file (write a custom ClassLoader that first scans all jar/war files for .class files WITHOUT loading them)在那里中断加载并搜索该文件(编写一个自定义的 ClassLoader,它首先扫描所有 jar/war 文件以查找 .class 文件而不加载它们)
  • follow the loading order given in the .war file (don't know ad hoc where to find that, tho按照 .war 文件中给出的加载顺序(不知道在哪里可以找到那个,寿

Code:代码:

package stackoverflow;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

public class JarLoading {



    public static void main(final String[] args) throws MalformedURLException, IOException {
        final String fileName = "D:\\upload\\K4D-Prozesstafel.war";
        final File file = new File(fileName);
        reloadWar(file);
    }



    static private void reloadWar(final File pJarFile) throws MalformedURLException, IOException {
        System.out.println("JarLoading.reloadJar() " + pJarFile);
        final String defaultPattern = "jar:file:" + pJarFile + "!/WEB-INF/classes/";
        //      final URL oldURL = pJarFile.toURI().toURL();
        final URL newURL = new URL(defaultPattern);
        System.out.println("DP:\t" + defaultPattern);

        try (final URLClassLoader classLoader = new URLClassLoader(new URL[] { newURL }); //
                final JarFile file = new JarFile(pJarFile);) {

            final Enumeration<JarEntry> entries = file.entries();
            while (entries.hasMoreElements()) {
                final JarEntry jarEntry = entries.nextElement();
                if (!isClassFile(jarEntry)) {
                    continue;
                }

                try {
                    final String id = getClassName(jarEntry);
                    System.out.println("Loading file " + id);
                    final Class<?> cls = classLoader.loadClass(id);
                    System.out.println("\tLoaded file: " + cls);
                } catch (final ClassNotFoundException e2) {
                    System.out.println("\tFile not a class file: " + jarEntry.getName() + "\n\t\t" + e2);
                    return;
                }
            }

            //          if (plugin.isValid()) mPlugins.add(plugin);
        }
    }



    static private boolean isClassFile(final ZipEntry entry) {
        return !entry.isDirectory() && entry.getName().endsWith(".class");
    }
    static private String getClassName(final ZipEntry entry) {
        String className = entry.getName().replace('/', '.'); // including ".class"
        className = className.substring(0, className.length() - ".class".length());
        className = className.replace("WEB-INF.classes.", "");
        return className;
    }
}

This now will probably lead to exceptions such as:现在这可能会导致异常,例如:

JarLoading.reloadJar() D:\upload\K4D-Prozesstafel.war
DP: jar:file:D:\upload\K4D-Prozesstafel.war!/WEB-INF/classes/
Loading file biz.k4d.gremienplaner.prozesstafel.cruds.BoxCRUD
Exception in thread "main" java.lang.NoClassDefFoundError: biz/cbsoft/lib/jee/glassfish/io/crud/TemplateCRUD_BaseEntity

Because the class file biz.k4d.gremienplaner.prozesstafel.cruds.BoxCRUD gets loaded now, but it depends on a file from another jar, namely biz/cbsoft/lib/jee/glassfish/io/crud/TemplateCRUD_BaseEntity .因为类文件biz.k4d.gremienplaner.prozesstafel.cruds.BoxCRUD现在已加载,但它依赖于另一个 jar 中的文件,即biz/cbsoft/lib/jee/glassfish/io/crud/TemplateCRUD_BaseEntity

OK.好的。 I've changed the getClassNamesFromJarFile method slightly:我稍微改变了getClassNamesFromJarFile方法:

public static Set<String> getClassNamesFromJarFile(
        JarFile file, String pfx, String sfx) {
    Set<String> result = new HashSet<>();
    Enumeration<JarEntry> enm = file.entries();
    while (enm.hasMoreElements()) {
        JarEntry e = enm.nextElement();
        String path = e.getName();
        if (path.startsWith(pfx) && path.endsWith(sfx)) {
            path = path.substring(pfx.length(), path.length()-sfx.length());
            path = path.replace('/', '.');
            result.add(path);
        }
    }
    return result;
}

Then:然后:

public static void main(String[] args) throws IOException {

    String warpath = "C:\\User\\Me\\Desktop\\someWarFile.war";

    // Fetch JarFile
    JarFile jar = new JarFile(warpath);

    // Load .war into the JVM
    URL url = new URL("jar:file:" + warpath + "!/WEB-INF/classes/");
    URLClassLoader child = new URLClassLoader(
            new URL[]{url},
            Object.class.getClassLoader()
    );

    // Get class names in the .war
    Set<String> classes = getClassNamesFromJarFile(jar, "WEB-INF/classes/", ".class");

    // Try to get classes from name
    for (String className : classes) {
        try {
            System.out.println("CLASS FOUND: " + className);
            Class<?> actualClass= Class.forName(className, true, child);
        } catch(Error | Exception e) {
            System.out.println("ERROR: " + e);
        }
    }
}

UPDATE:更新:

Now the classes will not load if they depend on other classes not in the classpath.现在,如果类依赖于不在类路径中的其他类,它们将不会加载。 For instance, if the war contains jar files in WEB-INF/lib, you need to add them to the URLClassLoader .例如,如果 war 包含 WEB-INF/lib 中的 jar 文件,则需要将它们添加到URLClassLoader中。 You should also make available the jar of the servlet API.您还应该提供 servlet API 的 jar。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM