使用JDK内部Xalan和SecurityManager从XSL样式表内部加载外部Java方法



在我们的项目中,我们目前转向了Java 8,现在希望使用内部XML库,而不是其他库。为此,我们从类路径中删除了例如xalan.jar。

这导致在尝试呈现XML文件时出现ClassNotFoundException。在我们用来转换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);
    }

它进一步调用CCD_ 6以获得正确的类加载器来加载我的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,而不是上下文类加载器。

BTW:我也希望在这里获得上下文类加载器,但如果SecurityManager允许加载一个类,那么稍后会进行额外的检查,为什么它只在这里返回null?

这再次意味着我的类试图用引导类加载器加载,而引导类加载器不知道我的类,因此抛出异常。删除SecurityManager会返回上下文类加载器,它能够加载我的类,并且XML可以成功转换。

那么,如果你还在这里睡觉,有没有办法在XSL样式表中仍然使用我的外部Java方法?在这种情况下,有没有办法加载我的课程?我考虑了以下可能的解决方案,但它们并不能真正让我满意:

  • 修补ObjectFactory,删除或扩展对SecurityManager的检查,并将其放在我的应用程序的引导类路径上,使其在原始类路径之前加载(脏破解)
  • 删除SecurityManager(目前或多或少是一个伪实现,但这不是一个永久的解决方案)
  • 将Java方法作为XSL中的扩展函数编写(不确定这是否适用于我们现有的所有方法,而且非常耗时且容易出错;我也不确定这能否真正解决问题)
  • 使用反射以以下方式在XSL中创建我的JavaClass实例:

然而,最后一种方法也不起作用,因为类也是用AppClassLoader加载的,而且在解析过程中可能也不可用。但也许还有类似的解决方案?

提前谢谢。

谨致问候,Jens

做一些类似的工作:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:fo="http://www.w3.org/1999/XSL/Format"
   xmlns:myHelper="http://xml.apache.org/xalan/java/my.package.name.JavaClass">
...
<xsl:template name="example">
  <fo:block>
    <xsl:value-of select="myHelper:testMethod('test')"/>
  </fo:block>
</xsl:template>

在样式表声明中如何指定命名空间(在本例中为myHelper)取决于您用于处理XSL的内容,上面的示例适用于Xalan。

最新更新