Project

Profile

Help

Bug #4759

Saxon.NET: InvalidCastException in sun.net.ftp.impl.FtpClient, possible IKVM issue?

Added by Emanuel Wlaschitz 24 days ago. Updated 21 days ago.

Status:
In Progress
Priority:
Normal
Category:
.NET API
Sprint/Milestone:
-
Start date:
2020-09-28
Due date:
% Done:

0%

Estimated time:
Legacy ID:
Applies to branch:
10, 9.9
Fix Committed on Branch:
Fixed in Maintenance Release:

Description

We got Saxon-HE 9.9.1.2 integrated in one of our tools, and it seems to fail on one particular machine every so often with the following error:

System.TypeInitializationException: The type initializer for 'sun.net.ftp.impl.FtpClient' threw an exception. ---> System.InvalidCastException: Unable to cast object of type 'java.util.PropertyResourceBundle' to type 'sun.util.resources.OpenListResourceBundle'.
   at sun.util.resources.LocaleData.getCurrencyNames(Locale locale)
   at sun.util.locale.provider.LocaleResources.getCurrencyName(String key)
   at sun.util.locale.provider.CurrencyNameProviderImpl.getString(String , Locale )
   at sun.util.locale.provider.CurrencyNameProviderImpl.getSymbol(String currencyCode, Locale locale)
   at java.util.Currency.CurrencyNameGetter.getObject(CurrencyNameProvider , Locale , String , Object[] )
   at java.util.Currency.CurrencyNameGetter.getObject(LocaleServiceProvider , Locale , String , Object[] )
   at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObjectImpl(LocalizedObjectGetter , Locale , Boolean , String , Object[] )
   at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObject(LocalizedObjectGetter getter, Locale locale, String key, Object[] params)
   at java.util.Currency.getSymbol(Locale locale)
   at java.text.DecimalFormatSymbols.initialize(Locale )
   at java.text.DecimalFormatSymbols..ctor(Locale locale)
   at sun.util.locale.provider.DecimalFormatSymbolsProviderImpl.getInstance(Locale locale)
   at java.text.DecimalFormatSymbols.getInstance(Locale locale)
   at sun.util.locale.provider.NumberFormatProviderImpl.getInstance(Locale , Int32 )
   at sun.util.locale.provider.NumberFormatProviderImpl.getIntegerInstance(Locale locale)
   at java.text.NumberFormat.getInstance(LocaleProviderAdapter , Locale , Int32 )
   at java.text.NumberFormat.getInstance(Locale , Int32 )
   at java.text.NumberFormat.getIntegerInstance(Locale inLocale)
   at java.text.SimpleDateFormat.initialize(Locale )
   at java.text.SimpleDateFormat..ctor(String pattern, Locale locale)
   at java.text.SimpleDateFormat..ctor(String pattern)
   at sun.net.ftp.impl.FtpClient..cctor()
   --- End of inner exception stack trace ---
   at sun.net.ftp.impl.FtpClient.create()
   at sun.net.ftp.impl.DefaultFtpClientProvider.createFtpClient()
   at sun.net.ftp.FtpClient.create()
   at sun.net.www.protocol.ftp.FtpURLConnection.connect()
   at net.sf.saxon.lib.StandardUnparsedTextResolver.resolve(URI absoluteURI, String encoding, Configuration config)
   at net.sf.saxon.functions.UnparsedTextFunction.readFile(URI absoluteURI, String encoding, XPathContext context)
   at net.sf.saxon.functions.UnparsedText.evalUnparsedText(StringValue hrefVal, String base, String encoding, XPathContext context)
   at net.sf.saxon.functions.UnparsedTextAvailable.evalUnparsedTextAvailable(StringValue hrefVal, String encoding, XPathContext context)
   at net.sf.saxon.functions.UnparsedTextAvailable.call(XPathContext context, Sequence[] arguments)
   at net.sf.saxon.functions.UnparsedTextAvailable.call(XPathContext xpc, Sequence[] sarr)
   at net.sf.saxon.expr.FunctionCall.iterate(XPathContext context)
   at net.sf.saxon.expr.Expression.effectiveBooleanValue(XPathContext context)
   at net.sf.saxon.expr.AndExpression.effectiveBooleanValue(XPathContext c)
   at net.sf.saxon.expr.AndExpression.effectiveBooleanValue(XPathContext c)
   at net.sf.saxon.expr.AndExpression.effectiveBooleanValue(XPathContext c)
   at net.sf.saxon.expr.AndExpression.effectiveBooleanValue(XPathContext c)
   at net.sf.saxon.expr.instruct.Choose.choose(XPathContext )
   at net.sf.saxon.expr.instruct.Choose.processLeavingTail(XPathContext context)
   at net.sf.saxon.expr.instruct.Block.processLeavingTail(XPathContext context)
   at net.sf.saxon.expr.instruct.Instruction.process(XPathContext context)
   at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(XPathContext context, NodeInfo copiedNode)
   at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(XPathContext context)
   at net.sf.saxon.expr.instruct.Block.processLeavingTail(XPathContext context)
   at net.sf.saxon.expr.instruct.Instruction.process(XPathContext context)
   at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(XPathContext context, NodeInfo copiedNode)
   at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(XPathContext context)
   at net.sf.saxon.expr.instruct.Block.processLeavingTail(XPathContext context)
   at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(XPathContext context)
   at net.sf.saxon.trans.Mode.applyTemplates(ParameterSet parameters, ParameterSet tunnelParameters, XPathContextMajor context, Location locationId)
   at net.sf.saxon.expr.instruct.ApplyTemplates.ApplyTemplatesPackage.processLeavingTail()
   at net.sf.saxon.trans.Mode.applyTemplates(ParameterSet parameters, ParameterSet tunnelParameters, XPathContextMajor context, Location locationId)
   at net.sf.saxon.expr.instruct.ApplyTemplates.ApplyTemplatesPackage.processLeavingTail()
   at net.sf.saxon.trans.Mode.applyTemplates(ParameterSet parameters, ParameterSet tunnelParameters, XPathContextMajor context, Location locationId)
   at net.sf.saxon.trans.XsltController.applyTemplates(Sequence source, Receiver out)
   at net.sf.saxon.s9api.AbstractXsltTransformer.applyTemplatesToSource(Source , Receiver )
   at net.sf.saxon.s9api.XsltTransformer.transform()
   at Saxon.Api.XsltTransformer.Run(XmlDestination destination)
   at [our own code calling XsltTransformer.Run]

Other machines appear to be fine, otherwise we'd have found this sooner.

Searching for this gives little to no applicable Saxon-related results, and searching outside Saxon brings at least this report on GitHub which indicates there might be IKVM.OpenJDK.Cldrdata.dll missing in the application directory. We randomly tried that, but that machine is on an English OS so it shouldn't apply.

By testing, it seems we at least need IKVM.OpenJDK.Localedata.dll in there (either as well, or just instead of Cldrdata) - but we don't have any more info than that (since we're still trying to narrow it down a lot).

Neither of those files is part of the Saxon-HE distribution at this point, which is a bit surprising (not just because it has worked before; and we've been on Saxon-HE 9.9.1.2 since March while we've only seen those issues pop up recently, since last month or so).

The XSLT seems innocent as well, the only call it makes that seems remotely relevant in the context of the stacktrace above is a check for bundling files (which then emits different <style>/<script> tags):

<xsl:choose>
  <xsl:when test="not($disableBundles) and unparsed-text-available('bundling/vendor.min.css') and unparsed-text-available('bundling/vendor.min.js')">
    <!-- use bundled files -->
  </xsl:when>
  <xsl:otherwise>
    <!-- use regular files -->
  </xsl:otherwise>
</xsl:choose>

The calling code seems pretty straight-forward as well; the only noteworthy things there are the fact that we load an XML file into System.Xml.Linq.XDocument; often from a UNC path (which may or may not be on the same machine), and then use XDocument.CreateReader() to create the input for XsltTransformer.InitialContextNode (using DocumentBuilder.Build(xmlReader)) while the stylesheet is always local (and loaded using XsltCompiler.Compile(File.OpenRead(stylesheetPath))).

Other than that, we're having a lot of issues trying to narrow it down and creating a minimal repro case; mainly because it only happens on that one machine and not consistently enough to call it "reproducible".

Any thoughts and hints would be appreciated on how to proceed further; since copying random DLL files into the application folder seems a bit weird way of getting things to work (especially since we're apparently the first ones that see this).

History

#1 Updated by Michael Kay 24 days ago

I don't recognize this as something we've seen before. What .NET version is it?

#2 Updated by Emanuel Wlaschitz 24 days ago

This is on .NET Framework 4.7, since we didn't get around to upgrade to .NET Framework 4.8 yet.

#3 Updated by Martin Honnen 24 days ago

Interesting, I am able to reproduce an exception (although java.lang.ExceptionInInitializerError) with Saxon .NET 9.9 or 10 run from the command line doing unparsed-text-available on an FTP URL depending on the CultureInfo set on Windows 10 German edition.

Stylesheet does

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
    <xsl:param name="uri">ftp://ftp.halifax.rwth-aachen.de/apache/xalan/README.txt</xsl:param>
    <xsl:template name="xsl:initial-template">
        <xsl:value-of select="unparsed-text-available(iri-to-uri($uri))"/>
    </xsl:template>
</xsl:stylesheet>

run with -it gives

Unbehandelte Ausnahme: java.lang.ExceptionInInitializerError
   bei net.sf.saxon.Transform.doTransform(String[] args, String command)
   bei Saxon.Cmd.DotNetTransform.Main(String[] args)

in a Windows 10 Powershell Windows where I have done Set-Culture -CultureInfo de-DE.

If I switch to Set-Culture -CultureInfo en-US, Saxon does work and gives <?xml version="1.0" encoding="UTF-8"?>true.

Saxon Java works indepently of the CultureInfo set.

#4 Updated by Martin Honnen 24 days ago

If I put IKVM.OpenJDK.Localedata.dll from IKVM 8.1.5717 into the bin directory of Saxon 10 .NET the above stylesheet works independent of the CultureInfo.

#5 Updated by Martin Honnen 24 days ago

In a C# project referencing Saxon 10.2 HE from NuGet I can avoid the exception by setting CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US"); before creating Saxon's Processor with e.g. Processor processor = new Processor(true);.

If I use CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE"); Processor processor = new Processor(true); I very much get a stack trace like the original one:

Unbehandelte Ausnahme: System.TypeInitializationException: Der Typeninitialisierer f├╝r "sun.net.ftp.impl.FtpClient" hat eine Ausnahme verursacht. ---> System.InvalidCastException: Das Objekt des Typs "java.util.PropertyResourceBundle" kann nicht in Typ "sun.util.resources.OpenListResourceBundle" umgewandelt werden.
   bei sun.util.resources.LocaleData.getCurrencyNames(Locale locale)
   bei sun.util.locale.provider.LocaleResources.getCurrencyName(String key)
   bei sun.util.locale.provider.CurrencyNameProviderImpl.getString(String , Locale )
   bei sun.util.locale.provider.CurrencyNameProviderImpl.getSymbol(String currencyCode, Locale locale)
   bei java.util.Currency.CurrencyNameGetter.getObject(CurrencyNameProvider , Locale , String , Object[] )
   bei java.util.Currency.CurrencyNameGetter.getObject(LocaleServiceProvider , Locale , String , Object[] )
   bei sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObjectImpl(LocalizedObjectGetter , Locale , Boolean , String , Object[] )
   bei sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObject(LocalizedObjectGetter getter, Locale locale, String key, Object[] params)
   bei java.util.Currency.getSymbol(Locale locale)
   bei java.text.DecimalFormatSymbols.initialize(Locale )
   bei java.text.DecimalFormatSymbols..ctor(Locale locale)
   bei sun.util.locale.provider.DecimalFormatSymbolsProviderImpl.getInstance(Locale locale)
   bei java.text.DecimalFormatSymbols.getInstance(Locale locale)
   bei sun.util.locale.provider.NumberFormatProviderImpl.getInstance(Locale , Int32 )
   bei sun.util.locale.provider.NumberFormatProviderImpl.getIntegerInstance(Locale locale)
   bei java.text.NumberFormat.getInstance(LocaleProviderAdapter , Locale , Int32 )
   bei java.text.NumberFormat.getInstance(Locale , Int32 )
   bei java.text.NumberFormat.getIntegerInstance(Locale inLocale)
   bei java.text.SimpleDateFormat.initialize(Locale )
   bei java.text.SimpleDateFormat..ctor(String pattern, Locale locale)
   bei java.text.SimpleDateFormat..ctor(String pattern)
   bei sun.net.ftp.impl.FtpClient..cctor()
   --- Ende der internen Ausnahmestapel├╝berwachung ---
   bei sun.net.ftp.impl.FtpClient.create()
   bei sun.net.ftp.impl.DefaultFtpClientProvider.createFtpClient()
   bei sun.net.ftp.FtpClient.create()
   bei sun.net.www.protocol.ftp.FtpURLConnection.connect()
   bei net.sf.saxon.lib.StandardUnparsedTextResolver.resolve(URI absoluteURI, String encoding, Configuration config)
   bei net.sf.saxon.functions.UnparsedTextFunction.readFile(URI absoluteURI, String encoding, CharSequenceConsumer output, XPathContext context)
   bei net.sf.saxon.functions.UnparsedText.evalUnparsedText(StringValue hrefVal, String base, String encoding, XPathContext context)
   bei net.sf.saxon.functions.UnparsedTextAvailable.evalUnparsedTextAvailable(StringValue hrefVal, String encoding, XPathContext context)
   bei net.sf.saxon.functions.UnparsedTextAvailable.call(XPathContext context, Sequence[] arguments)
   bei net.sf.saxon.functions.UnparsedTextAvailable.<bridge>call(XPathContext xpc, Sequence[] sarr)
   bei net.sf.saxon.expr.FunctionCall.iterate(XPathContext context)
   bei net.sf.saxon.expr.Expression.evaluateItem(XPathContext context)
   bei net.sf.saxon.expr.AtomicSequenceConverter.evaluateItem(XPathContext context)
   bei net.sf.saxon.expr.AtomicSequenceConverter.<bridge>evaluateItem(XPathContext xpc)
   bei net.sf.saxon.expr.parser.Evaluator.6.evaluate(Expression , XPathContext )
   bei net.sf.saxon.expr.SystemFunctionCall.evaluateArguments(XPathContext context)
   bei net.sf.saxon.expr.SystemFunctionCall.process(Outputter output, XPathContext context)
   bei net.sf.saxon.expr.instruct.ValueOf.processLeavingTail(Outputter output, XPathContext context)
   bei net.sf.saxon.expr.instruct.Instruction.process(Outputter output, XPathContext context)
   bei net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(Outputter out, XPathContext context, NodeInfo copiedNode)
   bei net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(Outputter output, XPathContext context)
   bei net.sf.saxon.expr.instruct.Instruction.process(Outputter output, XPathContext context)
   bei net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(Outputter out, XPathContext context, NodeInfo copiedNode)
   bei net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(Outputter output, XPathContext context)
   bei net.sf.saxon.expr.instruct.NamedTemplate.expand(Outputter output, XPathContext context)
   bei net.sf.saxon.trans.XsltController.callTemplate(StructuredQName initialTemplateName, Receiver out)
   bei net.sf.saxon.s9api.Xslt30Transformer.callTemplate(QName templateName, Destination destination)
   bei Saxon.Api.Xslt30Transformer.CallTemplate(QName templateName, XmlDestination destination)

#6 Updated by Martin Honnen 24 days ago

If I add IKVM 8.1.51.71 from NuGet (which has IKVM.OpenJDK.Localedata.dll) to the project the code runs fine even with doing CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");.

#7 Updated by Emanuel Wlaschitz 24 days ago

Interresting. We don't set CultureInfo.CurrentCulture (or CultureInfo.CurrentUICulture for that matter). Also, I cannot cause this to fail either by setting it to de, de-DE or de-AT (or en, en-US). Creating the Processor with licensedEdition: true doesn't make a difference either (although seems counter-intuitive, especially since it is HE and no license file is present).

Not locally on my machine (which is a German Windows 10 1709 with an English language pack), not on that affected machine (which is an English Windows Server 2016).

But that was my main reason to open this in advance (before I got a repro-case), to see if someone else can add to it.

Also, since I just noticed this: We do use unparsed-text-available(), but we do not pass an actual (explicit) FTP URI to it. To be fair, we use a relative path and assume it is relative to the XSLT file, but that one should always available locally and compiled from a local (windows) path (and works so far; so YMMV).

I'm still poking around on a potential repro-case, but no success so far; so I seem to be missing some detail from the big application.

#8 Updated by Martin Honnen 24 days ago

Based on your stack trace showing

at sun.net.www.protocol.ftp.FtpURLConnection.connect()
   at net.sf.saxon.lib.StandardUnparsedTextResolver.resolve(URI absoluteURI, String encoding, Configuration config)

given that https://saxonica.plan.io/projects/saxon/repository/he/revisions/master/entry/latest9.9/hej/net/sf/saxon/lib/StandardUnparsedTextResolver.java#L122 does nothing but URLConnection connection = absoluteURL.openConnection();, it appears that in the error case an FTP URL ends up in StandardUnparsedTextResolver, that is why I wrote the test case with an FTP URL.

I still think that the culprit is that an FTP URL is being used and then that FTP client used under the hood does some Locale/CultureInfo dependent new java.text.SimpleDateFormat(pattern) passing a default/platform locale in new java.text.SimpleDateFormat(pattern, locale) and then with reduced list of dlls Saxon comes with there is no support for a locale outside the English one.

Any chance there is an FTP URL used on your site if the error occurs?

#9 Updated by Emanuel Wlaschitz 23 days ago

The only thing I could not rule out at this point is potential proxy auto-configuration that might be happening on that particular machine.

But we don't interact with FTP in any way, not as part of the application and not on that machine either; so I can't really tell where it is coming from.

I'm currently looking into recompiling the application to toggle debugging on (and hopefully hit that debug output with the absoluteUrl), but at the moment the thing doesn't reproduce at all. As if it knows it is being debugged and doesn't trigger...

#10 Updated by Emanuel Wlaschitz 23 days ago

I couldn't manage to turn on debugging (or logging) just yet, but document-uri() was always empty in the XSLT so I looked at static-base-uri() - which is indeed a UNC path that uses the machine name. It looks like this: file://machine-name/share/xslt/test.xsl

So I tinkered with it a little...this works:

<xsl:value-of select="unparsed-text-available('file://localhost/share/xslt/test.xsl')"/>

This causes the same exception (despite not using a ftp:// uri):

<xsl:value-of select="unparsed-text-available('file://machine-name/share/xslt/test.xsl')"/>

This might be the same URI problem that we've had before (due to differences between how Java and .NET handle URIs that contain machine names); and was reported various times before (#4280 to name just a one). It works as soon as I put three or more slashes in there (as in: file:///machine-name, file:////machine-name etc.)

Yet it only happens when the CultureInfo.CurrentCulture is non-English.

So, how do we proceed from here? I practically know how to fix this in our XSLT, but it is still a weird coincidence/error message depending on whether an IKVM DLL is present or not.

#11 Updated by O'Neil Delpratt 22 days ago

  • Category set to .NET API
  • Assignee set to O'Neil Delpratt
  • Applies to branch 10 added

Hi, I have followed through the discussion of this problem. It indeed interesting that the presence of the library IKVM.OpenJDK.Localedata.dll solves the problem.

I will try to reproduce it at my end, hopefully the XSLT in comment #3 will be enough.

#12 Updated by Emanuel Wlaschitz 22 days ago

Hey O'Neil, you should be able to use this minimalistic sample:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="#all">
    <xsl:template match="/">
        <xsl:value-of select="unparsed-text-available('file://machine-name/share/does-not-matter.txt')"/>
    </xsl:template>
</xsl:stylesheet>

Just make sure to set CultureInfo.CurrentCulture to something that isn't English before running this.

machine-name, share and does-not-matter.txt don't have to exist at all; but using a workable machine/share combination avoids a timeout (where it attempts to find the machine first, apparently).

That XSLT passes if you change file:// to file:/// (again, regardless of whether the machine, share or file actually exist).

#13 Updated by O'Neil Delpratt 22 days ago

  • Status changed from New to In Progress

Thanks for the repo and useful comments. I will investigate it and report back shortly.

#14 Updated by O'Neil Delpratt 21 days ago

  • Priority changed from Low to Normal

Hi,

Thanks for the repo which I used to reproduce the bug. I ran it in powershell with Set-Culture -CultureInfo de-DE as suggested in comment #3. See exception I got:

Unhandled Exception: java.lang.ExceptionInInitializerError
   at net.sf.saxon.Transform.doTransform(String[] args, String command)
   at Saxon.Cmd.DotNetTransform.Main(String[] args)

#15 Updated by O'Neil Delpratt 21 days ago

Again, I also confirm that the presence of the library IKVM.OpenJDK.Localedata.dll solves the exception from being thrown. This is an usually bug which does boil down to how we or IKVM is handling the FTP URL with some Locale/CultureInfo dependency. We don't see many locale bugs, but this is the second one in a month, it is reasonable to say maybe the solution is we have to supply this extra dll library in the product to handle such problems. We will discuss this internally.

Please register to edit this issue

Also available in: Atom PDF