Project

Profile

Help

Bug #4463

error when using an external function in .net

Added by Trond Husø 11 months ago. Updated about 2 months ago.

Status:
New
Priority:
Low
Category:
.NET API
Sprint/Milestone:
-
Start date:
2020-02-24
Due date:
% Done:

0%

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

Description

Continuing the thread from the a-mail here.

This is the error code the error produces: err:XPST0017

which is: It is a static error if the expanded QName and number of arguments in a static function call do not match the name and arity of a function signature in the static context.

(I'll add more later today)

Btw: hope this is the correct way to report.

History

#1 Updated by Trond Husø 11 months ago

The following errors occurs when compiling the xslt:

2020-02-24 19:05:00,271 [6 ] ERROR ErrorAppenderLogger [FIS2NITF] [(null)] - Error message: Cannot find a matching 2-argument function named {clitype:XsltTools.XsltFunctions?from=file///C:/Visual Studio Projects/Projects/NTBNormalizerService/trunk/NTBNormalizerService/bin/Debug/XsltTools/XsltFunctions/XsltFunctions.dll}SportCalculateTimeFromWinner(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true")

The external dll is defined like this: xmlns:ntb="clitype:XsltTools.XsltFunctions?from=file:////c:/XsltTools/XSLTFunctions.dll"

Called like this: <xsl:value-of select="ntb:SportCalculateTimeFromWinner($WinnerTime, $CurrentTime)"/>

The processor is created like this:

public Processor CreateProcessor()
        {
            // Create the processor
            var processor = new Processor();
            processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true");
            processor.SetProperty(FeatureKeys.GENERATE_BYTE_CODE, "false");

            return processor;
        }

The xslt is compiled like this: public void CompileXsltDocument(Processor processor) { // Create a compiler

        var compiler = processor.NewXsltCompiler();
        
        
        compiler.ErrorList = new List<Exception>();
        try
        {
            Logger.Debug("Creating the transformer");
            if (new FileInfo(this.FileXsl).Exists)
            {
                
                this.Transformer = compiler
                    
                    .Compile(new Uri(this.FileXsl))
                    .Load();
            }
            else
            {
                throw new NormalizerException("XSLT file not found!");
            }
        }
        catch (Exception exception)
        {
            
            Logger.Error("something happened when creating the transformer");
            Logger.Error("Please check the error log for more information");
            ErrorLogger.Error(exception);

            foreach (var errorMessage in compiler.ErrorList)
            {
                ErrorLogger.ErrorFormat("Error message: {0}", errorMessage);
            }
            throw;
        }
    }

I am using the EE version of Saxon - Version 9.4.0.6

#2 Updated by Michael Kay 11 months ago

  • Category set to .NET API
  • Assignee set to O'Neil Delpratt

#3 Updated by Michael Kay 11 months ago

Handing this one over to you, O'Neil. The thread has been running a while on the saxon-help Sourceforge list. He's using 9.4 but I don't think things have changed much since then (except we might produce better diagnostics these days).. I think it's basically a dynamic loading problem on .NET, but he's not managing to get much in the way of useful diagnostics.

#4 Updated by Trond Husø 11 months ago

Would the license cover an upgrade to latest version? I can try and reference that one. Can't hurt trying I guess.....

#5 Updated by Trond Husø 11 months ago

After installing and referencing 9.9 and looking at underlying exception I see this:

at net.sf.saxon.style.Compilation.compileSingletonPackage(Configuration config, CompilerInfo compilerInfo, Source source) at net.sf.saxon.s9api.XsltCompiler.compile(Source source)

My gut feeling tells me this could have something to do with security settings, but I am not sure. I have also tested with adding the dll to a folder above (or below) the exe-file with no success. I have also set the security settings to allow for the same user on the server.

#6 Updated by Michael Kay 11 months ago

I don't think that the compileSingletonPackage() method is relevant to the problem.

#7 Updated by Trond Husø 11 months ago

I don't think so either. My gut feeling, again, tells me this has something todo with dll and security settings in windows.

I've also tried to create a secure dll (public key) and tested that in the xslt, same result.

#8 Updated by Michael Kay 11 months ago

I think that the last time I had an issue like this, I solved it by experimenting with different options for dynamic loading of the relevant class directly from the application code, keeping Saxon out of the way until I knew dynamic loading was possible.

If it's any help, this is the (Java) logic Saxon is using to load a .NET class/type, given the URI:

public static cli.System.Type dynamicLoad(String uri, String baseURI, boolean debug) throws XPathException {
        if (uri.startsWith("clitype:")) {
            uri = uri.substring(8);
        } else {
            throw new IllegalArgumentException("Unrecognized .NET external URI: " + uri);
        }
        String typeName;
        String queryParams;
        int q = uri.indexOf('?');
        if (q == 0 || q == uri.length() - 1) {
            if (debug) {
                System.err.println("Misplaced '?' in " + uri);
            }
            throw new XPathException("Misplaced '?' in " + uri);
        }
        if (q > 0) {
            typeName = uri.substring(0, q);
            queryParams = uri.substring(q + 1);
        } else {
            typeName = uri;
            queryParams = "";
        }
        if ("".equals(queryParams)) {
            cli.System.Type type = cli.System.Type.GetType(typeName);
            if (type == null && debug) {
                try {
                    //noinspection ConstantIfStatement
                    if (false) {
                        throw new cli.System.TypeLoadException();
                    }
                    //noinspection UnusedDeclaration
                    cli.System.Type type2 = cli.System.Type.GetType(typeName, true);
                } catch (Exception | cli.System.TypeLoadException err) {
                    System.err.println("Failed to load type " + typeName + ": " + err.getMessage());
                    return null;
                }
                System.err.println("Failed to load type " + typeName);
            }
            return type;
        } else {
//            AssemblyName aname = new AssemblyName();
            String loadFrom = null;
            String href = null;
            String partialName = null;
            String asmName = null;
            String loc = null;
            String ver = null;
            String sn = null;
            StringTokenizer tok = new StringTokenizer(queryParams, ";&");
            while (tok.hasMoreTokens()) {
                String kv = tok.nextToken();
                int eq = kv.indexOf('=');
                if (eq <= 0) {
                    if (debug) {
                        System.err.println("Bad keyword=value pair in " + kv);
                    }
                    throw new XPathException("Bad keyword=value pair in " + kv);
                }
                String keyword = kv.substring(0, eq);
                String value = kv.substring(eq + 1);
                if (keyword.equals("asm")) {
                    asmName = value;
                } else if (keyword.equals("ver")) {
                    ver = value;
                } else if (keyword.equals("loc")) {
                    loc = value;
                } else if (keyword.equals("sn")) {
                    sn = value;
                } else if (keyword.equals("from")) {
                    loadFrom = value;
                } else if (keyword.equals("href")) {
                    href = value;
                } else if (keyword.equals("partialname")) {
                    partialName = value;
                } else if (debug) {
                    System.err.println("Unrecognized keyword in URI: " + keyword + " (ignored)");
                }
            }
            Assembly asm;
            try {
                //noinspection ConstantIfStatement
                if (false) {
                    throw new cli.System.IO.FileNotFoundException();
                }
                if (partialName != null) {
                    asm = Assembly.LoadWithPartialName(partialName);
                } else if (loadFrom != null) {
                    String abs = loadFrom;
                    if (baseURI != null) {
                        abs = ResolveURI.makeAbsolute(loadFrom, baseURI).toString();
                        if (debug) {
                            System.err.println("Absolute location URI: " + abs);
                        }
                    }
                    asm = Assembly.LoadFrom(abs);
                } else if (href != null) {
                    asm = Assembly.LoadFrom(href);
                } else {
                    String longName = asmName;
                    if (ver != null) {
                        longName += ", Version=" + ver;
                    }
                    if (loc != null) {
                        longName += ", Culture=" + loc;
                    }
                    if (sn != null) {
                        longName += ", PublicKeyToken=" + sn;
                    }
                    asm = Assembly.Load(longName);
//                    asm = Assembly.Load(aname);
                }
                if (debug) {
                    System.err.println("Assembly " + asm.get_FullName() + " successfully loaded");
                    System.err.println("Assembly codebase (" +
                            (asm.get_GlobalAssemblyCache() ? "GAC" : "local") +
                            "): " + asm.get_CodeBase());
                }
            } catch (cli.System.IO.FileNotFoundException err) {
                if (debug) {
                    System.err.println("Failed to load assembly " + uri + ": " + err.getMessage() +
                            " (FileNotFoundException)");
                }
                throw new XPathException("Failed to load assembly " + uri + ": " + err.getMessage());
            } catch (Throwable err) {
                if (debug) {
                    System.err.println("Failed to load assembly " + uri + ": " + err.getMessage() +
                            " (" + err.getClass().getName() + ")");
                }
                throw new XPathException("Failed to load assembly " + uri + ": " + err.getMessage());
            }
            cli.System.Type type = asm.GetType(typeName);
            if (type == null) {
                if (debug) {
                    System.err.println("Type " + typeName + " not found in assembly");
                }
                throw new XPathException("Type " + typeName + " not found in assembly");
            }
            return type;
        }
    }

One thing that puzzles me is that you're not seeing any messages about failure to load the class.

The other avenue you could pursue as a workaround is to explicitly register the class containing the extension function. If you drill down from the Processor object to the underlying (Java) ProfessionalConfiguration object, calling config.getExtensionBinder("clitype") will give you the DotNetExtensionLibrary, and this has a method declareDotNetType(String uri, cli.System.Type theClass). If the namespace URI of an extension function has been associated with an .NET System.Type in this way, then no dynamic loading is necessary.

#9 Updated by O'Neil Delpratt 5 months ago

As suggested in comment #8 the workaround for explicitly registering the extension function we have now made it more accessible with the introduction of the method Processor.BindExtensions. See bug issue: #4468

#10 Updated by Trond Husø 2 months ago

A bit more information.

Which version of Saxon is this? Where can I download it? I have not tested this version.

Error message below:

   0-11-18 12:22:59,320 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - javax.xml.transform.TransformerConfigurationException: Failed to compile stylesheet. 4 errors detected.
   2020-11-18 12:24:19,273 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - javax.xml.transform.TransformerConfigurationException: Failed to compile stylesheet. 4 errors detected.
   2020-11-18 12:24:19,273 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - Error message: Cannot find a matching 2-argument function named {clitype:XSLTTools.XsltFunctions?from=file:///D:/NTBTools/Normalizer-Tools/XSLTTools.dll}SportCalculateTimeFromWinner(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true")
   2020-11-18 12:24:19,273 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - Error message: Cannot find a matching 2-argument function named {clitype:XSLTTools.XsltFunctions?from=file:///D:/NTBTools/Normalizer-Tools/XSLTTools.dll}SportCalculateTimeFromWinner(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true")
   2020-11-18 12:24:19,273 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - Error message: Cannot find a matching 2-argument function named {clitype:XSLTTools.XsltFunctions?from=file:///D:/NTBTools/Normalizer-Tools/XSLTTools.dll}SportCalculateTimeFromWinner(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true")
   2020-11-18 12:24:19,273 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - Error message: Cannot find a matching 2-argument function named {clitype:XSLTTools.XsltFunctions?from=file:///D:/NTBTools/Normalizer-Tools/XSLTTools.dll}SportCalculateTimeFromWinner(). For diagnostics on calls to .NET methods, use the -TJ command line option or call processor.SetProperty("http://saxon.sf.net/feature/trace-external-functions", "true")
   2020-11-18 12:24:19,273 [3 ] ERROR ErrorAppenderLogger [(null)] [(null)] - javax.xml.transform.TransformerConfigurationException: Failed to compile stylesheet. 4 errors detected.

Some source code:

using System; using System.Collections; using System.Globalization;

namespace XSLTTools { public static class XsltFunctions { public static string EncodeContent(string content) { // We shall open up a file of replace-tags, then we shall replace all characters with this information var hashDictionary = new Hashtable();

       // ReadFileToArray(DicFilename);
       return content;
   }



   public static string SportCalculateTimeFromWinner(string winnerTime, string currentTime)
   {
       // It could be that we have a sprint where there are no times
       if (winnerTime == "")

#11 Updated by Michael Kay 2 months ago

The method Processor.BindExtensions is available in 10.3. See

https://saxonica.com/documentation/index.html#!dotnetdoc/Saxon.Api/Processor@BindExtensions

#12 Updated by Trond Husø 2 months ago

From the documentation: BindExtensions public void BindExtensions(string uri, Type type) Declare a mapping from a specific namespace URI to a .NET class This will get applied to Saxon-PEN or Saxon-EEN product Parameters:

uri - the namespace URI of the function name type - the .NET class that implements the functions in this namespace

Would this be correct? type = XsltFunctions uri: ////f:/XsltTools/XsltFunctions/XSLTFunctions.dll

#13 Updated by Michael Kay about 2 months ago

Sorry for not responding sooner.

In the call on BindExtensions, the URI is not the location of the DLL containing the implementation, it is the namespace URI used in the function name.

We're in the process of preparing an example.

Please register to edit this issue

Also available in: Atom PDF