[英]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所以你可以
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.