在我们的项目中,我们目前迁移到 Java 8 并希望现在使用内部 XML 库而不是其他库。为此,我们从类路径中删除了例如 xalan.jar。
这会在尝试呈现我们的 XML 文件时导致 ClassNotFoundExceptions。在我们用来转换 XML 文件的 XSL 样式表中,我们还调用外部 Java 方法,如下例所示:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:java="http://xml.apache.org/xalan/java">
...
<xsl:template name="example">
<fo:block>
<xsl:value-of select="java:my.package.name.JavaClass.testMethod('test')"/>
</fo:block>
</xsl:template>
...
但是,总是在testMethod()
应该调用外部方法时,进程会因 ClassNotFoundException 而停止。我调试了com.sun.org.apache.xalan.internal.xsltc.compiler.FunctionCall
类中的代码,发现 XSLTC 试图实例my.package.name.JavaClass
化testMethod()
. 没关系。但...
尝试在以下代码中实例化/加载该类com.sun.org.apache.xalan.internal.utils.ObjectFactory
:
/**
* Find a Class using the same class loader for the ObjectFactory by default
* or boot class loader when Security Manager is in place
*/
public static Class<?> findProviderClass(String className, boolean doFallback)
throws ClassNotFoundException, ConfigurationError
{
return findProviderClass (className,
findClassLoader (), doFallback);
}
进一步调用findClassLoader()
以获取正确的类加载器来加载我的JavaClass
:
/**
* Figure out which ClassLoader to use. For JDK 1.2 and later use
* the context ClassLoader.
*/
public static ClassLoader findClassLoader()
{
if (System.getSecurityManager()!=null) {
//this will ensure bootclassloader is used
return null;
}
// Figure out which ClassLoader to use for loading the provider
// class. If there is a Context ClassLoader then use it.
ClassLoader context = SecuritySupport.getContextClassLoader();
ClassLoader system = SecuritySupport.getSystemClassLoader();
ClassLoader chain = system;
while (true) {
if (context == chain) {
...loop to get correct class loader
}
现在在我们的项目中,我们遇到了一个“问题”,即我们有一个 SecurityManager 实现,这意味着上面的这个实现只是返回null
而不是上下文类加载器。
顺便说一句:我也希望在这里获得上下文类加载器,但是如果 SecurityManager 允许加载一个类,那么稍后会进行额外的检查,为什么它在这里只返回 null 呢?
这再次意味着我的类被尝试使用不知道我的类并因此引发异常的引导类加载器加载。删除 SecurityManager 会返回能够加载我的类的上下文类加载器,并且可以成功转换 XML。
所以——如果你还在这里睡着了——有没有办法在 XSL 样式表中仍然使用我的外部 Java 方法?在这种情况下有没有办法加载我的课程?我想过以下可能的解决方案,但它们都不能真正让我满意:
- 修补 ObjectFactory,删除或扩展对 SecurityManager 的检查,并将其放在我的应用程序的引导类路径中,它在原始应用程序之前加载(肮脏的黑客)
- 删除 SecurityManager (现在或多或少是一个虚拟实现,但这不是永久解决方案)
- 将 Java 方法编写为 XSL 中的扩展函数(不确定这是否适用于我们拥有的所有方法,并且会非常耗时且容易出错;我也不确定这是否真的解决了问题)
通过以下方式使用反射在 XSL 中创建我的 JavaClass 的实例:
但是,最后一种方法也不起作用,因为该类也使用 AppClassLoader 加载,并且在解析过程中也可能不可用。但也许有类似的解决方案?
提前致谢。
最好的问候,延斯